@shipfox/client-workflows 20.0.0 → 22.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 (118) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +51 -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 +232 -44
  5. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  6. package/dist/components/job-detail/job-empty-states.js +2 -2
  7. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  8. package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
  9. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
  10. package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
  11. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
  12. package/dist/components/job-graph/job-graph-content.js +2 -2
  13. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  14. package/dist/components/step-list/step-list-model.d.ts +1 -0
  15. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  16. package/dist/components/step-list/step-list-model.js +1 -1
  17. package/dist/components/step-list/step-list-model.js.map +1 -1
  18. package/dist/components/step-list/step-list.d.ts +2 -1
  19. package/dist/components/step-list/step-list.d.ts.map +1 -1
  20. package/dist/components/step-list/step-list.js +25 -9
  21. package/dist/components/step-list/step-list.js.map +1 -1
  22. package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
  23. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  24. package/dist/components/workflow-run-list/job-status-strip.js +22 -11
  25. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  26. package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
  27. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
  28. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  29. package/dist/components/workflow-run-list/workflow-run-list-content.js +13 -7
  30. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  31. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  32. package/dist/components/workflow-run-list/workflow-run-list-states.js +43 -38
  33. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  34. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  35. package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
  36. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  37. package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
  38. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  39. package/dist/components/workflow-run-list/workflow-run-row.js +25 -18
  40. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  41. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  42. package/dist/components/workflow-run-summary/workflow-run-summary.js +4 -4
  43. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  44. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  45. package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
  46. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  47. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  48. package/dist/components/workflow-run-view/workflow-run-states.js +1 -1
  49. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  50. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  51. package/dist/components/workflow-run-view/workflow-run-view.js +103 -78
  52. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  53. package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
  54. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
  55. package/dist/pages/project-workflows-page.d.ts.map +1 -1
  56. package/dist/pages/project-workflows-page.js +171 -158
  57. package/dist/pages/project-workflows-page.js.map +1 -1
  58. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
  59. package/dist/pages/workflow-job-detail-page.js +3 -2
  60. package/dist/pages/workflow-job-detail-page.js.map +1 -1
  61. package/dist/pages/workflow-run-detail-page.js +2 -1
  62. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  63. package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
  64. package/dist/pages/workflow-run-list-page.js +9 -11
  65. package/dist/pages/workflow-run-list-page.js.map +1 -1
  66. package/dist/routes/job-detail.d.ts +1 -1
  67. package/dist/routes/job-detail.js +1 -1
  68. package/dist/routes/job-detail.js.map +1 -1
  69. package/dist/routes/run-detail.d.ts +1 -1
  70. package/dist/routes/run-detail.js +1 -1
  71. package/dist/routes/run-detail.js.map +1 -1
  72. package/dist/routes/runs.d.ts +1 -1
  73. package/dist/routes/runs.js +1 -1
  74. package/dist/routes/runs.js.map +1 -1
  75. package/dist/routes/workflows.d.ts +3 -0
  76. package/dist/routes/workflows.js +3 -0
  77. package/dist/routes/workflows.js.map +1 -1
  78. package/dist/tsconfig.test.tsbuildinfo +1 -1
  79. package/package.json +8 -8
  80. package/src/components/job-detail/job-detail-view.tsx +264 -49
  81. package/src/components/job-detail/job-empty-states.tsx +2 -2
  82. package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
  83. package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
  84. package/src/components/job-graph/job-graph-content.tsx +2 -2
  85. package/src/components/job-graph/job-graph.stories.tsx +1 -1
  86. package/src/components/job-graph/job-node.stories.tsx +1 -1
  87. package/src/components/step-list/step-list-model.ts +1 -1
  88. package/src/components/step-list/step-list.test.tsx +29 -1
  89. package/src/components/step-list/step-list.tsx +29 -11
  90. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  91. package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
  92. package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
  93. package/src/components/workflow-run-list/workflow-run-list-states.tsx +42 -41
  94. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +53 -4
  95. package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
  96. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
  97. package/src/components/workflow-run-list/workflow-run-row.tsx +26 -19
  98. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +3 -3
  99. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +11 -2
  100. package/src/components/workflow-run-summary/workflow-run-summary.tsx +4 -7
  101. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
  102. package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
  103. package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
  104. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +142 -0
  105. package/src/components/workflow-run-view/workflow-run-view.test.tsx +42 -8
  106. package/src/components/workflow-run-view/workflow-run-view.tsx +112 -96
  107. package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
  108. package/src/pages/project-workflows-page.tsx +135 -125
  109. package/src/pages/workflow-job-detail-page.test.tsx +32 -0
  110. package/src/pages/workflow-job-detail-page.tsx +5 -1
  111. package/src/pages/workflow-run-detail-page.tsx +1 -1
  112. package/src/pages/workflow-run-list-page.tsx +9 -11
  113. package/src/pages/workflow-run-pages.test.tsx +26 -0
  114. package/src/routes/job-detail.tsx +1 -1
  115. package/src/routes/run-detail.tsx +1 -1
  116. package/src/routes/runs.tsx +1 -1
  117. package/src/routes/workflows.tsx +1 -0
  118. 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 (531.81ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # @shipfox/client-workflows
2
2
 
3
+ ## 22.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 7693eb3: Render empty and load-error states inside bordered panel bodies and keep loading placeholders aligned with their data regions.
8
+ - bc440f2: Uses the standard full-width layout for workflow run details and a destructive cancellation action.
9
+ - Updated dependencies [50b3867]
10
+ - Updated dependencies [00c1cb8]
11
+ - Updated dependencies [7693eb3]
12
+ - Updated dependencies [00c1cb8]
13
+ - Updated dependencies [00c1cb8]
14
+ - Updated dependencies [00c1cb8]
15
+ - Updated dependencies [56f4526]
16
+ - Updated dependencies [af6b31e]
17
+ - @shipfox/client-shell@22.0.0
18
+ - @shipfox/react-ui@2.1.0
19
+ - @shipfox/client-ui@22.0.0
20
+ - @shipfox/client-projects@22.0.0
21
+ - @shipfox/client-triggers@22.0.0
22
+ - @shipfox/api-workflows-dto@13.0.0
23
+ - @shipfox/client-logs@22.0.0
24
+
25
+ ## 21.0.0
26
+
27
+ ### Patch Changes
28
+
29
+ - 6425336: Aligns the run workspace rail with its content frame at wide viewport sizes while keeping dedicated job content full width.
30
+ - 36f284f: Recomposes workflow runs into a full-width data panel and shows the complete job preview.
31
+ - 261701e: Removes per-page canvas and width overrides so workflow pages render full-width surfaces instead of a locally painted, capped 1120px column.
32
+ - 7db4171: Composes job detail logs into the Panel surface with searchable output and display controls.
33
+ - 6703982: Retire the `background-neutral-background` CSS token and migrate workflow page canvases to `background-subtle-base`.
34
+ - 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.
35
+ - Updated dependencies [0f4abe4]
36
+ - Updated dependencies [71d0c44]
37
+ - Updated dependencies [9c21429]
38
+ - Updated dependencies [30beb8f]
39
+ - Updated dependencies [7db4171]
40
+ - Updated dependencies [16733a7]
41
+ - Updated dependencies [fd15e1f]
42
+ - Updated dependencies [163c40a]
43
+ - Updated dependencies [c4376a1]
44
+ - Updated dependencies [6703982]
45
+ - Updated dependencies [f1d127e]
46
+ - Updated dependencies [0e860d7]
47
+ - @shipfox/react-ui@2.0.0
48
+ - @shipfox/client-shell@21.0.0
49
+ - @shipfox/client-logs@21.0.0
50
+ - @shipfox/client-triggers@21.0.0
51
+ - @shipfox/client-projects@21.0.0
52
+ - @shipfox/client-ui@21.0.0
53
+
3
54
  ## 20.0.0
4
55
 
5
56
  ### 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,31 +354,134 @@ 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, {
308
- className: "min-h-120 p-panel",
309
481
  icon: "componentLine",
310
482
  title: emptyState.title,
311
483
  description: emptyState.description,
312
- variant: "compact"
484
+ variant: "panel"
313
485
  });
