@shipfox/client-workflows 20.0.0 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  4. package/dist/components/job-detail/job-detail-view.js +231 -42
  5. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  6. package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
  7. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
  8. package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
  9. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
  10. package/dist/components/step-list/step-list-model.d.ts +1 -0
  11. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  12. package/dist/components/step-list/step-list-model.js +1 -1
  13. package/dist/components/step-list/step-list-model.js.map +1 -1
  14. package/dist/components/step-list/step-list.d.ts +2 -1
  15. package/dist/components/step-list/step-list.d.ts.map +1 -1
  16. package/dist/components/step-list/step-list.js +24 -7
  17. package/dist/components/step-list/step-list.js.map +1 -1
  18. package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
  19. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  20. package/dist/components/workflow-run-list/job-status-strip.js +22 -11
  21. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  22. package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
  23. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
  24. package/dist/components/workflow-run-list/workflow-run-list-states.js +2 -2
  25. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  26. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  27. package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
  28. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  29. package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
  30. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  31. package/dist/components/workflow-run-list/workflow-run-row.js +13 -10
  32. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  33. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  34. package/dist/components/workflow-run-summary/workflow-run-summary.js +1 -1
  35. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  36. package/dist/components/workflow-run-view/run-workspace-nav.js +1 -1
  37. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  38. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  39. package/dist/components/workflow-run-view/workflow-run-states.js +1 -1
  40. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  41. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  42. package/dist/components/workflow-run-view/workflow-run-view.js +43 -36
  43. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  44. package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
  45. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
  46. package/dist/pages/project-workflows-page.d.ts.map +1 -1
  47. package/dist/pages/project-workflows-page.js +3 -2
  48. package/dist/pages/project-workflows-page.js.map +1 -1
  49. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
  50. package/dist/pages/workflow-job-detail-page.js +3 -2
  51. package/dist/pages/workflow-job-detail-page.js.map +1 -1
  52. package/dist/pages/workflow-run-detail-page.js +2 -1
  53. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  54. package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
  55. package/dist/pages/workflow-run-list-page.js +9 -11
  56. package/dist/pages/workflow-run-list-page.js.map +1 -1
  57. package/dist/routes/job-detail.d.ts +1 -1
  58. package/dist/routes/job-detail.js +1 -1
  59. package/dist/routes/job-detail.js.map +1 -1
  60. package/dist/routes/run-detail.d.ts +1 -1
  61. package/dist/routes/run-detail.js +1 -1
  62. package/dist/routes/run-detail.js.map +1 -1
  63. package/dist/routes/runs.d.ts +1 -1
  64. package/dist/routes/runs.js +1 -1
  65. package/dist/routes/runs.js.map +1 -1
  66. package/dist/routes/workflows.d.ts +3 -0
  67. package/dist/routes/workflows.js +3 -0
  68. package/dist/routes/workflows.js.map +1 -1
  69. package/dist/tsconfig.test.tsbuildinfo +1 -1
  70. package/package.json +7 -7
  71. package/src/components/job-detail/job-detail-view.tsx +263 -47
  72. package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
  73. package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
  74. package/src/components/job-graph/job-graph.stories.tsx +1 -1
  75. package/src/components/job-graph/job-node.stories.tsx +1 -1
  76. package/src/components/step-list/step-list-model.ts +1 -1
  77. package/src/components/step-list/step-list.test.tsx +29 -1
  78. package/src/components/step-list/step-list.tsx +28 -9
  79. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  80. package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
  81. package/src/components/workflow-run-list/workflow-run-list-states.tsx +2 -2
  82. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +53 -4
  83. package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
  84. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
  85. package/src/components/workflow-run-list/workflow-run-row.tsx +17 -13
  86. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +2 -2
  87. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +2 -1
  88. package/src/components/workflow-run-summary/workflow-run-summary.tsx +1 -4
  89. package/src/components/workflow-run-view/run-workspace-nav.tsx +1 -1
  90. package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
  91. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +140 -0
  92. package/src/components/workflow-run-view/workflow-run-view.test.tsx +38 -7
  93. package/src/components/workflow-run-view/workflow-run-view.tsx +45 -37
  94. package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
  95. package/src/pages/project-workflows-page.tsx +3 -2
  96. package/src/pages/workflow-job-detail-page.test.tsx +32 -0
  97. package/src/pages/workflow-job-detail-page.tsx +5 -1
  98. package/src/pages/workflow-run-detail-page.tsx +1 -1
  99. package/src/pages/workflow-run-list-page.tsx +9 -11
  100. package/src/pages/workflow-run-pages.test.tsx +26 -0
  101. package/src/routes/job-detail.tsx +1 -1
  102. package/src/routes/run-detail.tsx +1 -1
  103. package/src/routes/runs.tsx +1 -1
  104. package/src/routes/workflows.tsx +1 -0
  105. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 86 files with swc (666.85ms)
