@shipfox/client-workflows 21.0.0 → 22.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +33 -0
  3. package/dist/components/job-detail/job-detail-view.js +1 -2
  4. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  5. package/dist/components/job-detail/job-empty-states.js +2 -2
  6. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  7. package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -1
  8. package/dist/components/job-detail/job-execution-time-text.js +2 -15
  9. package/dist/components/job-detail/job-execution-time-text.js.map +1 -1
  10. package/dist/components/job-graph/job-duration-format.js +1 -1
  11. package/dist/components/job-graph/job-duration-format.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.js +1 -2
  15. package/dist/components/step-list/step-list.js.map +1 -1
  16. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  17. package/dist/components/workflow-run-list/workflow-run-list-content.js +13 -7
  18. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  19. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  20. package/dist/components/workflow-run-list/workflow-run-list-states.js +42 -37
  21. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  22. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  23. package/dist/components/workflow-run-list/workflow-run-row.js +14 -10
  24. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  25. package/dist/components/workflow-run-summary/workflow-run-summary.js +3 -3
  26. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  27. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  28. package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
  29. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  30. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  31. package/dist/components/workflow-run-view/workflow-run-view.js +68 -50
  32. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  33. package/dist/pages/project-workflows-page.js +181 -172
  34. package/dist/pages/project-workflows-page.js.map +1 -1
  35. package/dist/tsconfig.test.tsbuildinfo +1 -1
  36. package/package.json +8 -8
  37. package/src/components/job-detail/job-detail-view.tsx +1 -2
  38. package/src/components/job-detail/job-empty-states.tsx +2 -2
  39. package/src/components/job-detail/job-execution-time-text.test.ts +46 -0
  40. package/src/components/job-detail/job-execution-time-text.tsx +2 -23
  41. package/src/components/job-graph/job-duration-format.ts +1 -1
  42. package/src/components/job-graph/job-graph-content.tsx +2 -2
  43. package/src/components/step-list/step-list.tsx +1 -2
  44. package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
  45. package/src/components/workflow-run-list/workflow-run-list-states.tsx +41 -40
  46. package/src/components/workflow-run-list/workflow-run-row.tsx +11 -8
  47. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +1 -1
  48. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +9 -1
  49. package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
  50. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
  51. package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
  52. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +3 -1
  53. package/src/components/workflow-run-view/workflow-run-view.test.tsx +11 -8
  54. package/src/components/workflow-run-view/workflow-run-view.tsx +78 -70
  55. package/src/pages/project-workflows-page.test.tsx +14 -4
  56. package/src/pages/project-workflows-page.tsx +138 -132
  57. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 86 files with swc (171.96ms)