314
486
  }
315
487
  function JobNotFoundState({ workspaceSlug, projectSlug, workflowRunId, search }) {
@@ -379,7 +551,7 @@ function NewerAttemptNotice({ workspaceSlug, projectSlug, runId, jobId, attempt
379
551
  })
380
552
  });
381
553
  }
382
- function runningStepSelection(jobExecution) {
554
+ function runningStepSelection(jobExecution, model) {
383
555
  if (!jobExecution) return undefined;
384
556
  const steps = [
385
557
  ...jobExecution.steps
@@ -393,7 +565,7 @@ function runningStepSelection(jobExecution) {
393
565
  if (attempt) return {
394
566
  stepId: step.id,
395
567
  attemptId: attempt.id,
396
- stepLabel: step.name || step.key || step.id
568
+ stepLabel: model.entries.find((entry)=>entry.id === attempt.id)?.step.label ?? 'Step'
397
569
  };
398
570
  }
399
571
  return undefined;
@@ -408,6 +580,22 @@ function findAttempt(jobExecution, attemptId) {
408
580
  }
409
581
  return undefined;
410
582
  }
583
+ function findExpandedLogSelection(jobExecution, attemptIds, model) {
584
+ if (!jobExecution) return undefined;
585
+ for(let index = attemptIds.length - 1; index >= 0; index -= 1){
586
+ const attemptId = attemptIds[index];
587
+ if (!attemptId) continue;
588
+ const match = findAttempt(jobExecution, attemptId);
589
+ const attempt = match?.step.attempts.find((candidate)=>candidate.id === attemptId);
590
+ const entry = model.entries.find((candidate)=>candidate.id === attemptId);
591
+ if (match && attempt && entry) return {
592
+ step: match.step,
593
+ attempt,
594
+ stepLabel: entry.step.label
595
+ };
596
+ }
597
+ return undefined;
598
+ }
411
599
  function annotationCountForStep(summary, stepId, attempt) {
412
600
  return summary?.stepCounts?.find((entry)=>entry.stepId === stepId && entry.attempt === attempt)?.total;
413
601
  }