2
+ Successfully compiled: 86 files with swc (171.96ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @shipfox/client-workflows
2
2
 
3
+ ## 21.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 6425336: Aligns the run workspace rail with its content frame at wide viewport sizes while keeping dedicated job content full width.
8
+ - 36f284f: Recomposes workflow runs into a full-width data panel and shows the complete job preview.
9
+ - 261701e: Removes per-page canvas and width overrides so workflow pages render full-width surfaces instead of a locally painted, capped 1120px column.
10
+ - 7db4171: Composes job detail logs into the Panel surface with searchable output and display controls.
11
+ - 6703982: Retire the `background-neutral-background` CSS token and migrate workflow page canvases to `background-subtle-base`.
12
+ - f1d127e: Use dark contrast surfaces with readable semantic foregrounds across code and log views, preserve status on log-row edge accents, and add shared highlight and tooltip tokens for code and keyboard affordances.
13
+ - Updated dependencies [0f4abe4]
14
+ - Updated dependencies [71d0c44]
15
+ - Updated dependencies [9c21429]
16
+ - Updated dependencies [30beb8f]
17
+ - Updated dependencies [7db4171]
18
+ - Updated dependencies [16733a7]
19
+ - Updated dependencies [fd15e1f]
20
+ - Updated dependencies [163c40a]
21
+ - Updated dependencies [c4376a1]
22
+ - Updated dependencies [6703982]
23
+ - Updated dependencies [f1d127e]
24
+ - Updated dependencies [0e860d7]
25
+ - @shipfox/react-ui@2.0.0
26
+ - @shipfox/client-shell@21.0.0
27
+ - @shipfox/client-logs@21.0.0
28
+ - @shipfox/client-triggers@21.0.0
29
+ - @shipfox/client-projects@21.0.0
30
+ - @shipfox/client-ui@21.0.0
31
+
3
32
  ## 20.0.0
4
33
 
5
34
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"job-detail-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-detail-view.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAwB,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,mBAAmB,CAAC;AAyB3B,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAC3B,iBAAiB,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,WAAW,EACX,aAAa,EACb,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,iBAAiB,GAClB,EAAE,kBAAkB,+BAwQpB"}
1
+ {"version":3,"file":"job-detail-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-detail-view.tsx"],"names":[],"mappings":"AAyBA,OAAO,EAAwB,KAAK,GAAG,EAAoB,MAAM,uBAAuB,CAAC;AAGzF,OAAO,KAAK,EAAC,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,mBAAmB,CAAC;AA8B3B,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAC;IACrD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAC3B,iBAAiB,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,WAAW,EACX,aAAa,EACb,KAAK,EACL,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,iBAAiB,GAClB,EAAE,kBAAkB,+BAqUpB"}
@@ -3,19 +3,25 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  // biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.
4
4
  import { ApiError } from '@shipfox/client-api';
5
5
  import { QueryLoadError } from '@shipfox/client-ui';
6
+ import { Badge } from '@shipfox/react-ui/badge';
7
+ import { IconButton } from '@shipfox/react-ui/button';
8
+ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
6
9
  import { EmptyState } from '@shipfox/react-ui/empty-state';
7
10
  import { Icon } from '@shipfox/react-ui/icon';
11
+ import { Panel, PanelActions, PanelBody, PanelHeader } from '@shipfox/react-ui/panel';
12
+ import { SearchInline } from '@shipfox/react-ui/search';
8
13
  import { Skeleton } from '@shipfox/react-ui/skeleton';
9
14
  import { TimeTickerProvider } from '@shipfox/react-ui/time-ticker';
10
15
  import { Text } from '@shipfox/react-ui/typography';
11
16
  import { Link } from '@tanstack/react-router';
12
- import { useEffect, useRef, useState } from 'react';
17
+ import { useCallback, useEffect, useRef, useState } from 'react';
13
18
  import { summarizeJobAnnotations } from '#core/run-annotation.js';
14
19
  import { isWorkflowRunTerminal } from '#core/workflow-run.js';
15
20
  import { useWorkflowRunAnnotationSummaryQuery } from '#hooks/api/annotations.js';