2
+ Successfully compiled: 86 files with swc (218.13ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @shipfox/client-workflows
2
2
 
3
+ ## 22.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 50adc12: Displays live job duration timers in whole seconds.
8
+ - e99205b: Removes the redundant page heading and description from the workflows page.
9
+ - Updated dependencies [e92517f]
10
+ - @shipfox/client-shell@22.0.1
11
+ - @shipfox/client-triggers@22.0.1
12
+ - @shipfox/client-projects@22.0.1
13
+
14
+ ## 22.0.0
15
+
16
+ ### Patch Changes
17
+
18
+ - 7693eb3: Render empty and load-error states inside bordered panel bodies and keep loading placeholders aligned with their data regions.
19
+ - bc440f2: Uses the standard full-width layout for workflow run details and a destructive cancellation action.
20
+ - Updated dependencies [50b3867]
21
+ - Updated dependencies [00c1cb8]
22
+ - Updated dependencies [7693eb3]
23
+ - Updated dependencies [00c1cb8]
24
+ - Updated dependencies [00c1cb8]
25
+ - Updated dependencies [00c1cb8]
26
+ - Updated dependencies [56f4526]
27
+ - Updated dependencies [af6b31e]
28
+ - @shipfox/client-shell@22.0.0
29
+ - @shipfox/react-ui@2.1.0
30
+ - @shipfox/client-ui@22.0.0
31
+ - @shipfox/client-projects@22.0.0
32
+ - @shipfox/client-triggers@22.0.0
33
+ - @shipfox/api-workflows-dto@13.0.0
34
+ - @shipfox/client-logs@22.0.0
35
+
3
36
  ## 21.0.0
4
37
 
5
38
  ### Patch Changes
@@ -478,11 +478,10 @@ function JobLogPanelHeader({ stepLabel, attempt, status, search, onSearchChange,
478
478
  function EmptyStateForMissingExecution({ job }) {
479
479
  const emptyState = emptyStateForMissingExecution(job);
480
480
  return /*#__PURE__*/ _jsx(EmptyState, {
481
- className: "min-h-120 p-panel",
482
481
  icon: "componentLine",
483
482
  title: emptyState.title,
484
483
  description: emptyState.description,
485
- variant: "compact"
484
+ variant: "panel"
486
485
  });
487
486
  }
488
487
  function JobNotFoundState({ workspaceSlug, projectSlug, workflowRunId, search }) {
@@ -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 {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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"}
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 icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"panel\"\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":";AAAA,gIAAgI;AAChI,+GAA+G;AAE/G,SAAQA,QAAQ,QAAO,sBAAsB;AAC7C,SAAQC,cAAc,QAAO,qBAAqB;AAClD,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SACEC,YAAY,EACZC,wBAAwB,EACxBC,mBAAmB,EACnBC,iBAAiB,EACjBC,mBAAmB,QACd,kCAAkC;AACzC,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,KAAK,EAAEC,YAAY,EAAEC,SAAS,EAAEC,WAAW,QAAO,0BAA0B;AACpF,SAAQC,YAAY,QAAO,2BAA2B;AACtD,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,kBAAkB,QAAO,gCAAgC;AACjE,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAwBC,WAAW,EAAEC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAE/E,SAAQC,uBAAuB,QAAO,0BAA0B;AAChE,SAAQC,qBAAqB,QAAoC,wBAAwB;AACzF,SAAQC,oCAAoC,QAAO,4BAA4B;AAC/E,SAAQC,sBAAsB,QAAO,gCAAgC;AAErE,SAEEC,uBAAuB,EACvBC,uBAAuB,QAClB,oBAAoB;AAC3B,SAAkCC,QAAQ,QAAO,wBAAwB;AACzE,SACEC,kBAAkB,EAClBC,mBAAmB,QAEd,kCAAkC;AACzC,SACEC,mBAAmB,EACnBC,qBAAqB,QAChB,8CAA8C;AACrD,SAAQC,eAAe,QAAO,yBAAyB;AACvD,SAAQC,eAAe,QAAO,yBAAyB;AACvD,SACEC,oBAAoB,EACpBC,gBAAgB,EAChBC,6BAA6B,EAC7BC,mBAAmB,EACnBC,+BAA+B,QAC1B,wBAAwB;AAC/B,SACEC,2BAA2B,EAE3BC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,mBAAmB,QAAO,8BAA8B;AAChE,SAAQC,kBAAkB,QAAO,4BAA4B;AAgB7D,OAAO,SAASC,cAAc,EAC5BC,aAAa,EACbC,WAAW,EACXC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,KAAK,EACLC,YAAY,EACZC,QAAQ,EACRC,iBAAiB,EACE;IACnB,MAAMC,UAAUlC,OAAuB;IACvC,MAAMmC,gBAAgBnC,OAAuB;IAC7C,MAAMoC,sBAAsBpC,OAA2CqC;IACvE,MAAM,CAACC,WAAWC,aAAa,GAAGtC,SAAS;IAC3C,MAAM,CAACuC,UAAUC,YAAY,GAAGxC,SAAS;IACzC,MAAM,CAACyC,iBAAiBC,mBAAmB,GAAG1C,SAAS;IACvD,MAAM,CAAC2C,uBAAuBC,yBAAyB,GAAG5C,SAA4B,EAAE;IACxF,MAAM,CAAC6C,kBAAkBC,oBAAoB,GAAG9C,SAAiC,CAAC;IAClF,MAAM,CAAC+C,wBAAwBC,0BAA0B,GAAGhD,SAAkC,CAAC;IAC/F,MAAMiD,gBAAgBpB,MAAMqB,IAAI,KAAKd;IACrC,0FAA0F;IAC1F,6FAA6F;IAC7F,MAAMe,cAAc/C,uBAAuB;QACzCsB;QACA0B,YAAYvB,MAAMqB,IAAI,EAAEE,WAAWC;IACrC;IACA,MAAMC,uBAAuBH,YAAYA,WAAW,GAChDlD,wBAAwBkD,YAAYA,WAAW,EAAExB,OAAO;QACtD4B,WAAWJ,YAAYK,OAAO,EAAED,aAAa;IAC/C,KACAnB;IACJ,MAAMqB,oBAAoB,GAAG9B,MAAM,CAAC,EAAEC,OAAO8B,cAAc,IAAI,IAAI;IACnE,MAAM,CAACC,gBAAgBC,kBAAkB,GAAG5D,SAAyB,IAAO,CAAA;YAC1E6D,KAAKJ;YACLK,WAAW;QACb,CAAA;IACA,MAAMC,yBACJJ,eAAeE,GAAG,KAAKJ,oBAAoBE,eAAeG,SAAS,GAAG;IACxE,MAAME,gBAAgBnC,MAAMqB,IAAI,EAAEe,KAAKC,KAAK,CAACC,YAAcA,UAAUC,EAAE,KAAKzC;IAC5E,MAAM0C,wBAAwBL,gBAC1B7C,4BAA4B;QAACmD,KAAKN;QAAeO,WAAW3C;IAAM,GAAG4C,YAAY,EAAEJ,KACnFxC,OAAO8B,cAAc;IACzB,MAAMe,oBAAoB5C,MAAMqB,IAAI,GAChC,CAAChD,sBAAsB2B,MAAMqB,IAAI,CAACE,UAAU,CAACsB,MAAM,IACnD;IACJ,MAAMC,yBAAyBxE,qCAC7B0B,MAAMqB,IAAI,EAAEkB,IACZvC,MAAMqB,IAAI,EAAEE,WAAWC,SACvBgB,uBACA;QAACO,SAASH;IAAiB;IAG7B3E,UAAU;QACR,IAAI,CAAC6B,SAAS,CAACsB,eAAe;QAC9B,MAAM4B,UAAU5C,QAAQ6C,OAAO,EAAEC,cAA2B;QAC5D,uFAAuF;QACvF,8EAA8E;QAC9EF,SAASG,MAAM;YAACC,eAAe;QAAI;IACrC,GAAG;QAAChC;QAAetB;KAAM;IAEzB,MAAMuD,0BAA0BrF,YAAY,CAACiE,WAAmBqB;QAC9DnC,0BAA0B,CAAC8B;YACzB,IAAIA,OAAO,CAAChB,UAAU,KAAKqB,YAAY,OAAOL;YAC9C,OAAO;gBAAC,GAAGA,OAAO;gBAAE,CAAChB,UAAU,EAAEqB;YAAU;QAC7C;IACF,GAAG,EAAE;IAEL,IAAItD,MAAMuD,SAAS,EAAE,qBAAO,KAACC;IAE7B,IAAIxD,MAAMyD,OAAO,IAAIzD,MAAMqB,IAAI,KAAKd,WAAW;QAC7C,IAAIP,MAAM0D,KAAK,YAAY9G,YAAYoD,MAAM0D,KAAK,CAACb,MAAM,KAAK,KAAK;YACjE,qBAAO,KAAChE;QACV;QACA,qBAAO,KAAChC;YAAemD,OAAOA;YAAO2D,SAAQ;YAAeC,MAAK;;IACnE;IAEA,IAAI5D,MAAMqB,IAAI,KAAKd,WAAW,qBAAO,KAACiD;IAEtC,MAAMK,MAAM7D,MAAMqB,IAAI;IACtB,MAAMoB,MAAMoB,IAAIzB,IAAI,CAACC,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKzC;IAC1D,IAAI,CAAC2C,KAAK;QACR,qBACE,KAACqB;YACCnE,eAAeA;YACfC,aAAaA;YACbC,eAAeA;YACfE,QAAQA;;IAGd;IAEA,MAAMgE,oBAAoBzE,4BAA4B;QAACmD;QAAKC,WAAW3C;IAAM;IAC7E,MAAMiE,uBAAuBD,kBAAkBpB,YAAY;IAC3D,MAAMsB,kBAAkBC,QAAQnE,OAAOoE,MAAM,IAAIJ,kBAAkBK,IAAI;IACvE,MAAMC,gBAAgB1F,mBAAmB;QAAC8D;QAAKE,cAAcqB;IAAoB;IACjF,MAAMM,0BAA0B/E,4BAA4ByE;IAC5D,IAAIA,sBAAsB;QACxB,MAAMO,gBAAgBjE,oBAAoB2C,OAAO;QACjD,IACE,CAACsB,iBACDA,cAAczE,KAAK,KAAK2C,IAAIF,EAAE,IAC9BgC,cAAc1C,cAAc,KAAKmC,qBAAqBzB,EAAE,EACxD;YACAjC,oBAAoB2C,OAAO,GAAG;gBAC5BnD,OAAO2C,IAAIF,EAAE;gBACbV,gBAAgBmC,qBAAqBzB,EAAE;gBACvCG,WAAW4B;gBACXE,cAAcF,4BAA4B/D;YAC5C;QACF,OAAO,IAAI,CAACgE,cAAcC,YAAY,IAAIF,yBAAyB;YACjEhE,oBAAoB2C,OAAO,GAAG;gBAC5B,GAAGsB,aAAa;gBAChB7B,WAAW4B;gBACXE,cAAc;YAChB;QACF;IACF,OAAO;QACLlE,oBAAoB2C,OAAO,GAAG1C;IAChC;IACA,MAAMkE,mBAAmBnE,oBAAoB2C,OAAO,EAAEP;IACtD,MAAMgC,oBAAoBT,kBAAkBF,kBAAkBW,iBAAiB,GAAGnE;IAClF,MAAMoE,mBAAmBC,qBAAqBZ,sBAAsBK;IACpE,MAAMQ,iBAAiBd,kBAAkBK,IAAI,EAAE7B,MAAMkC,kBAAkBN;IACvE,MAAMW,2BAA2Bb,kBAC7BF,kBAAkBW,iBAAiB,GACnCD,kBAAkBxC;IACtB,MAAM8C,uBAAuBC,yBAC3BhB,sBACAlD,uBACAuD;IAEF,MAAMY,kBAAkBF,sBAAsBX;IAC9C,MAAMc,qBAAqBH,sBAAsBvD;IACjD,MAAM2D,oBAAoBD,oBAAoBrC,UAAUoC,iBAAiBpC;IACzE,MAAMuC,gBAAgBlB,QACpBgB,sBAAsBhE,sBAAsB,CAACgE,mBAAmB3C,EAAE,CAAC;IAErE,MAAM8C,qBACJV,qBAAqBpE,aACpBsE,CAAAA,mBAAmBF,iBAAiBR,MAAM,IACzCW,6BAA6BH,iBAAiB1C,SAAS,AAAD;IAC1D,MAAMqD,mBACJ,CAACrB,mBAAmBQ,qBAAqBlE,aAAayD,uBAClD5E,oBAAoBqD,KAAKuB,wBACzBzD;IAEN,SAASgF,gBAAgB1D,cAAsB;QAC7C1B,kBAAkB;YAChB,GAAGJ,MAAM;YACT8B;YACAsC,QAAQ5D;YACRiF,eAAejF;QACjB;IACF;IAEA,SAASkF,cAAcxD,SAA6B;QAClD,IAAI,CAAC+B,sBAAsB;QAC3B,IAAI,CAAC/B,WAAW;YACd9B,kBAAkB;gBAChB,GAAGJ,MAAM;gBACT8B,gBAAgBmC,qBAAqBzB,EAAE;gBACvC4B,QAAQ5D;gBACRiF,eAAejF;YACjB;YACA;QACF;QAEA,MAAMmF,QAAQC,YAAY3B,sBAAsB/B;QAChD,IAAI,CAACyD,OAAO;QACZvF,kBAAkB;YAChB,GAAGJ,MAAM;YACT8B,gBAAgBmC,qBAAqBzB,EAAE;YACvC4B,QAAQuB,MAAMtB,IAAI,CAAC7B,EAAE;YACrBiD,eAAeE,MAAMzD,SAAS;QAChC;IACF;IAEA,SAAS2D,sBAAsB3D,SAAwB;QACrDF,kBAAkB;YAACC,KAAKJ;YAAmBK;QAAS;IACtD;IAEA,SAAS4D;QACP,IAAI,CAAClB,oBAAoB,CAACX,sBAAsB;QAChD7D,kBAAkB;YAChB,GAAGJ,MAAM;YACT8B,gBAAgBmC,qBAAqBzB,EAAE;YACvC4B,QAAQQ,iBAAiBR,MAAM;YAC/BqB,eAAeb,iBAAiB1C,SAAS;QAC3C;IACF;IAEA,SAAS6D;QACP,IAAI,CAACZ,oBAAoB;QACzBjE,oBAAoB,CAACgC,UAAa,CAAA;gBAChC,GAAGA,OAAO;gBACV,CAACiC,mBAAmB3C,EAAE,CAAC,EAAE,AAACU,CAAAA,OAAO,CAACiC,mBAAmB3C,EAAE,CAAC,IAAI,CAAA,IAAK;YACnE,CAAA;IACF;IAEA,qBACE,KAAC1E;QAAmBkI,YAAY;QAAMC,yBAAyB;kBAC7D,cAAA,MAACC;YAAIC,KAAK9F;YAAS+F,WAAU;;gBAC1BnG,MAAMyD,OAAO,iBAAG,KAAC3E;oBAAsBkB,OAAOA;qBAAY;gBAC1DE,YAAYD,gBAAgBA,eAAe4D,IAAItC,UAAU,CAACC,OAAO,iBAChE,KAAC4E;oBACCzG,eAAeA;oBACfC,aAAaA;oBACbyG,OAAOxC,IAAItB,EAAE;oBACbzC,OAAOI,SAASqC,EAAE;oBAClBf,SAASvB;qBAET;8BACJ,KAACgG;oBAAIC,KAAK7F;oBAAe8F,WAAU;8BACjC,cAAA,MAACG;wBAAQC,cAAY,GAAG9D,IAAI+D,WAAW,CAAC,KAAK,CAAC;wBAAEL,WAAU;;0CACxD,MAACG;gCAAQH,WAAU;;kDACjB,KAACnH;wCACCyD,KAAKA;wCACLuB,sBAAsBA;wCACtByC,8BAA8BlB;wCAC9B5F,eAAeA;wCACfC,aAAaA;wCACbC,eAAegE,IAAItB,EAAE;wCACrBhB,YAAYsC,IAAItC,UAAU,CAACC,OAAO;wCAClCkF,mBAAmBjF;wCACnBkF,YACE3C,qCACE,KAACjF;4CAAgB0D,KAAKA;4CAAKmE,WAAW5C;6CACpCzD;;kDAGR,MAAChD;wCAAMsJ,oBAAkB;wCAACV,WAAU;;0DAClC,KAACW;gDACCC,WAAWhC,sBAAsBgC;gDACjCvF,SAAS0D,oBAAoB1D;gDAC7BqB,QAAQsC;gDACRpF,QAAQS;gDACRwG,gBAAgBvG;gDAChBwG,WAAWnB;gDACXoB,YAAY9B;gDACZxE,iBAAiBA;gDACjBuG,yBAAyBtG;gDACzBuG,MAAM1G;gDACN2G,cAAc1G;gDACd2G,UAAU,CAACpC;;0DAEb,MAACzH;gDAAU0I,WAAU;;kEACnB,KAACrI;wDAAKyJ,IAAG;wDAAKpB,WAAU;kEAAU;;oDAGjCnC,qCACC;;0EACE,KAAC3E;gEAAgCsD,cAAcqB;;0EAC/C,KAACtF;gEACC+D,KAAKA;gEACLE,cAAcqB;gEACdU,mBAAmBA;gEACnB8C,0BAA0B/C,kBAAkBxC;gEAC5CwF,yBAAyBhC;gEACzBiC,4BAA4B3G;gEAC5BmB,wBAAwBA;gEACxB0D,uBAAuBA;gEACvB+B,uBAAuB;gEACvBC,YAAY1I,iBAAiBuD,KAAKuB;gEAClC6D,YAAY;gEACZ1B,WAAU;gEACV2B,oBAAoB,CAACC,wBACnB,KAACC;wEACCD,SAASA;wEACT1H,eAAeA;wEACfN,QAAQS;wEACR4G,MAAM1G;wEACNE,iBAAiBA;wEACjBqB,WAAW8F,QAAQ9F,SAAS;wEAC5BgG,cAAcjH,gBAAgB,CAAC+G,QAAQ9F,SAAS,CAAC,IAAI;wEACrDiG,kBAAkB7E;;gEAGtB8E,iBAAiB,CAACC,sBAChB,KAAC3I;wEACC2I,OAAOA;wEACPC,IAAI;wEACJC,cAAc,CAACD,OAASzC,sBAAsByC,OAAOD,MAAM7F,EAAE,GAAG;wEAChE5C,eAAeA;wEACfC,aAAaA;wEACbC,eAAegE,IAAItB,EAAE;wEACrBhB,YAAYsC,IAAItC,UAAU,CAACC,OAAO;wEAClC1B,OAAO2C,IAAIF,EAAE;wEACbgG,iBAAiBC,uBACf1F,uBAAuBzB,IAAI,EAC3B+G,MAAMhE,IAAI,CAAC7B,EAAE,EACb6F,MAAM5G,OAAO;;;;uEAOvB,KAACiH;wDAA8BhG,KAAKA;;;;;;;;4BAK3C4C,sBAAsBV,iCACrB,MAACsB;gCACCyC,MAAK;gCACLC,aAAU;gCACVxC,WAAU;;kDAEV,MAACrI;wCAAK8K,MAAK;wCAAKzC,WAAU;;4CAAwC;4CAChD;0DAChB,KAAC0C;gDAAK1C,WAAU;0DACbxB,iBAAiBoC,SAAS;;4CACtB;;;kDAGT,KAAC+B;wCACCC,MAAK;wCACL5C,WAAU;wCACV6C,SAASnD;kDACV;;;iCAID;4BACHP,iCACC,KAACxH;gCAAK8K,MAAK;gCAAKzC,WAAU;0CACvBb;iCAED;;;;;;;AAMhB;AAEA,SAAS0C,aAAa,EACpBD,OAAO,EACP1H,aAAa,EACbN,MAAM,EACNqH,IAAI,EACJxG,eAAe,EACfqB,SAAS,EACTgG,YAAY,EACZC,gBAAgB,EAUjB;IACC,IAAIH,QAAQkB,WAAW,EAAE,qBAAO,KAAChK;IAEjC,qBACE,KAACqH;QACCoC,MAAK;QACLQ,UAAU;QACV3C,cAAY,GAAGwB,QAAQhB,SAAS,CAAC,iBAAiB,EAAEgB,QAAQvG,OAAO,EAAE;QACrE2E,WAAU;kBAEV,cAAA,KAAC3G;YACC2E,QAAQ4D,QAAQ5D,MAAM;YACtB3C,SAASuG,QAAQvG,OAAO;YACxB2H,eAAepB,QAAQoB,aAAa;YACpCC,kBAAkBrB,QAAQqB,gBAAgB;YAC1C/I,eAAeA;YACfN,QAAQA;YACRqH,MAAMA;YACNxG,iBAAiBA;YACjBqB,WAAWA;YACXgG,cAAcA;YACdC,kBAAkBA;;;AAI1B;AAEA,SAASpB,kBAAkB,EACzBC,SAAS,EACTvF,OAAO,EACPqB,MAAM,EACN9C,MAAM,EACNiH,cAAc,EACdC,SAAS,EACTC,UAAU,EACVtG,eAAe,EACfuG,uBAAuB,EACvBC,IAAI,EACJC,YAAY,EACZC,QAAQ,EAcT;IACC,MAAM+B,eAAexG,SAASjE,oBAAoBiE,UAAUtC;IAE5D,qBACE,MAAC7C;QAAYyI,WAAU;;0BACrB,MAACF;gBAAIE,WAAU;;kCACb,KAACrI;wBAAK8K,MAAK;wBAAKU,IAAI;wBAACnD,WAAU;kCAC5BY,aAAa;;oBAEfsC,6BACC,MAACpD;wBAAIE,WAAU;;0CACb,KAACrJ;gCAAMyM,SAASF,aAAaG,KAAK;gCAAEZ,MAAK;gCAAMa,QAAO;0CACnDJ,aAAaK,KAAK;;4BAEpBlI,wBACC,MAACqH;gCAAK1C,WAAU;;oCAA4C;oCAAS3E;;iCACnE;;uCAGN,KAAC1D;wBAAK8K,MAAK;wBAAKzC,WAAU;kCAAgC;;;;0BAK9D,MAAC3I;gBAAa2I,WAAU;;kCACtB,KAACxI;wBACCgM,OAAO5J;wBACP6J,UAAU,CAACC,QAAU7C,eAAe6C,MAAMC,MAAM,CAACH,KAAK;wBACtDpD,cAAW;wBACXwD,aAAY;wBACZnB,MAAK;wBACLzC,WAAU;wBACVmB,UAAUA;;kCAEZ,KAACvK;wBACCgM,MAAK;wBACLQ,SAAQ;wBACRX,MAAK;wBACLhF,MAAK;wBACL2C,cAAW;wBACXyC,SAAS/B;wBACTK,UAAUA,YAAYJ;wBACtB8C,WAAW9C;;kCAEb,MAAClK;;0CACC,KAACI;gCAAoB6M,OAAO;0CAC1B,cAAA,KAAClN;oCACCgM,MAAK;oCACLQ,SAAQ;oCACRX,MAAK;oCACLhF,MAAK;oCACL2C,cAAW;oCACXe,UAAUA;;;0CAGd,MAACpK;gCAAoBgN,OAAM;gCAAMtB,MAAK;;kDACpC,KAACzL;kDAAkB;;kDACnB,KAACF;wCACCkN,SAASvJ;wCACTwJ,iBAAiBjD;kDAClB;;kDAGD,KAAClK;wCAAyBkN,SAAS/C;wCAAMgD,iBAAiB/C;kDAAc;;;;;;;;;;AAQpF;AAEA,SAASoB,8BAA8B,EAAChG,GAAG,EAAa;IACtD,MAAMmF,aAAazI,8BAA8BsD;IACjD,qBACE,KAACpF;QACCuG,MAAK;QACLyG,OAAOzC,WAAWyC,KAAK;QACvBC,aAAa1C,WAAW0C,WAAW;QACnCf,SAAQ;;AAGd;AAEA,SAASzF,iBAAiB,EACxBnE,aAAa,EACbC,WAAW,EACXC,aAAa,EACbE,MAAM,EAMP;IACC,qBACE,MAACkG;QAAIE,WAAU;;0BACb,KAAC9I;gBACCuG,MAAK;gBACLyG,OAAM;gBACNC,aAAY;;0BAEd,MAACvM;gBACCwM,IAAG;gBACHC,QAAQ;oBAAC7K;oBAAeC;oBAAaC;gBAAa;gBAClDE,QACEtB,wBACE;oBAAC8C,YAAYxB,OAAOwB,UAAU;gBAAA,GAC9B;oBAACA,YAAYxB,OAAOwB,UAAU;gBAAA;gBAGlC4E,WAAU;;kCAEV,KAAC7I;wBAAKmN,MAAK;wBAAgB7B,MAAM;wBAAI8B,eAAY;;oBAAS;;;;;AAKlE;AAEA,SAAStE,mBAAmB,EAC1BzG,aAAa,EACbC,WAAW,EACXyG,KAAK,EACLvG,KAAK,EACL0B,OAAO,EAOR;IACC,qBACE,KAACyE;QAAIyC,MAAK;QAASvC,WAAU;kBAC3B,cAAA,MAACF;YAAIE,WAAU;;8BACb,KAACrI;oBAAK8K,MAAK;oBAAKzC,WAAU;8BAAgC;;8BAG1D,MAACpI;oBACCwM,IAAG;oBACHC,QAAQ;wBAAC7K;wBAAeC;wBAAaC,eAAewG;wBAAOvG;oBAAK;oBAChEC,QAAQvB,wBAAwB;wBAAC+C,YAAYC;oBAAO;oBACpD2E,WAAU;;wBACX;wBACgB3E;;;;;;AAKzB;AAEA,SAASoD,qBAAqBjC,YAAsC,EAAEgI,KAAoB;IACxF,IAAI,CAAChI,cAAc,OAAOpC;IAC1B,MAAMqK,QAAQ;WAAIjI,aAAaiI,KAAK;KAAC,CAACC,IAAI,CACxC,CAACC,MAAMC,QAAUD,KAAKE,QAAQ,GAAGD,MAAMC,QAAQ,IAAIF,KAAKvI,EAAE,CAAC0I,aAAa,CAACF,MAAMxI,EAAE;IAEnF,IAAK,IAAI2I,YAAYN,MAAMO,MAAM,GAAG,GAAGD,aAAa,GAAGA,aAAa,EAAG;QACrE,MAAM9G,OAAOwG,KAAK,CAACM,UAAU;QAC7B,IAAI,CAAC9G,MAAM;QACX,MAAM5C,UAAU;eAAI4C,KAAKgH,QAAQ;SAAC,CAC/BP,IAAI,CAAC,CAACC,MAAMC,QAAUD,KAAKtJ,OAAO,GAAGuJ,MAAMvJ,OAAO,IAAIsJ,KAAKvI,EAAE,CAAC0I,aAAa,CAACF,MAAMxI,EAAE,GACpF8I,OAAO,GACPhJ,IAAI,CAAC,CAACC,YAAcA,UAAUO,MAAM,KAAK;QAC5C,IAAIrB,SACF,OAAO;YACL2C,QAAQC,KAAK7B,EAAE;YACfN,WAAWT,QAAQe,EAAE;YACrBwE,WAAW4D,MAAMW,OAAO,CAACjJ,IAAI,CAAC,CAAC+F,QAAUA,MAAM7F,EAAE,KAAKf,QAAQe,EAAE,GAAG6B,KAAKsF,SAAS;QACnF;IACJ;IACA,OAAOnJ;AACT;AAEA,SAASoF,YAAYhD,YAA0B,EAAEV,SAAiB;IAChE,KAAK,MAAMmC,QAAQzB,aAAaiI,KAAK,CAAE;QACrC,MAAMpJ,UAAU4C,KAAKgH,QAAQ,CAAC/I,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKN;QACnE,IAAIT,SAAS,OAAO;YAAC4C;YAAMnC,WAAWT,QAAQe,EAAE;QAAA;IAClD;IACA,OAAOhC;AACT;AAEA,SAASyE,yBACPrC,YAAsC,EACtC4I,UAA6B,EAC7BZ,KAAoB;IAEpB,IAAI,CAAChI,cAAc,OAAOpC;IAE1B,IAAK,IAAIiL,QAAQD,WAAWJ,MAAM,GAAG,GAAGK,SAAS,GAAGA,SAAS,EAAG;QAC9D,MAAMvJ,YAAYsJ,UAAU,CAACC,MAAM;QACnC,IAAI,CAACvJ,WAAW;QAChB,MAAMyD,QAAQC,YAAYhD,cAAcV;QACxC,MAAMT,UAAUkE,OAAOtB,KAAKgH,SAAS/I,KAAK,CAACC,YAAcA,UAAUC,EAAE,KAAKN;QAC1E,MAAMmG,QAAQuC,MAAMW,OAAO,CAACjJ,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKN;QACjE,IAAIyD,SAASlE,WAAW4G,OAAO,OAAO;YAAChE,MAAMsB,MAAMtB,IAAI;YAAE5C;YAASuF,WAAWqB,MAAMhE,IAAI,CAACsF,KAAK;QAAA;IAC/F;IAEA,OAAOnJ;AACT;AAEA,SAASiI,uBACP7G,OAAyC,EACzCwC,MAAc,EACd3C,OAAe;IAEf,OAAOG,SAAS8J,YAAYpJ,KAAK,CAAC+F,QAAUA,MAAMjE,MAAM,KAAKA,UAAUiE,MAAM5G,OAAO,KAAKA,UACrFkK;AACN;AAEA,SAASlI;IACP,qBACE,KAACyC;QAAIE,WAAU;kBACb,cAAA,MAACF;YAAIE,WAAU;;8BACb,MAACwF;oBAAOxF,WAAU;;sCAChB,KAACvI;4BAASuI,WAAU;;sCACpB,KAACvI;4BAASuI,WAAU;;sCACpB,KAACvI;4BAASuI,WAAU;;sCACpB,KAACvI;4BAASuI,WAAU;;;;8BAEtB,KAACF;oBAAIE,WAAU;8BACZyF,yBAAyBC,GAAG,CAAC,CAACC,oBAC7B,MAAC7F;4BAECE,WAAU;;8CAEV,KAACvI;oCAASuI,WAAU;;8CACpB,KAACvI;oCAASuI,WAAU;;;2BAJf2F;;;;;AAWnB;AAEA,MAAMF,2BAA2B;IAAC;IAAU;IAAU;IAAU;IAAU;CAAS"}
@@ -205,11 +205,11 @@ function humanizeFailureReason(reason) {
205
205
  }
206
206
  export function CarriedOverStepPanel() {
207
207
  return /*#__PURE__*/ _jsx(EmptyState, {
208
- className: "min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base p-panel",
208
+ className: "rounded-8 border border-border-neutral-base bg-background-components-base",
209
209
  icon: "componentLine",
210
210
  title: "Carried over from a previous attempt",
211
211
  description: "Not executed in this run.",
212
- variant: "compact"
212
+ variant: "panel"
213
213
  });
214
214
  }
215
215
  export function toSelectedAttemptError(step, error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-empty-states.tsx"],"sourcesContent":["import {Callout, CalloutContent, CalloutDescription, CalloutTitle} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport type {Job, JobExecution, Step, StepError} from '#core/workflow-run.js';\nimport {\n AGENT_CONFIG_ISSUES,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n STEP_ERROR_REASONS,\n} from '#core/workflow-run.js';\nimport type {StepListEmptyState} from '../step-list/index.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\n\nconst MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION =\n 'A materialized job output could not be persisted: it exceeded a size or entry cap, contained a non-JSON-safe value, or referenced an unresolved value. Check the output mapping and values before re-running the workflow.';\nconst OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION =\n 'The materialized job output exceeded its configured size limit. Review the failure details before re-running the workflow.';\n\nexport function outputFailureDescriptionForExecution(\n jobExecution: JobExecution,\n): string | undefined {\n if (jobExecution.steps.length === 0) return undefined;\n\n if (jobExecution.statusReason === 'output_invalid') {\n return jobExecution.statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;\n }\n if (jobExecution.statusReason === 'output_too_large') {\n return jobExecution.statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;\n }\n return undefined;\n}\n\nexport function MaterializedOutputFailureNotice({jobExecution}: {jobExecution: JobExecution}) {\n const description = outputFailureDescriptionForExecution(jobExecution);\n if (!description) return null;\n\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"border-b border-border-neutral-base px-row py-row\"\n >\n <CalloutContent>\n <CalloutTitle>Job output could not be persisted</CalloutTitle>\n <CalloutDescription>{description}</CalloutDescription>\n </CalloutContent>\n </Callout>\n );\n}\n\nexport function emptyStateForJob(\n job: Job,\n jobExecution: JobExecution,\n): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n const runner = jobExecution.runner?.length ? jobExecution.runner : job.runner;\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: runner?.length ? 'Runner preparing job' : 'Waiting for a runner',\n description: runner?.length\n ? `Runner ${runner.join(', ')} is preparing the job. Steps will appear here when work begins.`\n : 'No runner has claimed this job yet. Steps will appear here when a runner starts work.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'failed') {\n return {\n title: 'Job failed before its first step started',\n description: preStepFailureDescription(\n jobExecution.statusReason ?? job.statusReason,\n runner,\n jobExecution.statusReasonMessage,\n ),\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded') {\n return {\n title: 'Completed without recorded steps',\n description: `Execution #${jobExecution.sequence} succeeded before any step output was recorded.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nexport function emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n if (job.status === 'failed') {\n return {\n title: 'Job failed before an execution was created',\n description: preStepFailureDescription(job.statusReason, job.runner),\n status: 'failed',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nexport function skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'output_invalid':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n case 'output_too_large':\n return 'The materialized job output exceeded its configured size limit.';\n }\n}\n\nfunction preStepFailureDescription(\n reason: string | null,\n runner: string[] | null = null,\n statusReasonMessage: string | null = null,\n): string {\n const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';\n\n switch (reason) {\n case 'runner_lost':\n return `The runner stopped responding before work began.${runnerCopy} Check runner availability before re-running the workflow.`;\n case 'timed_out':\n return `The job timed out before work began.${runnerCopy} Check runner capacity and timeout configuration before re-running the workflow.`;\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The execution ended while the run was being cancelled.';\n case 'step_failed':\n return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;\n case 'output_too_large':\n return statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;\n case 'output_invalid':\n return statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;\n case 'condition_errored':\n return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';\n case 'dependency_not_completed':\n case 'default_gate_rejected':\n return 'A required job did not complete successfully. Resolve that failure before re-running the workflow.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so no work started.';\n case 'unknown':\n case null:\n return `Shipfox did not record a failure reason.${runnerCopy} Re-run only after checking the runner and workflow configuration.`;\n default:\n return `The execution ended because ${humanizeFailureReason(reason)}.${runnerCopy} Resolve that condition before re-running the workflow.`;\n }\n}\n\nfunction humanizeFailureReason(reason: string): string {\n return reason.replaceAll('_', ' ').replaceAll('-', ' ');\n}\n\nexport function CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"min-h-120 rounded-8 border border-border-neutral-base bg-background-components-base p-panel\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"compact\"\n />\n );\n}\n\nexport function toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nexport function isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nexport function jobSucceededSummary(job: Job, execution: JobExecution): string | undefined {\n if (job.carriedOver || execution.status !== 'succeeded') return undefined;\n const stepCount = execution.steps.filter((step) => step.status === 'succeeded').length;\n if (stepCount === 0) return undefined;\n const duration = execution.displayDuration;\n return `${stepCount} step${stepCount === 1 ? '' : 's'} succeeded${duration ? ` in ${formatJobExecutionTime(duration)}` : ''}`;\n}\n"],"names":["Callout","CalloutContent","CalloutDescription","CalloutTitle","EmptyState","AGENT_CONFIG_ISSUES","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","formatJobExecutionTime","MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION","OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION","outputFailureDescriptionForExecution","jobExecution","steps","length","undefined","statusReason","statusReasonMessage","MaterializedOutputFailureNotice","description","role","type","variant","className","emptyStateForJob","job","carriedOver","title","status","displayStatus","mode","runner","join","preStepFailureDescription","sequence","emptyStateForMissingExecution","listenerStatus","skippedJobDescription","reason","runnerCopy","humanizeFailureReason","replaceAll","CarriedOverStepPanel","icon","toSelectedAttemptError","step","error","rawReason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","isAgentConfigFailure","jobSucceededSummary","execution","stepCount","filter","duration","displayDuration"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-empty-states.tsx"],"sourcesContent":["import {Callout, CalloutContent, CalloutDescription, CalloutTitle} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport type {Job, JobExecution, Step, StepError} from '#core/workflow-run.js';\nimport {\n AGENT_CONFIG_ISSUES,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n STEP_ERROR_REASONS,\n} from '#core/workflow-run.js';\nimport type {StepListEmptyState} from '../step-list/index.js';\nimport {formatJobExecutionTime} from './job-execution-time-text.js';\n\nconst MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION =\n 'A materialized job output could not be persisted: it exceeded a size or entry cap, contained a non-JSON-safe value, or referenced an unresolved value. Check the output mapping and values before re-running the workflow.';\nconst OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION =\n 'The materialized job output exceeded its configured size limit. Review the failure details before re-running the workflow.';\n\nexport function outputFailureDescriptionForExecution(\n jobExecution: JobExecution,\n): string | undefined {\n if (jobExecution.steps.length === 0) return undefined;\n\n if (jobExecution.statusReason === 'output_invalid') {\n return jobExecution.statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;\n }\n if (jobExecution.statusReason === 'output_too_large') {\n return jobExecution.statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;\n }\n return undefined;\n}\n\nexport function MaterializedOutputFailureNotice({jobExecution}: {jobExecution: JobExecution}) {\n const description = outputFailureDescriptionForExecution(jobExecution);\n if (!description) return null;\n\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"border-b border-border-neutral-base px-row py-row\"\n >\n <CalloutContent>\n <CalloutTitle>Job output could not be persisted</CalloutTitle>\n <CalloutDescription>{description}</CalloutDescription>\n </CalloutContent>\n </Callout>\n );\n}\n\nexport function emptyStateForJob(\n job: Job,\n jobExecution: JobExecution,\n): StepListEmptyState | undefined {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n const displayStatus =\n job.mode === 'listening'\n ? deriveJobExecutionDisplayStatus(jobExecution)\n : deriveJobDisplayStatus(job);\n const runner = jobExecution.runner?.length ? jobExecution.runner : job.runner;\n\n if (jobExecution.status === 'running' && displayStatus === 'pending') {\n return {\n title: runner?.length ? 'Runner preparing job' : 'Waiting for a runner',\n description: runner?.length\n ? `Runner ${runner.join(', ')} is preparing the job. Steps will appear here when work begins.`\n : 'No runner has claimed this job yet. Steps will appear here when a runner starts work.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'Steps will appear here once the job starts.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'running') {\n return {\n title: 'Waiting for the first step',\n description: 'The first running step will appear here shortly.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before any step started.',\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'failed') {\n return {\n title: 'Job failed before its first step started',\n description: preStepFailureDescription(\n jobExecution.statusReason ?? job.statusReason,\n runner,\n jobExecution.statusReasonMessage,\n ),\n status: displayStatus,\n };\n }\n\n if (displayStatus === 'succeeded') {\n return {\n title: 'Completed without recorded steps',\n description: `Execution #${jobExecution.sequence} succeeded before any step output was recorded.`,\n status: displayStatus,\n };\n }\n\n return undefined;\n}\n\nexport function emptyStateForMissingExecution(job: Job): StepListEmptyState {\n if (job.carriedOver) {\n return {\n title: 'Carried over from a previous attempt',\n description: 'This job did not execute in this run.',\n status: 'succeeded',\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'listening') {\n return {\n title: 'Waiting for trigger events',\n description: 'Matching trigger events will create job executions here.',\n status: deriveJobDisplayStatus(job),\n };\n }\n\n if (job.mode === 'listening' && job.listenerStatus === 'resolved') {\n return {\n title: 'Listener resolved without executions',\n description: 'No matching trigger event created a job execution before the listener stopped.',\n status: job.status,\n };\n }\n\n if (job.status === 'pending') {\n return {\n title: 'Waiting for this job to start',\n description: 'No execution has been created for this job yet.',\n status: 'pending',\n };\n }\n\n if (job.status === 'skipped') {\n return {\n title: 'This job was skipped',\n description: skippedJobDescription(job.statusReason),\n status: 'skipped',\n };\n }\n\n if (job.status === 'cancelled') {\n return {\n title: 'Cancelled before start',\n description: 'This job was cancelled before an execution was created.',\n status: 'cancelled',\n };\n }\n\n if (job.status === 'failed') {\n return {\n title: 'Job failed before an execution was created',\n description: preStepFailureDescription(job.statusReason, job.runner),\n status: 'failed',\n };\n }\n\n return {\n title: 'Execution details unavailable',\n description: 'This job finished, but no job execution record is available.',\n status: job.status,\n };\n}\n\nexport function skippedJobDescription(reason: Job['statusReason']): string {\n switch (reason) {\n case 'dependency_not_completed':\n return 'A required job did not complete, so this job was skipped.';\n case 'default_gate_rejected':\n return 'A required job did not succeed, so this job was skipped.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so this job was skipped.';\n case 'condition_errored':\n return 'The job condition could not be evaluated, so this job was skipped.';\n case 'user_cancelled':\n case 'run_cancelled':\n case 'timed_out':\n case 'runner_lost':\n case 'output_invalid':\n case 'step_failed':\n case 'unknown':\n case null:\n return 'This job did not start.';\n case 'output_too_large':\n return 'The materialized job output exceeded its configured size limit.';\n }\n}\n\nfunction preStepFailureDescription(\n reason: string | null,\n runner: string[] | null = null,\n statusReasonMessage: string | null = null,\n): string {\n const runnerCopy = runner?.length ? ` Required runner labels: ${runner.join(', ')}.` : '';\n\n switch (reason) {\n case 'runner_lost':\n return `The runner stopped responding before work began.${runnerCopy} Check runner availability before re-running the workflow.`;\n case 'timed_out':\n return `The job timed out before work began.${runnerCopy} Check runner capacity and timeout configuration before re-running the workflow.`;\n case 'user_cancelled':\n case 'run_cancelled':\n return 'The execution ended while the run was being cancelled.';\n case 'step_failed':\n return `The execution failed before step details were recorded.${runnerCopy} Review run annotations before re-running the workflow.`;\n case 'output_too_large':\n return statusReasonMessage || OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION;\n case 'output_invalid':\n return statusReasonMessage || MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION;\n case 'condition_errored':\n return 'The job condition could not be evaluated. Review run annotations before re-running the workflow.';\n case 'dependency_not_completed':\n case 'default_gate_rejected':\n return 'A required job did not complete successfully. Resolve that failure before re-running the workflow.';\n case 'condition_false':\n case 'condition_rejected':\n return 'The job condition did not match, so no work started.';\n case 'unknown':\n case null:\n return `Shipfox did not record a failure reason.${runnerCopy} Re-run only after checking the runner and workflow configuration.`;\n default:\n return `The execution ended because ${humanizeFailureReason(reason)}.${runnerCopy} Resolve that condition before re-running the workflow.`;\n }\n}\n\nfunction humanizeFailureReason(reason: string): string {\n return reason.replaceAll('_', ' ').replaceAll('-', ' ');\n}\n\nexport function CarriedOverStepPanel() {\n return (\n <EmptyState\n className=\"rounded-8 border border-border-neutral-base bg-background-components-base\"\n icon=\"componentLine\"\n title=\"Carried over from a previous attempt\"\n description=\"Not executed in this run.\"\n variant=\"panel\"\n />\n );\n}\n\nexport function toSelectedAttemptError(\n step: Step,\n error: Record<string, unknown> | null,\n): StepError | null {\n if (error === null) return null;\n\n const rawReason = error.reason;\n const parsedReason =\n typeof rawReason === 'string' &&\n STEP_ERROR_REASONS.has(rawReason as StepError['reason'] & string)\n ? (rawReason as NonNullable<StepError['reason']>)\n : undefined;\n const rawAgentConfigIssue = error.agentConfigIssue ?? error.agent_config_issue;\n const agentConfigIssue =\n typeof rawAgentConfigIssue === 'string' &&\n AGENT_CONFIG_ISSUES.has(rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n ? (rawAgentConfigIssue as NonNullable<StepError['agentConfigIssue']>)\n : undefined;\n const exitCode = error.exitCode ?? error.exit_code;\n const resolvedReason = parsedReason\n ? parsedReason\n : agentConfigIssue\n ? 'agent_config_invalid'\n : undefined;\n\n if (resolvedReason === undefined) return null;\n\n return {\n message: typeof error.message === 'string' ? error.message : '',\n exitCode: exitCode === null || typeof exitCode === 'number' ? exitCode : null,\n signal: typeof error.signal === 'string' ? error.signal : undefined,\n reason: resolvedReason,\n agentConfigIssue,\n category: step.type === 'setup' || step.type === 'checkout' ? 'setup' : 'user',\n };\n}\n\nexport function isAgentConfigFailure(step: Step, error: StepError | null): boolean {\n return step.type === 'agent' && error?.reason === 'agent_config_invalid';\n}\n\nexport function jobSucceededSummary(job: Job, execution: JobExecution): string | undefined {\n if (job.carriedOver || execution.status !== 'succeeded') return undefined;\n const stepCount = execution.steps.filter((step) => step.status === 'succeeded').length;\n if (stepCount === 0) return undefined;\n const duration = execution.displayDuration;\n return `${stepCount} step${stepCount === 1 ? '' : 's'} succeeded${duration ? ` in ${formatJobExecutionTime(duration)}` : ''}`;\n}\n"],"names":["Callout","CalloutContent","CalloutDescription","CalloutTitle","EmptyState","AGENT_CONFIG_ISSUES","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","STEP_ERROR_REASONS","formatJobExecutionTime","MATERIALIZED_OUTPUT_FAILURE_DESCRIPTION","OUTPUT_TOO_LARGE_FAILURE_DESCRIPTION","outputFailureDescriptionForExecution","jobExecution","steps","length","undefined","statusReason","statusReasonMessage","MaterializedOutputFailureNotice","description","role","type","variant","className","emptyStateForJob","job","carriedOver","title","status","displayStatus","mode","runner","join","preStepFailureDescription","sequence","emptyStateForMissingExecution","listenerStatus","skippedJobDescription","reason","runnerCopy","humanizeFailureReason","replaceAll","CarriedOverStepPanel","icon","toSelectedAttemptError","step","error","rawReason","parsedReason","has","rawAgentConfigIssue","agentConfigIssue","agent_config_issue","exitCode","exit_code","resolvedReason","message","signal","category","isAgentConfigFailure","jobSucceededSummary","execution","stepCount","filter","duration","displayDuration"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"job-execution-time-text.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-execution-time-text.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAE5D,wBAAgB,oBAAoB,CAAC,EAAC,IAAI,EAAC,EAAE;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAC,UAIpE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAMrE"}
1
+ {"version":3,"file":"job-execution-time-text.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/job-execution-time-text.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC;AAG5D,wBAAgB,oBAAoB,CAAC,EAAC,IAAI,EAAC,EAAE;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAC,UAIpE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAErE"}
@@ -1,24 +1,11 @@
1
1
  import { useTimeTick } from '@shipfox/react-ui/time-ticker';
2
- import { formatDuration } from '@shipfox/react-ui/utils';
2
+ import { formatJobExecutionTimeLabel } from '../job-graph/job-duration-format.js';
3
3
  export function JobExecutionTimeText({ time }) {
4
4
  useTimeTick();
5
5
  return formatJobExecutionTime(time);
6
6
  }
7
7
  export function formatJobExecutionTime(time) {
8
- if (time.state === 'live') {
9
- return formatDuration(Date.now() - Date.parse(time.fromIso));
10
- }
11
- return formatElapsedDuration(time.elapsed);
12
- }
13
- function formatElapsedDuration(duration) {
14
- const days = duration.days ?? 0;
15
- const hours = duration.hours ?? 0;
16
- const minutes = duration.minutes ?? 0;
17
- const seconds = duration.seconds ?? 0;
18
- if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
19
- if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
20
- if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
21
- return `${seconds}s`;
8
+ return formatJobExecutionTimeLabel(time);
22
9
  }
23
10
 
24
11
  //# sourceMappingURL=job-execution-time-text.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-execution-time-text.tsx"],"sourcesContent":["import {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {formatDuration} from '@shipfox/react-ui/utils';\nimport type {JobExecutionTime} from '#core/workflow-run.js';\n\nexport function JobExecutionTimeText({time}: {time: JobExecutionTime}) {\n useTimeTick();\n\n return formatJobExecutionTime(time);\n}\n\nexport function formatJobExecutionTime(time: JobExecutionTime): string {\n if (time.state === 'live') {\n return formatDuration(Date.now() - Date.parse(time.fromIso));\n }\n\n return formatElapsedDuration(time.elapsed);\n}\n\nfunction formatElapsedDuration(duration: {\n days?: number | undefined;\n hours?: number | undefined;\n minutes?: number | undefined;\n seconds?: number | undefined;\n}): string {\n const days = duration.days ?? 0;\n const hours = duration.hours ?? 0;\n const minutes = duration.minutes ?? 0;\n const seconds = duration.seconds ?? 0;\n\n if (days > 0) return hours > 0 ? `${days}d ${hours}h` : `${days}d`;\n if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;\n if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n return `${seconds}s`;\n}\n"],"names":["useTimeTick","formatDuration","JobExecutionTimeText","time","formatJobExecutionTime","state","Date","now","parse","fromIso","formatElapsedDuration","elapsed","duration","days","hours","minutes","seconds"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,cAAc,QAAO,0BAA0B;AAGvD,OAAO,SAASC,qBAAqB,EAACC,IAAI,EAA2B;IACnEH;IAEA,OAAOI,uBAAuBD;AAChC;AAEA,OAAO,SAASC,uBAAuBD,IAAsB;IAC3D,IAAIA,KAAKE,KAAK,KAAK,QAAQ;QACzB,OAAOJ,eAAeK,KAAKC,GAAG,KAAKD,KAAKE,KAAK,CAACL,KAAKM,OAAO;IAC5D;IAEA,OAAOC,sBAAsBP,KAAKQ,OAAO;AAC3C;AAEA,SAASD,sBAAsBE,QAK9B;IACC,MAAMC,OAAOD,SAASC,IAAI,IAAI;IAC9B,MAAMC,QAAQF,SAASE,KAAK,IAAI;IAChC,MAAMC,UAAUH,SAASG,OAAO,IAAI;IACpC,MAAMC,UAAUJ,SAASI,OAAO,IAAI;IAEpC,IAAIH,OAAO,GAAG,OAAOC,QAAQ,IAAI,GAAGD,KAAK,EAAE,EAAEC,MAAM,CAAC,CAAC,GAAG,GAAGD,KAAK,CAAC,CAAC;IAClE,IAAIC,QAAQ,GAAG,OAAOC,UAAU,IAAI,GAAGD,MAAM,EAAE,EAAEC,QAAQ,CAAC,CAAC,GAAG,GAAGD,MAAM,CAAC,CAAC;IACzE,IAAIC,UAAU,GAAG,OAAOC,UAAU,IAAI,GAAGD,QAAQ,EAAE,EAAEC,QAAQ,CAAC,CAAC,GAAG,GAAGD,QAAQ,CAAC,CAAC;IAC/E,OAAO,GAAGC,QAAQ,CAAC,CAAC;AACtB"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-execution-time-text.tsx"],"sourcesContent":["import {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport type {JobExecutionTime} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from '../job-graph/job-duration-format.js';\n\nexport function JobExecutionTimeText({time}: {time: JobExecutionTime}) {\n useTimeTick();\n\n return formatJobExecutionTime(time);\n}\n\nexport function formatJobExecutionTime(time: JobExecutionTime): string {\n return formatJobExecutionTimeLabel(time);\n}\n"],"names":["useTimeTick","formatJobExecutionTimeLabel","JobExecutionTimeText","time","formatJobExecutionTime"],"mappings":"AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAE1D,SAAQC,2BAA2B,QAAO,sCAAsC;AAEhF,OAAO,SAASC,qBAAqB,EAACC,IAAI,EAA2B;IACnEH;IAEA,OAAOI,uBAAuBD;AAChC;AAEA,OAAO,SAASC,uBAAuBD,IAAsB;IAC3D,OAAOF,4BAA4BE;AACrC"}
@@ -2,7 +2,7 @@ import { humanDuration } from '@shipfox/react-ui/utils';
2
2
  export function formatJobExecutionTimeLabel(time) {
3
3
  switch(time.state){
4
4
  case 'live':
5
- return humanDuration(time.fromIso);
5
+ return humanDuration(time.fromIso) || '0s';
6
6
  case 'fixed':
7
7
  return formatFixedDurationLabel(time.elapsed);
8
8
  default:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-graph/job-duration-format.ts"],"sourcesContent":["import {humanDuration} from '@shipfox/react-ui/utils';\nimport type {\n JobDisplayDuration,\n JobExecutionDisplayStatus,\n JobExecutionTime,\n} from '#core/workflow-run.js';\n\nexport function formatJobExecutionTimeLabel(time: JobExecutionTime): string {\n switch (time.state) {\n case 'live':\n return humanDuration(time.fromIso);\n case 'fixed':\n return formatFixedDurationLabel(time.elapsed);\n default: {\n const exhaustive: never = time;\n return exhaustive;\n }\n }\n}\n\nexport function formatJobDurationAccessibleLabel(\n duration: JobDisplayDuration | null,\n displayStatus?: JobExecutionDisplayStatus,\n): string | undefined {\n if (duration === null) return undefined;\n\n const label = formatJobExecutionTimeLabel(duration);\n switch (duration.kind) {\n case 'queue':\n return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;\n case 'run':\n if (duration.state !== 'live') return `ran ${label}`;\n if (displayStatus === 'running') return `running ${label}`;\n return displayStatus === 'pending'\n ? `runner assigned ${label}`\n : `execution elapsed ${label}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction formatFixedDurationLabel({\n years = 0,\n months = 0,\n weeks = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n}: Extract<JobExecutionTime, {state: 'fixed'}>['elapsed']): string {\n const totalDays = years * 365 + months * 30 + weeks * 7 + days;\n const totalHours = totalDays * 24 + hours;\n\n if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, '0');\n}\n"],"names":["humanDuration","formatJobExecutionTimeLabel","time","state","fromIso","formatFixedDurationLabel","elapsed","exhaustive","formatJobDurationAccessibleLabel","duration","displayStatus","undefined","label","kind","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":"AAAA,SAAQA,aAAa,QAAO,0BAA0B;AAOtD,OAAO,SAASC,4BAA4BC,IAAsB;IAChE,OAAQA,KAAKC,KAAK;QAChB,KAAK;YACH,OAAOH,cAAcE,KAAKE,OAAO;QACnC,KAAK;YACH,OAAOC,yBAAyBH,KAAKI,OAAO;QAC9C;YAAS;gBACP,MAAMC,aAAoBL;gBAC1B,OAAOK;YACT;IACF;AACF;AAEA,OAAO,SAASC,iCACdC,QAAmC,EACnCC,aAAyC;IAEzC,IAAID,aAAa,MAAM,OAAOE;IAE9B,MAAMC,QAAQX,4BAA4BQ;IAC1C,OAAQA,SAASI,IAAI;QACnB,KAAK;YACH,OAAOJ,SAASN,KAAK,KAAK,SAAS,CAAC,SAAS,EAAES,OAAO,GAAG,CAAC,OAAO,EAAEA,OAAO;QAC5E,KAAK;YACH,IAAIH,SAASN,KAAK,KAAK,QAAQ,OAAO,CAAC,IAAI,EAAES,OAAO;YACpD,IAAIF,kBAAkB,WAAW,OAAO,CAAC,QAAQ,EAAEE,OAAO;YAC1D,OAAOF,kBAAkB,YACrB,CAAC,gBAAgB,EAAEE,OAAO,GAC1B,CAAC,kBAAkB,EAAEA,OAAO;QAClC;YAAS;gBACP,MAAML,aAAoBE;gBAC1B,OAAOF;YACT;IACF;AACF;AAEA,SAASF,yBAAyB,EAChCS,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EAC4C;IACvD,MAAMC,YAAYP,QAAQ,MAAMC,SAAS,KAAKC,QAAQ,IAAIC;IAC1D,MAAMK,aAAaD,YAAY,KAAKH;IAEpC,IAAII,aAAa,GAAG,OAAO,GAAGA,WAAW,EAAE,EAAEC,KAAKJ,SAAS,CAAC,CAAC;IAC7D,IAAIA,UAAU,GAAG,OAAO,GAAGA,QAAQ,EAAE,EAAEI,KAAKH,SAAS,CAAC,CAAC;IACvD,OAAO,GAAGA,QAAQ,CAAC,CAAC;AACtB;AAEA,SAASG,KAAKC,KAAa;IACzB,OAAOA,MAAMC,QAAQ,GAAGC,QAAQ,CAAC,GAAG;AACtC"}
1
+ {"version":3,"sources":["../../../src/components/job-graph/job-duration-format.ts"],"sourcesContent":["import {humanDuration} from '@shipfox/react-ui/utils';\nimport type {\n JobDisplayDuration,\n JobExecutionDisplayStatus,\n JobExecutionTime,\n} from '#core/workflow-run.js';\n\nexport function formatJobExecutionTimeLabel(time: JobExecutionTime): string {\n switch (time.state) {\n case 'live':\n return humanDuration(time.fromIso) || '0s';\n case 'fixed':\n return formatFixedDurationLabel(time.elapsed);\n default: {\n const exhaustive: never = time;\n return exhaustive;\n }\n }\n}\n\nexport function formatJobDurationAccessibleLabel(\n duration: JobDisplayDuration | null,\n displayStatus?: JobExecutionDisplayStatus,\n): string | undefined {\n if (duration === null) return undefined;\n\n const label = formatJobExecutionTimeLabel(duration);\n switch (duration.kind) {\n case 'queue':\n return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;\n case 'run':\n if (duration.state !== 'live') return `ran ${label}`;\n if (displayStatus === 'running') return `running ${label}`;\n return displayStatus === 'pending'\n ? `runner assigned ${label}`\n : `execution elapsed ${label}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction formatFixedDurationLabel({\n years = 0,\n months = 0,\n weeks = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n}: Extract<JobExecutionTime, {state: 'fixed'}>['elapsed']): string {\n const totalDays = years * 365 + months * 30 + weeks * 7 + days;\n const totalHours = totalDays * 24 + hours;\n\n if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, '0');\n}\n"],"names":["humanDuration","formatJobExecutionTimeLabel","time","state","fromIso","formatFixedDurationLabel","elapsed","exhaustive","formatJobDurationAccessibleLabel","duration","displayStatus","undefined","label","kind","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":"AAAA,SAAQA,aAAa,QAAO,0BAA0B;AAOtD,OAAO,SAASC,4BAA4BC,IAAsB;IAChE,OAAQA,KAAKC,KAAK;QAChB,KAAK;YACH,OAAOH,cAAcE,KAAKE,OAAO,KAAK;QACxC,KAAK;YACH,OAAOC,yBAAyBH,KAAKI,OAAO;QAC9C;YAAS;gBACP,MAAMC,aAAoBL;gBAC1B,OAAOK;YACT;IACF;AACF;AAEA,OAAO,SAASC,iCACdC,QAAmC,EACnCC,aAAyC;IAEzC,IAAID,aAAa,MAAM,OAAOE;IAE9B,MAAMC,QAAQX,4BAA4BQ;IAC1C,OAAQA,SAASI,IAAI;QACnB,KAAK;YACH,OAAOJ,SAASN,KAAK,KAAK,SAAS,CAAC,SAAS,EAAES,OAAO,GAAG,CAAC,OAAO,EAAEA,OAAO;QAC5E,KAAK;YACH,IAAIH,SAASN,KAAK,KAAK,QAAQ,OAAO,CAAC,IAAI,EAAES,OAAO;YACpD,IAAIF,kBAAkB,WAAW,OAAO,CAAC,QAAQ,EAAEE,OAAO;YAC1D,OAAOF,kBAAkB,YACrB,CAAC,gBAAgB,EAAEE,OAAO,GAC1B,CAAC,kBAAkB,EAAEA,OAAO;QAClC;YAAS;gBACP,MAAML,aAAoBE;gBAC1B,OAAOF;YACT;IACF;AACF;AAEA,SAASF,yBAAyB,EAChCS,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EAC4C;IACvD,MAAMC,YAAYP,QAAQ,MAAMC,SAAS,KAAKC,QAAQ,IAAIC;IAC1D,MAAMK,aAAaD,YAAY,KAAKH;IAEpC,IAAII,aAAa,GAAG,OAAO,GAAGA,WAAW,EAAE,EAAEC,KAAKJ,SAAS,CAAC,CAAC;IAC7D,IAAIA,UAAU,GAAG,OAAO,GAAGA,QAAQ,EAAE,EAAEI,KAAKH,SAAS,CAAC,CAAC;IACvD,OAAO,GAAGA,QAAQ,CAAC,CAAC;AACtB;AAEA,SAASG,KAAKC,KAAa;IACzB,OAAOA,MAAMC,QAAQ,GAAGC,QAAQ,CAAC,GAAG;AACtC"}
@@ -16,11 +16,11 @@ export function JobGraphContent({ model, trigger, selectedJobId, onSelectJob })
16
16
  const [hoveredJobId, setHoveredJobId] = useState();
17
17
  if (model.nodes.length === 0) {
18
18
  return /*#__PURE__*/ _jsx(EmptyState, {
19
- className: "min-h-160 p-panel",
19
+ className: "min-h-160",
20
20
  icon: "componentLine",
21
21
  title: "No jobs yet",
22
22
  description: "This run has not materialized jobs.",
23
- variant: "compact"
23
+ variant: "panel"
24
24
  });
25
25
  }
26
26
  const maxRows = Math.max(1, ...model.columns.map((column)=>column.length));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-graph/job-graph-content.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEvent} from 'react';\nimport {useRef, useState} from 'react';\nimport type {WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphModel, JobGraphNavigationKey} from './graph-model.js';\nimport {nextJobGraphNodeId} from './graph-model.js';\nimport {JobNode, TriggerNode} from './job-node.js';\nimport type {JobGraphSelectionSource} from './types.js';\n\nconst NODE_WIDTH = 208;\nconst NODE_HEIGHT = 48;\nconst COLUMN_GAP = 64;\n/** Fixed graph spacing shared by the DOM rows and the edge geometry below. */\nconst ROW_GAP = 20;\nconst TRIGGER_WIDTH = 36;\nconst PADDING = 16;\n\nexport function JobGraphContent({\n model,\n trigger,\n selectedJobId,\n onSelectJob,\n}: {\n model: JobGraphModel;\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n selectedJobId?: string | undefined;\n onSelectJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n}) {\n const nodeRefs = useRef(new Map<string, HTMLButtonElement>());\n const [hoveredJobId, setHoveredJobId] = useState<string | undefined>();\n\n if (model.nodes.length === 0) {\n return (\n <EmptyState\n className=\"min-h-160 p-panel\"\n icon=\"componentLine\"\n title=\"No jobs yet\"\n description=\"This run has not materialized jobs.\"\n variant=\"compact\"\n />\n );\n }\n\n const maxRows = Math.max(1, ...model.columns.map((column) => column.length));\n const contentWidth =\n PADDING * 2 +\n TRIGGER_WIDTH +\n COLUMN_GAP +\n model.columns.length * NODE_WIDTH +\n Math.max(0, model.columns.length - 1) * COLUMN_GAP;\n const contentHeight = PADDING * 2 + maxRows * NODE_HEIGHT + Math.max(0, maxRows - 1) * ROW_GAP;\n\n function setNodeRef(jobId: string) {\n return (element: HTMLButtonElement | null) => {\n if (element) {\n nodeRefs.current.set(jobId, element);\n } else {\n nodeRefs.current.delete(jobId);\n }\n };\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n const key = navigationKey(event.key);\n if (!key) return;\n\n const currentNodeId = event.currentTarget.dataset.jobId;\n if (!currentNodeId) return;\n\n const nextNodeId = nextJobGraphNodeId({model, currentNodeId, key});\n if (!nextNodeId) return;\n\n event.preventDefault();\n onSelectJob(nextNodeId, 'keyboard');\n nodeRefs.current.get(nextNodeId)?.focus();\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div className=\"min-h-0 overflow-auto bg-transparent\">\n <div className=\"relative\" style={{width: contentWidth, minHeight: contentHeight}}>\n <GraphEdges model={model} hoveredJobId={hoveredJobId} />\n <div\n className=\"absolute\"\n style={{left: PADDING, top: PADDING + (NODE_HEIGHT - TRIGGER_WIDTH) / 2}}\n >\n <TriggerNode trigger={trigger} />\n </div>\n {model.columns.map((column, columnIndex) => (\n <div\n key={column.map((node) => node.id).join(':')}\n className=\"absolute flex flex-col\"\n style={{left: jobLeft(columnIndex), top: PADDING, rowGap: ROW_GAP}}\n >\n {column.map((node) => (\n <JobNode\n key={node.id}\n node={node}\n selected={node.id === selectedJobId}\n ref={setNodeRef(node.id)}\n onSelect={() =>\n onSelectJob(node.id === selectedJobId ? undefined : node.id, 'pointer')\n }\n onKeyDown={handleKeyDown}\n onHoverStart={() => setHoveredJobId(node.id)}\n onHoverEnd={() =>\n setHoveredJobId((current) => (current === node.id ? undefined : current))\n }\n />\n ))}\n </div>\n ))}\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction navigationKey(key: string): JobGraphNavigationKey | undefined {\n if (\n key === 'ArrowRight' ||\n key === 'ArrowLeft' ||\n key === 'ArrowDown' ||\n key === 'ArrowUp' ||\n key === 'j' ||\n key === 'k'\n ) {\n return key;\n }\n return undefined;\n}\n\nfunction GraphEdges({\n model,\n hoveredJobId,\n}: {\n model: JobGraphModel;\n hoveredJobId?: string | undefined;\n}) {\n const byId = new Map(model.nodes.map((node) => [node.id, node]));\n const edgeViews = model.edges\n .map((edge) => {\n const fromNode = byId.get(edge.from);\n const toNode = byId.get(edge.to);\n const from = edge.from === 'trigger' ? triggerPoint() : jobRightPoint(fromNode);\n const to = jobLeftPoint(toNode);\n if (!from || !to) return undefined;\n\n return {\n id: edge.id,\n highlighted: edge.from === hoveredJobId || edge.to === hoveredJobId,\n path: edgePath({from, fromNode, to, toNode}),\n };\n })\n .filter((edgeView): edgeView is NonNullable<typeof edgeView> => edgeView !== undefined)\n .sort((left, right) => Number(left.highlighted) - Number(right.highlighted));\n\n return (\n <svg className=\"pointer-events-none absolute inset-0 size-full\" aria-hidden=\"true\">\n {edgeViews.map((edge) => {\n return (\n <g\n key={edge.id}\n className={cn(\n 'text-foreground-neutral-muted transition-colors',\n edge.highlighted && 'text-foreground-neutral-base',\n )}\n >\n <path\n data-edge-id={edge.id}\n d={edge.path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={edge.highlighted ? 1.5 : 1}\n />\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction edgePath({\n from,\n fromNode,\n to,\n toNode,\n}: {\n from: {x: number; y: number};\n fromNode: {column: number} | undefined;\n to: {x: number; y: number};\n toNode: {column: number} | undefined;\n}) {\n if (fromNode && toNode && toNode.column - fromNode.column > 1 && from.y !== to.y) {\n const targetLaneX = to.x - COLUMN_GAP / 2;\n return `M ${from.x} ${from.y} H ${targetLaneX} V ${to.y} H ${to.x}`;\n }\n\n const midX = from.x + (to.x - from.x) / 2;\n return `M ${from.x} ${from.y} H ${midX} V ${to.y} H ${to.x}`;\n}\n\nfunction triggerPoint() {\n return {x: PADDING + TRIGGER_WIDTH, y: PADDING + NODE_HEIGHT / 2};\n}\n\nfunction jobLeft(column: number): number {\n // Dependency depths are contiguous, so a node's depth matches its rendered column index.\n return PADDING + TRIGGER_WIDTH + COLUMN_GAP + column * (NODE_WIDTH + COLUMN_GAP);\n}\n\nfunction jobTop(row: number): number {\n return PADDING + row * (NODE_HEIGHT + ROW_GAP);\n}\n\nfunction jobLeftPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column), y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n\nfunction jobRightPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column) + NODE_WIDTH, y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n"],"names":["EmptyState","TimeTickerProvider","cn","useRef","useState","nextJobGraphNodeId","JobNode","TriggerNode","NODE_WIDTH","NODE_HEIGHT","COLUMN_GAP","ROW_GAP","TRIGGER_WIDTH","PADDING","JobGraphContent","model","trigger","selectedJobId","onSelectJob","nodeRefs","Map","hoveredJobId","setHoveredJobId","nodes","length","className","icon","title","description","variant","maxRows","Math","max","columns","map","column","contentWidth","contentHeight","setNodeRef","jobId","element","current","set","delete","handleKeyDown","event","key","navigationKey","currentNodeId","currentTarget","dataset","nextNodeId","preventDefault","get","focus","intervalMs","reducedMotionIntervalMs","div","style","width","minHeight","GraphEdges","left","top","columnIndex","jobLeft","rowGap","node","selected","id","ref","onSelect","undefined","onKeyDown","onHoverStart","onHoverEnd","join","byId","edgeViews","edges","edge","fromNode","from","toNode","to","triggerPoint","jobRightPoint","jobLeftPoint","highlighted","path","edgePath","filter","edgeView","sort","right","Number","svg","aria-hidden","g","data-edge-id","d","fill","stroke","strokeWidth","y","targetLaneX","x","midX","jobTop","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-graph/job-graph-content.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEvent} from 'react';\nimport {useRef, useState} from 'react';\nimport type {WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphModel, JobGraphNavigationKey} from './graph-model.js';\nimport {nextJobGraphNodeId} from './graph-model.js';\nimport {JobNode, TriggerNode} from './job-node.js';\nimport type {JobGraphSelectionSource} from './types.js';\n\nconst NODE_WIDTH = 208;\nconst NODE_HEIGHT = 48;\nconst COLUMN_GAP = 64;\n/** Fixed graph spacing shared by the DOM rows and the edge geometry below. */\nconst ROW_GAP = 20;\nconst TRIGGER_WIDTH = 36;\nconst PADDING = 16;\n\nexport function JobGraphContent({\n model,\n trigger,\n selectedJobId,\n onSelectJob,\n}: {\n model: JobGraphModel;\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n selectedJobId?: string | undefined;\n onSelectJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n}) {\n const nodeRefs = useRef(new Map<string, HTMLButtonElement>());\n const [hoveredJobId, setHoveredJobId] = useState<string | undefined>();\n\n if (model.nodes.length === 0) {\n return (\n <EmptyState\n className=\"min-h-160\"\n icon=\"componentLine\"\n title=\"No jobs yet\"\n description=\"This run has not materialized jobs.\"\n variant=\"panel\"\n />\n );\n }\n\n const maxRows = Math.max(1, ...model.columns.map((column) => column.length));\n const contentWidth =\n PADDING * 2 +\n TRIGGER_WIDTH +\n COLUMN_GAP +\n model.columns.length * NODE_WIDTH +\n Math.max(0, model.columns.length - 1) * COLUMN_GAP;\n const contentHeight = PADDING * 2 + maxRows * NODE_HEIGHT + Math.max(0, maxRows - 1) * ROW_GAP;\n\n function setNodeRef(jobId: string) {\n return (element: HTMLButtonElement | null) => {\n if (element) {\n nodeRefs.current.set(jobId, element);\n } else {\n nodeRefs.current.delete(jobId);\n }\n };\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n const key = navigationKey(event.key);\n if (!key) return;\n\n const currentNodeId = event.currentTarget.dataset.jobId;\n if (!currentNodeId) return;\n\n const nextNodeId = nextJobGraphNodeId({model, currentNodeId, key});\n if (!nextNodeId) return;\n\n event.preventDefault();\n onSelectJob(nextNodeId, 'keyboard');\n nodeRefs.current.get(nextNodeId)?.focus();\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div className=\"min-h-0 overflow-auto bg-transparent\">\n <div className=\"relative\" style={{width: contentWidth, minHeight: contentHeight}}>\n <GraphEdges model={model} hoveredJobId={hoveredJobId} />\n <div\n className=\"absolute\"\n style={{left: PADDING, top: PADDING + (NODE_HEIGHT - TRIGGER_WIDTH) / 2}}\n >\n <TriggerNode trigger={trigger} />\n </div>\n {model.columns.map((column, columnIndex) => (\n <div\n key={column.map((node) => node.id).join(':')}\n className=\"absolute flex flex-col\"\n style={{left: jobLeft(columnIndex), top: PADDING, rowGap: ROW_GAP}}\n >\n {column.map((node) => (\n <JobNode\n key={node.id}\n node={node}\n selected={node.id === selectedJobId}\n ref={setNodeRef(node.id)}\n onSelect={() =>\n onSelectJob(node.id === selectedJobId ? undefined : node.id, 'pointer')\n }\n onKeyDown={handleKeyDown}\n onHoverStart={() => setHoveredJobId(node.id)}\n onHoverEnd={() =>\n setHoveredJobId((current) => (current === node.id ? undefined : current))\n }\n />\n ))}\n </div>\n ))}\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction navigationKey(key: string): JobGraphNavigationKey | undefined {\n if (\n key === 'ArrowRight' ||\n key === 'ArrowLeft' ||\n key === 'ArrowDown' ||\n key === 'ArrowUp' ||\n key === 'j' ||\n key === 'k'\n ) {\n return key;\n }\n return undefined;\n}\n\nfunction GraphEdges({\n model,\n hoveredJobId,\n}: {\n model: JobGraphModel;\n hoveredJobId?: string | undefined;\n}) {\n const byId = new Map(model.nodes.map((node) => [node.id, node]));\n const edgeViews = model.edges\n .map((edge) => {\n const fromNode = byId.get(edge.from);\n const toNode = byId.get(edge.to);\n const from = edge.from === 'trigger' ? triggerPoint() : jobRightPoint(fromNode);\n const to = jobLeftPoint(toNode);\n if (!from || !to) return undefined;\n\n return {\n id: edge.id,\n highlighted: edge.from === hoveredJobId || edge.to === hoveredJobId,\n path: edgePath({from, fromNode, to, toNode}),\n };\n })\n .filter((edgeView): edgeView is NonNullable<typeof edgeView> => edgeView !== undefined)\n .sort((left, right) => Number(left.highlighted) - Number(right.highlighted));\n\n return (\n <svg className=\"pointer-events-none absolute inset-0 size-full\" aria-hidden=\"true\">\n {edgeViews.map((edge) => {\n return (\n <g\n key={edge.id}\n className={cn(\n 'text-foreground-neutral-muted transition-colors',\n edge.highlighted && 'text-foreground-neutral-base',\n )}\n >\n <path\n data-edge-id={edge.id}\n d={edge.path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={edge.highlighted ? 1.5 : 1}\n />\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction edgePath({\n from,\n fromNode,\n to,\n toNode,\n}: {\n from: {x: number; y: number};\n fromNode: {column: number} | undefined;\n to: {x: number; y: number};\n toNode: {column: number} | undefined;\n}) {\n if (fromNode && toNode && toNode.column - fromNode.column > 1 && from.y !== to.y) {\n const targetLaneX = to.x - COLUMN_GAP / 2;\n return `M ${from.x} ${from.y} H ${targetLaneX} V ${to.y} H ${to.x}`;\n }\n\n const midX = from.x + (to.x - from.x) / 2;\n return `M ${from.x} ${from.y} H ${midX} V ${to.y} H ${to.x}`;\n}\n\nfunction triggerPoint() {\n return {x: PADDING + TRIGGER_WIDTH, y: PADDING + NODE_HEIGHT / 2};\n}\n\nfunction jobLeft(column: number): number {\n // Dependency depths are contiguous, so a node's depth matches its rendered column index.\n return PADDING + TRIGGER_WIDTH + COLUMN_GAP + column * (NODE_WIDTH + COLUMN_GAP);\n}\n\nfunction jobTop(row: number): number {\n return PADDING + row * (NODE_HEIGHT + ROW_GAP);\n}\n\nfunction jobLeftPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column), y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n\nfunction jobRightPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column) + NODE_WIDTH, y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n"],"names":["EmptyState","TimeTickerProvider","cn","useRef","useState","nextJobGraphNodeId","JobNode","TriggerNode","NODE_WIDTH","NODE_HEIGHT","COLUMN_GAP","ROW_GAP","TRIGGER_WIDTH","PADDING","JobGraphContent","model","trigger","selectedJobId","onSelectJob","nodeRefs","Map","hoveredJobId","setHoveredJobId","nodes","length","className","icon","title","description","variant","maxRows","Math","max","columns","map","column","contentWidth","contentHeight","setNodeRef","jobId","element","current","set","delete","handleKeyDown","event","key","navigationKey","currentNodeId","currentTarget","dataset","nextNodeId","preventDefault","get","focus","intervalMs","reducedMotionIntervalMs","div","style","width","minHeight","GraphEdges","left","top","columnIndex","jobLeft","rowGap","node","selected","id","ref","onSelect","undefined","onKeyDown","onHoverStart","onHoverEnd","join","byId","edgeViews","edges","edge","fromNode","from","toNode","to","triggerPoint","jobRightPoint","jobLeftPoint","highlighted","path","edgePath","filter","edgeView","sort","right","Number","svg","aria-hidden","g","data-edge-id","d","fill","stroke","strokeWidth","y","targetLaneX","x","midX","jobTop","row"],"mappings":"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"}
@@ -202,11 +202,10 @@ function StepListEmptyStateView({ emptyState = {
202
202
  } }) {
203
203
  if (!emptyState.status) {
204
204
  return /*#__PURE__*/ _jsx(EmptyState, {
205
- className: "min-h-120 p-panel",
206
205
  icon: "componentLine",
207
206
  title: emptyState.title,
208
207
  description: emptyState.description,
209
- variant: "compact"
208
+ variant: "panel"
210
209
  });
211
210
  }
212
211
  return /*#__PURE__*/ _jsxs("div", {