16
21
  import { useRunAnnotationsQuery } from '#hooks/api/run-annotations.js';
17
22
  import { workflowJobSearchParams, workflowRunSearchParams } from '#routes/inputs.js';
18
23
  import { StepList } from '../step-list/index.js';
24
+ import { buildStepListModel, getStepStatusVisual } from '../step-list/step-list-model.js';
19
25
  import { WorkflowRunNotFound, WorkflowRunStaleError } from '../workflow-run-view/workflow-run-states.js';
20
26
  import { JobContextPanel } from './job-context-panel.js';
21
27
  import { JobDetailHeader } from './job-detail-header.js';
@@ -27,6 +33,12 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
27
33
  const rootRef = useRef(null);
28
34
  const pageScrollRef = useRef(null);
29
35
  const landingSelectionRef = useRef(undefined);
36
+ const [logSearch, setLogSearch] = useState('');
37
+ const [wrapLogs, setWrapLogs] = useState(false);
38
+ const [showLineNumbers, setShowLineNumbers] = useState(true);
39
+ const [expandedLogAttemptIds, setExpandedLogAttemptIds] = useState([]);
40
+ const [logRefreshTokens, setLogRefreshTokens] = useState({});
41
+ const [logFetchingByAttemptId, setLogFetchingByAttemptId] = useState({});
30
42
  const hasLoadedData = query.data !== undefined;
31
43
  // Reuse the run workspace's bounded annotation read for the job header chip. The separate
32
44
  // summary query below stays counts-only and is scoped to the inspector's selected execution.
@@ -64,6 +76,15 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
64
76
  hasLoadedData,
65
77
  jobId
66
78
  ]);
79
+ const handleLogFetchingChange = useCallback((attemptId, isFetching)=>{
80
+ setLogFetchingByAttemptId((current)=>{
81
+ if (current[attemptId] === isFetching) return current;
82
+ return {
83
+ ...current,
84
+ [attemptId]: isFetching
85
+ };
86
+ });
87
+ }, []);
67
88
  if (query.isPending) return /*#__PURE__*/ _jsx(JobDetailSkeleton, {});
68
89
  if (query.isError && query.data === undefined) {
69
90
  if (query.error instanceof ApiError && query.error.status === 404) {
@@ -92,6 +113,10 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
92
113
  });
93
114
  const selectedJobExecution = resolvedSelection.jobExecution;
94
115
  const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);
116
+ const stepListModel = buildStepListModel({
117
+ job,
118
+ jobExecution: selectedJobExecution
119
+ });
95
120
  const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);
96
121
  if (selectedJobExecution) {
97
122
  const frozenLanding = landingSelectionRef.current;
@@ -114,9 +139,14 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
114
139
  }
115
140
  const landingSelection = landingSelectionRef.current?.selection;
116
141
  const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;
117
- const runningSelection = runningStepSelection(selectedJobExecution);
142
+ const runningSelection = runningStepSelection(selectedJobExecution, stepListModel);
118
143
  const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;
119
144
  const selectedAttemptForNotice = hasExplicitStep ? resolvedSelection.selectedAttemptId : landingSelection?.attemptId;
145
+ const expandedLogSelection = findExpandedLogSelection(selectedJobExecution, expandedLogAttemptIds, stepListModel);
146
+ const selectedLogStep = expandedLogSelection?.step;
147
+ const selectedLogAttempt = expandedLogSelection?.attempt;
148
+ const selectedLogStatus = selectedLogAttempt?.status ?? selectedLogStep?.status;
149
+ const logIsFetching = Boolean(selectedLogAttempt && logFetchingByAttemptId[selectedLogAttempt.id]);
120
150
  const showRetargetNotice = runningSelection !== undefined && (selectedStepId !== runningSelection.stepId || selectedAttemptForNotice !== runningSelection.attemptId);
121
151
  const succeededSummary = !hasExplicitStep && landingSelection === undefined && selectedJobExecution ? jobSucceededSummary(job, selectedJobExecution) : undefined;
122
152
  function selectExecution(jobExecutionId) {
@@ -162,6 +192,13 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
162
192
  stepAttemptId: runningSelection.attemptId
163
193
  });
164
194
  }
195
+ function refreshLogs() {
196
+ if (!selectedLogAttempt) return;
197
+ setLogRefreshTokens((current)=>({
198
+ ...current,
199
+ [selectedLogAttempt.id]: (current[selectedLogAttempt.id] ?? 0) + 1
200
+ }));
201
+ }
165
202
  return /*#__PURE__*/ _jsx(TimeTickerProvider, {
166
203
  intervalMs: 1000,
167
204
  reducedMotionIntervalMs: 10_000,
@@ -203,47 +240,79 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
203
240
  execution: selectedJobExecution
204
241
  }) : undefined
205
242
  }),
206
- /*#__PURE__*/ _jsx(Text, {
207
- as: "h2",
208
- className: "sr-only",
209
- children: "Logs"
210
- }),
211
- selectedJobExecution ? /*#__PURE__*/ _jsxs(_Fragment, {
243
+ /*#__PURE__*/ _jsxs(Panel, {
244
+ "data-job-log-panel": true,
245
+ className: "min-w-0",
212
246
  children: [
213
- /*#__PURE__*/ _jsx(MaterializedOutputFailureNotice, {
214
- jobExecution: selectedJobExecution
247
+ /*#__PURE__*/ _jsx(JobLogPanelHeader, {
248
+ stepLabel: expandedLogSelection?.stepLabel,
249
+ attempt: selectedLogAttempt?.attempt,
250
+ status: selectedLogStatus,
251
+ search: logSearch,
252
+ onSearchChange: setLogSearch,
253
+ onRefresh: refreshLogs,
254
+ refreshing: logIsFetching,
255
+ showLineNumbers: showLineNumbers,
256
+ onShowLineNumbersChange: setShowLineNumbers,
257
+ wrap: wrapLogs,
258
+ onWrapChange: setWrapLogs,
259
+ disabled: !selectedLogAttempt
215
260
  }),
216
- /*#__PURE__*/ _jsx(StepList, {
217
- job: job,
218
- jobExecution: selectedJobExecution,
219
- selectedAttemptId: selectedAttemptId,
220
- defaultSelectedAttemptId: landingSelection?.attemptId,
221
- onSelectedAttemptChange: selectAttempt,
222
- inspectorOpenAttemptId: inspectorOpenAttemptId,
223
- onInspectorOpenChange: onInspectorOpenChange,
224
- autoSelectActiveAttempt: true,
225
- emptyState: emptyStateForJob(job, selectedJobExecution),
226
- showHeader: false,
227
- className: "rounded-none border-0 bg-transparent",
228
- renderExpandedStep: (context)=>/*#__PURE__*/ _jsx(ExpandedStep, {
229
- context: context,
230
- pageScrollRef: pageScrollRef
261
+ /*#__PURE__*/ _jsxs(PanelBody, {
262
+ className: "min-w-0 p-0",
263
+ children: [
264
+ /*#__PURE__*/ _jsx(Text, {
265
+ as: "h2",
266
+ className: "sr-only",
267
+ children: "Logs"
231
268
  }),
232
- renderInspector: (entry)=>/*#__PURE__*/ _jsx(StepInspectorSheet, {
233
- entry: entry,
234
- open: true,
235
- onOpenChange: (open)=>onInspectorOpenChange(open ? entry.id : null),
236
- workspaceSlug: workspaceSlug,
237
- projectSlug: projectSlug,
238
- workflowRunId: run.id,
239
- runAttempt: run.runAttempt.attempt,
240
- jobId: job.id,
241
- annotationCount: annotationCountForStep(annotationSummaryQuery.data, entry.step.id, entry.attempt)
269
+ selectedJobExecution ? /*#__PURE__*/ _jsxs(_Fragment, {
270
+ children: [
271
+ /*#__PURE__*/ _jsx(MaterializedOutputFailureNotice, {
272
+ jobExecution: selectedJobExecution
273
+ }),
274
+ /*#__PURE__*/ _jsx(StepList, {
275
+ job: job,
276
+ jobExecution: selectedJobExecution,
277
+ selectedAttemptId: selectedAttemptId,
278
+ defaultSelectedAttemptId: landingSelection?.attemptId,
279
+ onSelectedAttemptChange: selectAttempt,
280
+ onExpandedAttemptIdsChange: setExpandedLogAttemptIds,
281
+ inspectorOpenAttemptId: inspectorOpenAttemptId,
282
+ onInspectorOpenChange: onInspectorOpenChange,
283
+ autoSelectActiveAttempt: true,
284
+ emptyState: emptyStateForJob(job, selectedJobExecution),
285
+ showHeader: false,
286
+ className: "rounded-none border-0 bg-transparent shadow-none",
287
+ renderExpandedStep: (context)=>/*#__PURE__*/ _jsx(ExpandedStep, {
288
+ context: context,
289
+ pageScrollRef: pageScrollRef,
290
+ search: logSearch,
291
+ wrap: wrapLogs,
292
+ showLineNumbers: showLineNumbers,
293
+ attemptId: context.attemptId,
294
+ refreshToken: logRefreshTokens[context.attemptId] ?? 0,
295
+ onFetchingChange: handleLogFetchingChange
296
+ }),
297
+ renderInspector: (entry)=>/*#__PURE__*/ _jsx(StepInspectorSheet, {
298
+ entry: entry,
299
+ open: true,
300
+ onOpenChange: (open)=>onInspectorOpenChange(open ? entry.id : null),
301
+ workspaceSlug: workspaceSlug,
302
+ projectSlug: projectSlug,
303
+ workflowRunId: run.id,
304
+ runAttempt: run.runAttempt.attempt,
305
+ jobId: job.id,
306
+ annotationCount: annotationCountForStep(annotationSummaryQuery.data, entry.step.id, entry.attempt)
307
+ })
308
+ })
309
+ ]
310
+ }) : /*#__PURE__*/ _jsx(EmptyStateForMissingExecution, {
311
+ job: job
242
312
  })
313
+ ]
243
314
  })
244
315
  ]
245
- }) : /*#__PURE__*/ _jsx(EmptyStateForMissingExecution, {
246
- job: job
247
316
  })
248
317
  ]
249
318
  }),
@@ -285,23 +354,127 @@ export function JobDetailView({ workspaceSlug, projectSlug, workflowRunId, jobId
285
354
  })
286
355
  });
287
356
  }
288
- function ExpandedStep({ context, pageScrollRef }) {
357
+ function ExpandedStep({ context, pageScrollRef, search, wrap, showLineNumbers, attemptId, refreshToken, onFetchingChange }) {
289
358
  if (context.carriedOver) return /*#__PURE__*/ _jsx(CarriedOverStepPanel, {});
290
359
  return /*#__PURE__*/ _jsx("section", {
291
360
  role: "region",
292
361
  tabIndex: 0,
293
362
  "aria-label": `${context.stepLabel} output, attempt ${context.attempt}`,
294
- className: "flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle",
363
+ className: "flex min-w-0 flex-col border-t border-border-neutral-base bg-background-contrast-base outline-none focus-visible:shadow-border-interactive-with-active",
295
364
  children: /*#__PURE__*/ _jsx(StepAttemptLogPanel, {
296
365
  stepId: context.stepId,
297
366
  attempt: context.attempt,
298
367
  attemptStatus: context.attemptStatus,
299
368
  attemptStartedAt: context.attemptStartedAt,
300
369
  pageScrollRef: pageScrollRef,
301
- surfaceClassName: "rounded-none border-0 bg-transparent shadow-none"
370
+ search: search,
371
+ wrap: wrap,
372
+ showLineNumbers: showLineNumbers,
373
+ attemptId: attemptId,
374
+ refreshToken: refreshToken,
375
+ onFetchingChange: onFetchingChange
302
376
  })
303
377
  });
304
378
  }
379
+ function JobLogPanelHeader({ stepLabel, attempt, status, search, onSearchChange, onRefresh, refreshing, showLineNumbers, onShowLineNumbersChange, wrap, onWrapChange, disabled }) {
380
+ const statusVisual = status ? getStepStatusVisual(status) : undefined;
381
+ return /*#__PURE__*/ _jsxs(PanelHeader, {
382
+ className: "flex flex-wrap items-center gap-group",
383
+ children: [
384
+ /*#__PURE__*/ _jsxs("div", {
385
+ className: "min-w-0 flex-1",
386
+ children: [
387
+ /*#__PURE__*/ _jsx(Text, {
388
+ size: "sm",
389
+ bold: true,
390
+ className: "truncate text-foreground-neutral-base",
391
+ children: stepLabel ?? 'Logs'
392
+ }),
393
+ statusVisual ? /*#__PURE__*/ _jsxs("div", {
394
+ className: "flex min-w-0 items-center gap-inline text-foreground-neutral-muted",
395
+ children: [
396
+ /*#__PURE__*/ _jsx(Badge, {
397
+ variant: statusVisual.badge,
398
+ size: "2xs",
399
+ radius: "rounded",
400
+ children: statusVisual.label
401
+ }),
402
+ attempt ? /*#__PURE__*/ _jsxs("span", {
403
+ className: "font-code text-xs leading-20 tabular-nums",
404
+ children: [
405
+ "attempt ",
406
+ attempt
407
+ ]
408
+ }) : null
409
+ ]
410
+ }) : /*#__PURE__*/ _jsx(Text, {
411
+ size: "xs",
412
+ className: "text-foreground-neutral-muted",
413
+ children: "Select a step to view its logs."
414
+ })
415
+ ]
416
+ }),
417
+ /*#__PURE__*/ _jsxs(PanelActions, {
418
+ className: "min-w-0 flex-wrap justify-end",
419
+ children: [
420
+ /*#__PURE__*/ _jsx(SearchInline, {
421
+ value: search,
422
+ onChange: (event)=>onSearchChange(event.target.value),
423
+ "aria-label": "Search logs",
424
+ placeholder: "Search logs",
425
+ size: "small",
426
+ className: "w-180 max-w-full",
427
+ disabled: disabled
428
+ }),
429
+ /*#__PURE__*/ _jsx(IconButton, {
430
+ type: "button",
431
+ variant: "transparent",
432
+ size: "sm",
433
+ icon: "refreshLine",
434
+ "aria-label": "Refresh logs",
435
+ onClick: onRefresh,
436
+ disabled: disabled || refreshing,
437
+ isLoading: refreshing
438
+ }),
439
+ /*#__PURE__*/ _jsxs(DropdownMenu, {
440
+ children: [
441
+ /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
442
+ asChild: true,
443
+ children: /*#__PURE__*/ _jsx(IconButton, {
444
+ type: "button",
445
+ variant: "transparent",
446
+ size: "sm",
447
+ icon: "settings3Line",
448
+ "aria-label": "Log settings",
449
+ disabled: disabled
450
+ })
451
+ }),
452
+ /*#__PURE__*/ _jsxs(DropdownMenuContent, {
453
+ align: "end",
454
+ size: "sm",
455
+ children: [
456
+ /*#__PURE__*/ _jsx(DropdownMenuLabel, {
457
+ children: "Log display"
458
+ }),
459
+ /*#__PURE__*/ _jsx(DropdownMenuCheckboxItem, {
460
+ checked: showLineNumbers,
461
+ onCheckedChange: onShowLineNumbersChange,
462
+ children: "Line numbers"
463
+ }),
464
+ /*#__PURE__*/ _jsx(DropdownMenuCheckboxItem, {
465
+ checked: wrap,
466
+ onCheckedChange: onWrapChange,
467
+ children: "Wrap long lines"
468
+ })
469
+ ]
470
+ })
471
+ ]
472
+ })
473
+ ]
474
+ })
475
+ ]
476
+ });
477
+ }
305
478
  function EmptyStateForMissingExecution({ job }) {
306
479
  const emptyState = emptyStateForMissingExecution(job);
307
480
  return /*#__PURE__*/ _jsx(EmptyState, {
@@ -379,7 +552,7 @@ function NewerAttemptNotice({ workspaceSlug, projectSlug, runId, jobId, attempt
379
552
  })
380
553
  });
381
554
  }
382
- function runningStepSelection(jobExecution) {
555
+ function runningStepSelection(jobExecution, model) {
383
556
  if (!jobExecution) return undefined;
384
557
  const steps = [
385
558
  ...jobExecution.steps
@@ -393,7 +566,7 @@ function runningStepSelection(jobExecution) {
393
566
  if (attempt) return {
394
567
  stepId: step.id,
395
568
  attemptId: attempt.id,
396
- stepLabel: step.name || step.key || step.id
569
+ stepLabel: model.entries.find((entry)=>entry.id === attempt.id)?.step.label ?? 'Step'
397
570
  };
398
571
  }
399
572
  return undefined;
@@ -408,6 +581,22 @@ function findAttempt(jobExecution, attemptId) {
408
581
  }
409
582
  return undefined;
410
583
  }
584
+ function findExpandedLogSelection(jobExecution, attemptIds, model) {
585
+ if (!jobExecution) return undefined;
586
+ for(let index = attemptIds.length - 1; index >= 0; index -= 1){
587
+ const attemptId = attemptIds[index];
588
+ if (!attemptId) continue;
589
+ const match = findAttempt(jobExecution, attemptId);
590
+ const attempt = match?.step.attempts.find((candidate)=>candidate.id === attemptId);
591
+ const entry = model.entries.find((candidate)=>candidate.id === attemptId);
592
+ if (match && attempt && entry) return {
593
+ step: match.step,
594
+ attempt,
595
+ stepLabel: entry.step.label
596
+ };
597
+ }
598
+ return undefined;
599
+ }
411
600
  function annotationCountForStep(summary, stepId, attempt) {
412
601
  return summary?.stepCounts?.find((entry)=>entry.stepId === stepId && entry.attempt === attempt)?.total;
413
602
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\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 <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={(context) => (\n <ExpandedStep context={context} pageScrollRef={pageScrollRef} />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-none border-0 bg-transparent shadow-none\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(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 {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","MaterializedOutputFailureNotice","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Panel, PanelActions, PanelBody, PanelHeader} from '@shipfox/react-ui/panel';\nimport {SearchInline} from '@shipfox/react-ui/search';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useCallback, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n buildStepListModel,\n getStepStatusVisual,\n type StepListModel,\n} from '../step-list/step-list-model.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const [logSearch, setLogSearch] = useState('');\n const [wrapLogs, setWrapLogs] = useState(false);\n const [showLineNumbers, setShowLineNumbers] = useState(true);\n const [expandedLogAttemptIds, setExpandedLogAttemptIds] = useState<readonly string[]>([]);\n const [logRefreshTokens, setLogRefreshTokens] = useState<Record<string, number>>({});\n const [logFetchingByAttemptId, setLogFetchingByAttemptId] = useState<Record<string, boolean>>({});\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n const handleLogFetchingChange = useCallback((attemptId: string, isFetching: boolean) => {\n setLogFetchingByAttemptId((current) => {\n if (current[attemptId] === isFetching) return current;\n return {...current, [attemptId]: isFetching};\n });\n }, []);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\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 <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const stepListModel = buildStepListModel({job, jobExecution: selectedJobExecution});\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution, stepListModel);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const expandedLogSelection = findExpandedLogSelection(\n selectedJobExecution,\n expandedLogAttemptIds,\n stepListModel,\n );\n const selectedLogStep = expandedLogSelection?.step;\n const selectedLogAttempt = expandedLogSelection?.attempt;\n const selectedLogStatus = selectedLogAttempt?.status ?? selectedLogStep?.status;\n const logIsFetching = Boolean(\n selectedLogAttempt && logFetchingByAttemptId[selectedLogAttempt.id],\n );\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n function refreshLogs() {\n if (!selectedLogAttempt) return;\n setLogRefreshTokens((current) => ({\n ...current,\n [selectedLogAttempt.id]: (current[selectedLogAttempt.id] ?? 0) + 1,\n }));\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Panel data-job-log-panel className=\"min-w-0\">\n <JobLogPanelHeader\n stepLabel={expandedLogSelection?.stepLabel}\n attempt={selectedLogAttempt?.attempt}\n status={selectedLogStatus}\n search={logSearch}\n onSearchChange={setLogSearch}\n onRefresh={refreshLogs}\n refreshing={logIsFetching}\n showLineNumbers={showLineNumbers}\n onShowLineNumbersChange={setShowLineNumbers}\n wrap={wrapLogs}\n onWrapChange={setWrapLogs}\n disabled={!selectedLogAttempt}\n />\n <PanelBody className=\"min-w-0 p-0\">\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n onExpandedAttemptIdsChange={setExpandedLogAttemptIds}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent shadow-none\"\n renderExpandedStep={(context) => (\n <ExpandedStep\n context={context}\n pageScrollRef={pageScrollRef}\n search={logSearch}\n wrap={wrapLogs}\n showLineNumbers={showLineNumbers}\n attemptId={context.attemptId}\n refreshToken={logRefreshTokens[context.attemptId] ?? 0}\n onFetchingChange={handleLogFetchingChange}\n />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </PanelBody>\n </Panel>\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n search,\n wrap,\n showLineNumbers,\n attemptId,\n refreshToken,\n onFetchingChange,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n search: string;\n wrap: boolean;\n showLineNumbers: boolean;\n attemptId: string;\n refreshToken: number;\n onFetchingChange: (attemptId: string, isFetching: boolean) => void;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-contrast-base outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n search={search}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n attemptId={attemptId}\n refreshToken={refreshToken}\n onFetchingChange={onFetchingChange}\n />\n </section>\n );\n}\n\nfunction JobLogPanelHeader({\n stepLabel,\n attempt,\n status,\n search,\n onSearchChange,\n onRefresh,\n refreshing,\n showLineNumbers,\n onShowLineNumbersChange,\n wrap,\n onWrapChange,\n disabled,\n}: {\n stepLabel: string | undefined;\n attempt: number | undefined;\n status: string | undefined;\n search: string;\n onSearchChange: (value: string) => void;\n onRefresh: () => void;\n refreshing: boolean;\n showLineNumbers: boolean;\n onShowLineNumbersChange: (value: boolean) => void;\n wrap: boolean;\n onWrapChange: (value: boolean) => void;\n disabled: boolean;\n}) {\n const statusVisual = status ? getStepStatusVisual(status) : undefined;\n\n return (\n <PanelHeader className=\"flex flex-wrap items-center gap-group\">\n <div className=\"min-w-0 flex-1\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {stepLabel ?? 'Logs'}\n </Text>\n {statusVisual ? (\n <div className=\"flex min-w-0 items-center gap-inline text-foreground-neutral-muted\">\n <Badge variant={statusVisual.badge} size=\"2xs\" radius=\"rounded\">\n {statusVisual.label}\n </Badge>\n {attempt ? (\n <span className=\"font-code text-xs leading-20 tabular-nums\">attempt {attempt}</span>\n ) : null}\n </div>\n ) : (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Select a step to view its logs.\n </Text>\n )}\n </div>\n <PanelActions className=\"min-w-0 flex-wrap justify-end\">\n <SearchInline\n value={search}\n onChange={(event) => onSearchChange(event.target.value)}\n aria-label=\"Search logs\"\n placeholder=\"Search logs\"\n size=\"small\"\n className=\"w-180 max-w-full\"\n disabled={disabled}\n />\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n icon=\"refreshLine\"\n aria-label=\"Refresh logs\"\n onClick={onRefresh}\n disabled={disabled || refreshing}\n isLoading={refreshing}\n />\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n icon=\"settings3Line\"\n aria-label=\"Log settings\"\n disabled={disabled}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" size=\"sm\">\n <DropdownMenuLabel>Log display</DropdownMenuLabel>\n <DropdownMenuCheckboxItem\n checked={showLineNumbers}\n onCheckedChange={onShowLineNumbersChange}\n >\n Line numbers\n </DropdownMenuCheckboxItem>\n <DropdownMenuCheckboxItem checked={wrap} onCheckedChange={onWrapChange}>\n Wrap long lines\n </DropdownMenuCheckboxItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </PanelActions>\n </PanelHeader>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined, model: StepListModel) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {\n stepId: step.id,\n attemptId: attempt.id,\n stepLabel: model.entries.find((entry) => entry.id === attempt.id)?.step.label ?? 'Step',\n };\n }\n return undefined;\n}\n\nfunction findAttempt(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 {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction findExpandedLogSelection(\n jobExecution: JobExecution | undefined,\n attemptIds: readonly string[],\n model: StepListModel,\n) {\n if (!jobExecution) return undefined;\n\n for (let index = attemptIds.length - 1; index >= 0; index -= 1) {\n const attemptId = attemptIds[index];\n if (!attemptId) continue;\n const match = findAttempt(jobExecution, attemptId);\n const attempt = match?.step.attempts.find((candidate) => candidate.id === attemptId);\n const entry = model.entries.find((candidate) => candidate.id === attemptId);\n if (match && attempt && entry) return {step: match.step, attempt, stepLabel: entry.step.label};\n }\n\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","Badge","IconButton","DropdownMenu","DropdownMenuCheckboxItem","DropdownMenuContent","DropdownMenuLabel","DropdownMenuTrigger","EmptyState","Icon","Panel","PanelActions","PanelBody","PanelHeader","SearchInline","Skeleton","TimeTickerProvider","Text","Link","useCallback","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","buildStepListModel","getStepStatusVisual","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","MaterializedOutputFailureNotice","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","logSearch","setLogSearch","wrapLogs","setWrapLogs","showLineNumbers","setShowLineNumbers","expandedLogAttemptIds","setExpandedLogAttemptIds","logRefreshTokens","setLogRefreshTokens","logFetchingByAttemptId","setLogFetchingByAttemptId","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","handleLogFetchingChange","isFetching","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","stepListModel","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","expandedLogSelection","findExpandedLogSelection","selectedLogStep","selectedLogAttempt","selectedLogStatus","logIsFetching","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","refreshLogs","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","data-job-log-panel","JobLogPanelHeader","stepLabel","onSearchChange","onRefresh","refreshing","onShowLineNumbersChange","wrap","onWrapChange","disabled","as","defaultSelectedAttemptId","onSelectedAttemptChange","onExpandedAttemptIdsChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","refreshToken","onFetchingChange","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","statusVisual","bold","variant","badge","radius","label","value","onChange","event","target","placeholder","isLoading","asChild","align","checked","onCheckedChange","title","description","to","params","name","aria-hidden","model","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","entries","attemptIds","index","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}