@shipfox/client-workflows 20.0.0 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +51 -0
  3. package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
  4. package/dist/components/job-detail/job-detail-view.js +232 -44
  5. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  6. package/dist/components/job-detail/job-empty-states.js +2 -2
  7. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  8. package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
  9. package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
  10. package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
  11. package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
  12. package/dist/components/job-graph/job-graph-content.js +2 -2
  13. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  14. package/dist/components/step-list/step-list-model.d.ts +1 -0
  15. package/dist/components/step-list/step-list-model.d.ts.map +1 -1
  16. package/dist/components/step-list/step-list-model.js +1 -1
  17. package/dist/components/step-list/step-list-model.js.map +1 -1
  18. package/dist/components/step-list/step-list.d.ts +2 -1
  19. package/dist/components/step-list/step-list.d.ts.map +1 -1
  20. package/dist/components/step-list/step-list.js +25 -9
  21. package/dist/components/step-list/step-list.js.map +1 -1
  22. package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
  23. package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
  24. package/dist/components/workflow-run-list/job-status-strip.js +22 -11
  25. package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
  26. package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
  27. package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
  28. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  29. package/dist/components/workflow-run-list/workflow-run-list-content.js +13 -7
  30. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  31. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  32. package/dist/components/workflow-run-list/workflow-run-list-states.js +43 -38
  33. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  34. package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
  35. package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
  36. package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
  37. package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
  38. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  39. package/dist/components/workflow-run-list/workflow-run-row.js +25 -18
  40. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  41. package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
  42. package/dist/components/workflow-run-summary/workflow-run-summary.js +4 -4
  43. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  44. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  45. package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
  46. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  47. package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
  48. package/dist/components/workflow-run-view/workflow-run-states.js +1 -1
  49. package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
  50. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  51. package/dist/components/workflow-run-view/workflow-run-view.js +103 -78
  52. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  53. package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
  54. package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
  55. package/dist/pages/project-workflows-page.d.ts.map +1 -1
  56. package/dist/pages/project-workflows-page.js +171 -158
  57. package/dist/pages/project-workflows-page.js.map +1 -1
  58. package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
  59. package/dist/pages/workflow-job-detail-page.js +3 -2
  60. package/dist/pages/workflow-job-detail-page.js.map +1 -1
  61. package/dist/pages/workflow-run-detail-page.js +2 -1
  62. package/dist/pages/workflow-run-detail-page.js.map +1 -1
  63. package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
  64. package/dist/pages/workflow-run-list-page.js +9 -11
  65. package/dist/pages/workflow-run-list-page.js.map +1 -1
  66. package/dist/routes/job-detail.d.ts +1 -1
  67. package/dist/routes/job-detail.js +1 -1
  68. package/dist/routes/job-detail.js.map +1 -1
  69. package/dist/routes/run-detail.d.ts +1 -1
  70. package/dist/routes/run-detail.js +1 -1
  71. package/dist/routes/run-detail.js.map +1 -1
  72. package/dist/routes/runs.d.ts +1 -1
  73. package/dist/routes/runs.js +1 -1
  74. package/dist/routes/runs.js.map +1 -1
  75. package/dist/routes/workflows.d.ts +3 -0
  76. package/dist/routes/workflows.js +3 -0
  77. package/dist/routes/workflows.js.map +1 -1
  78. package/dist/tsconfig.test.tsbuildinfo +1 -1
  79. package/package.json +8 -8
  80. package/src/components/job-detail/job-detail-view.tsx +264 -49
  81. package/src/components/job-detail/job-empty-states.tsx +2 -2
  82. package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
  83. package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
  84. package/src/components/job-graph/job-graph-content.tsx +2 -2
  85. package/src/components/job-graph/job-graph.stories.tsx +1 -1
  86. package/src/components/job-graph/job-node.stories.tsx +1 -1
  87. package/src/components/step-list/step-list-model.ts +1 -1
  88. package/src/components/step-list/step-list.test.tsx +29 -1
  89. package/src/components/step-list/step-list.tsx +29 -11
  90. package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
  91. package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
  92. package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
  93. package/src/components/workflow-run-list/workflow-run-list-states.tsx +42 -41
  94. package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +53 -4
  95. package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
  96. package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
  97. package/src/components/workflow-run-list/workflow-run-row.tsx +26 -19
  98. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +3 -3
  99. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +11 -2
  100. package/src/components/workflow-run-summary/workflow-run-summary.tsx +4 -7
  101. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
  102. package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
  103. package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
  104. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +142 -0
  105. package/src/components/workflow-run-view/workflow-run-view.test.tsx +42 -8
  106. package/src/components/workflow-run-view/workflow-run-view.tsx +112 -96
  107. package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
  108. package/src/pages/project-workflows-page.tsx +135 -125
  109. package/src/pages/workflow-job-detail-page.test.tsx +32 -0
  110. package/src/pages/workflow-job-detail-page.tsx +5 -1
  111. package/src/pages/workflow-run-detail-page.tsx +1 -1
  112. package/src/pages/workflow-run-list-page.tsx +9 -11
  113. package/src/pages/workflow-run-pages.test.tsx +26 -0
  114. package/src/routes/job-detail.tsx +1 -1
  115. package/src/routes/run-detail.tsx +1 -1
  116. package/src/routes/runs.tsx +1 -1
  117. package/src/routes/workflows.tsx +1 -0
  118. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={(context) => (\n <ExpandedStep context={context} pageScrollRef={pageScrollRef} />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-none border-0 bg-transparent shadow-none\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","MaterializedOutputFailureNotice","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Panel, PanelActions, PanelBody, PanelHeader} from '@shipfox/react-ui/panel';\nimport {SearchInline} from '@shipfox/react-ui/search';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useCallback, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n buildStepListModel,\n getStepStatusVisual,\n type StepListModel,\n} from '../step-list/step-list-model.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const [logSearch, setLogSearch] = useState('');\n const [wrapLogs, setWrapLogs] = useState(false);\n const [showLineNumbers, setShowLineNumbers] = useState(true);\n const [expandedLogAttemptIds, setExpandedLogAttemptIds] = useState<readonly string[]>([]);\n const [logRefreshTokens, setLogRefreshTokens] = useState<Record<string, number>>({});\n const [logFetchingByAttemptId, setLogFetchingByAttemptId] = useState<Record<string, boolean>>({});\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n const handleLogFetchingChange = useCallback((attemptId: string, isFetching: boolean) => {\n setLogFetchingByAttemptId((current) => {\n if (current[attemptId] === isFetching) return current;\n return {...current, [attemptId]: isFetching};\n });\n }, []);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const stepListModel = buildStepListModel({job, jobExecution: selectedJobExecution});\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution, stepListModel);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const expandedLogSelection = findExpandedLogSelection(\n selectedJobExecution,\n expandedLogAttemptIds,\n stepListModel,\n );\n const selectedLogStep = expandedLogSelection?.step;\n const selectedLogAttempt = expandedLogSelection?.attempt;\n const selectedLogStatus = selectedLogAttempt?.status ?? selectedLogStep?.status;\n const logIsFetching = Boolean(\n selectedLogAttempt && logFetchingByAttemptId[selectedLogAttempt.id],\n );\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n function refreshLogs() {\n if (!selectedLogAttempt) return;\n setLogRefreshTokens((current) => ({\n ...current,\n [selectedLogAttempt.id]: (current[selectedLogAttempt.id] ?? 0) + 1,\n }));\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Panel data-job-log-panel className=\"min-w-0\">\n <JobLogPanelHeader\n stepLabel={expandedLogSelection?.stepLabel}\n attempt={selectedLogAttempt?.attempt}\n status={selectedLogStatus}\n search={logSearch}\n onSearchChange={setLogSearch}\n onRefresh={refreshLogs}\n refreshing={logIsFetching}\n showLineNumbers={showLineNumbers}\n onShowLineNumbersChange={setShowLineNumbers}\n wrap={wrapLogs}\n onWrapChange={setWrapLogs}\n disabled={!selectedLogAttempt}\n />\n <PanelBody className=\"min-w-0 p-0\">\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n onExpandedAttemptIdsChange={setExpandedLogAttemptIds}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent shadow-none\"\n renderExpandedStep={(context) => (\n <ExpandedStep\n context={context}\n pageScrollRef={pageScrollRef}\n search={logSearch}\n wrap={wrapLogs}\n showLineNumbers={showLineNumbers}\n attemptId={context.attemptId}\n refreshToken={logRefreshTokens[context.attemptId] ?? 0}\n onFetchingChange={handleLogFetchingChange}\n />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </PanelBody>\n </Panel>\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n search,\n wrap,\n showLineNumbers,\n attemptId,\n refreshToken,\n onFetchingChange,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n search: string;\n wrap: boolean;\n showLineNumbers: boolean;\n attemptId: string;\n refreshToken: number;\n onFetchingChange: (attemptId: string, isFetching: boolean) => void;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-contrast-base outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n search={search}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n attemptId={attemptId}\n refreshToken={refreshToken}\n onFetchingChange={onFetchingChange}\n />\n </section>\n );\n}\n\nfunction JobLogPanelHeader({\n stepLabel,\n attempt,\n status,\n search,\n onSearchChange,\n onRefresh,\n refreshing,\n showLineNumbers,\n onShowLineNumbersChange,\n wrap,\n onWrapChange,\n disabled,\n}: {\n stepLabel: string | undefined;\n attempt: number | undefined;\n status: string | undefined;\n search: string;\n onSearchChange: (value: string) => void;\n onRefresh: () => void;\n refreshing: boolean;\n showLineNumbers: boolean;\n onShowLineNumbersChange: (value: boolean) => void;\n wrap: boolean;\n onWrapChange: (value: boolean) => void;\n disabled: boolean;\n}) {\n const statusVisual = status ? getStepStatusVisual(status) : undefined;\n\n return (\n <PanelHeader className=\"flex flex-wrap items-center gap-group\">\n <div className=\"min-w-0 flex-1\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {stepLabel ?? 'Logs'}\n </Text>\n {statusVisual ? (\n <div className=\"flex min-w-0 items-center gap-inline text-foreground-neutral-muted\">\n <Badge variant={statusVisual.badge} size=\"2xs\" radius=\"rounded\">\n {statusVisual.label}\n </Badge>\n {attempt ? (\n <span className=\"font-code text-xs leading-20 tabular-nums\">attempt {attempt}</span>\n ) : null}\n </div>\n ) : (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Select a step to view its logs.\n </Text>\n )}\n </div>\n <PanelActions className=\"min-w-0 flex-wrap justify-end\">\n <SearchInline\n value={search}\n onChange={(event) => onSearchChange(event.target.value)}\n aria-label=\"Search logs\"\n placeholder=\"Search logs\"\n size=\"small\"\n className=\"w-180 max-w-full\"\n disabled={disabled}\n />\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n icon=\"refreshLine\"\n aria-label=\"Refresh logs\"\n onClick={onRefresh}\n disabled={disabled || refreshing}\n isLoading={refreshing}\n />\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n icon=\"settings3Line\"\n aria-label=\"Log settings\"\n disabled={disabled}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" size=\"sm\">\n <DropdownMenuLabel>Log display</DropdownMenuLabel>\n <DropdownMenuCheckboxItem\n checked={showLineNumbers}\n onCheckedChange={onShowLineNumbersChange}\n >\n Line numbers\n </DropdownMenuCheckboxItem>\n <DropdownMenuCheckboxItem checked={wrap} onCheckedChange={onWrapChange}>\n Wrap long lines\n </DropdownMenuCheckboxItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </PanelActions>\n </PanelHeader>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n 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":"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"}
@@ -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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"}
@@ -8,8 +8,14 @@ export interface StepAttemptLogPanelProps {
8
8
  /** The page scroll column used by the uncapped job-detail log surface. */
9
9
  pageScrollRef: RefObject<HTMLElement | null>;
10
10
  surfaceClassName?: string | undefined;
11
+ search?: string | undefined;
12
+ wrap?: boolean | undefined;
13
+ showLineNumbers?: boolean | undefined;
14
+ attemptId?: string | undefined;
15
+ refreshToken?: number | undefined;
16
+ onFetchingChange?: ((attemptId: string, isFetching: boolean) => void) | undefined;
11
17
  initialErrorRetryCount?: number | undefined;
12
18
  initialErrorRetryDelayMs?: number | undefined;
13
19
  }
14
- export declare function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName, initialErrorRetryCount, initialErrorRetryDelayMs, }: StepAttemptLogPanelProps): import("react").JSX.Element;
20
+ export declare function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName, search, wrap, showLineNumbers, attemptId, refreshToken, onFetchingChange, initialErrorRetryCount, initialErrorRetryDelayMs, }: StepAttemptLogPanelProps): import("react").JSX.Element;
15
21
  //# sourceMappingURL=step-attempt-log-panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAQxD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,gBAA2C,EAC3C,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+BA8F1B"}
1
+ {"version":3,"file":"step-attempt-log-panel.d.ts","sourceRoot":"","sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAQxD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0EAA0E;IAC1E,aAAa,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAClF,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,gBAA2C,EAC3C,MAAW,EACX,IAAY,EACZ,eAAsB,EACtB,SAAS,EACT,YAAgB,EAChB,gBAAgB,EAChB,sBAAsD,EACtD,wBAA2D,GAC5D,EAAE,wBAAwB,+BA2H1B"}
@@ -8,8 +8,8 @@ import { JobExecutionTimeText } from './job-execution-time-text.js';
8
8
  const TAIL_FOLLOW_THRESHOLD_PX = 24;
9
9
  const INITIAL_LOG_ERROR_RETRY_COUNT = 5;
10
10
  const INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;
11
- const defaultLogSurfaceClasses = 'rounded-8';
12
- export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName = defaultLogSurfaceClasses, initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT, initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS }) {
11
+ const defaultLogSurfaceClasses = 'rounded-none border-0 bg-background-contrast-base shadow-none';
12
+ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptStartedAt, pageScrollRef, surfaceClassName = defaultLogSurfaceClasses, search = '', wrap = false, showLineNumbers = true, attemptId, refreshToken = 0, onFetchingChange, initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT, initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS }) {
13
13
  const shouldFollowTailRef = useRef(true);
14
14
  const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;
15
15
  const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);
@@ -26,6 +26,23 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
26
26
  const missingActiveStream = retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);
27
27
  const initialError = query.isError && query.data === undefined && !missingActiveStream;
28
28
  const staleError = query.isError && query.data !== undefined;
29
+ const lastRefreshTokenRef = useRef(refreshToken);
30
+ useEffect(()=>{
31
+ if (refreshToken === lastRefreshTokenRef.current) return;
32
+ lastRefreshTokenRef.current = refreshToken;
33
+ void query.refetchLogs();
34
+ }, [
35
+ query.refetchLogs,
36
+ refreshToken
37
+ ]);
38
+ useEffect(()=>{
39
+ if (!attemptId) return;
40
+ onFetchingChange?.(attemptId, query.isFetching);
41
+ }, [
42
+ attemptId,
43
+ onFetchingChange,
44
+ query.isFetching
45
+ ]);
29
46
  useEffect(()=>{
30
47
  const scrollElement = pageScrollRef.current;
31
48
  if (!scrollElement) return undefined;
@@ -60,7 +77,9 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
60
77
  if (query.isPending) {
61
78
  return /*#__PURE__*/ _jsx(StepLogsLoadingSurface, {
62
79
  label: "Loading logs",
63
- className: surfaceClassName
80
+ className: surfaceClassName,
81
+ wrap: wrap,
82
+ showLineNumbers: showLineNumbers
64
83
  });
65
84
  }
66
85
  if (missingActiveStream) {
@@ -72,13 +91,15 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
72
91
  }
73
92
  return /*#__PURE__*/ _jsx(StepLogsLoadingSurface, {
74
93
  label: "Waiting for logs",
75
- className: surfaceClassName
94
+ className: surfaceClassName,
95
+ wrap: wrap,
96
+ showLineNumbers: showLineNumbers
76
97
  });
77
98
  }
78
99
  if (initialError) {
79
100
  return /*#__PURE__*/ _jsx(StepLogsError, {
80
101
  retrying: query.isFetching,
81
- onRetry: ()=>void query.refetch()
102
+ onRetry: ()=>void query.refetchLogs()
82
103
  });
83
104
  }
84
105
  return /*#__PURE__*/ _jsxs("div", {
@@ -101,7 +122,7 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
101
122
  size: "2xs",
102
123
  variant: "secondary",
103
124
  isLoading: query.isFetching,
104
- onClick: ()=>void query.refetch(),
125
+ onClick: ()=>void query.refetchLogs(),
105
126
  children: "Retry"
106
127
  })
107
128
  ]
@@ -109,20 +130,25 @@ export function StepAttemptLogPanel({ stepId, attempt, attemptStatus, attemptSta
109
130
  }) : null,
110
131
  /*#__PURE__*/ _jsx(LogView, {
111
132
  records: records,
133
+ search: search,
134
+ wrap: wrap,
135
+ showLineNumbers: showLineNumbers,
112
136
  emptyState: query.data?.complete ? 'complete' : 'pending',
113
137
  anchorToFailure: anchorToFailure,
114
- ariaLive: attemptStatus === 'running' ? 'polite' : 'off',
138
+ ariaLive: search.trim() ? 'off' : attemptStatus === 'running' ? 'polite' : 'off',
115
139
  className: surfaceClassName
116
140
  })
117
141
  ]
118
142
  });
119
143
  }
120
- function StepLogsLoadingSurface({ label, className }) {
144
+ function StepLogsLoadingSurface({ label, className, wrap, showLineNumbers }) {
121
145
  return /*#__PURE__*/ _jsx("div", {
122
146
  role: "status",
123
147
  "aria-label": label,
124
148
  children: /*#__PURE__*/ _jsx(LogViewSkeleton, {
125
- className: className
149
+ className: className,
150
+ wrap: wrap,
151
+ showLineNumbers: showLineNumbers
126
152
  })
127
153
  });
128
154
  }
@@ -133,7 +159,7 @@ function StepLogsWaitingSurface({ startedAt, className }) {
133
159
  className: className,
134
160
  children: /*#__PURE__*/ _jsxs(Text, {
135
161
  size: "xs",
136
- className: "px-tight py-row text-foreground-neutral-muted",
162
+ className: "px-tight py-row text-foreground-contrast-secondary",
137
163
  children: [
138
164
  "Waiting for output ·",
139
165
  ' ',
@@ -189,6 +215,7 @@ function StepLogsError({ retrying, onRetry }) {
189
215
  size: "2xs",
190
216
  variant: "secondary",
191
217
  isLoading: retrying,
218
+ disabled: retrying,
192
219
  onClick: onRetry,
193
220
  children: "Retry"
194
221
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst defaultLogSurfaceClasses = 'rounded-8';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n /** The start time used by the page's quiet waiting-for-output state. */\n attemptStartedAt?: string | undefined;\n /** The page scroll column used by the uncapped job-detail log surface. */\n pageScrollRef: RefObject<HTMLElement | null>;\n surfaceClassName?: string | undefined;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n attemptStartedAt,\n pageScrollRef,\n surfaceClassName = defaultLogSurfaceClasses,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n\n useEffect(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return undefined;\n\n const onScroll = () => {\n shouldFollowTailRef.current = isNearPageBottom(scrollElement);\n };\n scrollElement.addEventListener('scroll', onScroll, {passive: true});\n onScroll();\n return () => scrollElement.removeEventListener('scroll', onScroll);\n }, [pageScrollRef]);\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, pageScrollRef, recordCount]);\n\n if (query.isPending) {\n return <StepLogsLoadingSurface label=\"Loading logs\" className={surfaceClassName} />;\n }\n\n if (missingActiveStream) {\n if (attemptStatus === 'running' && attemptStartedAt) {\n return <StepLogsWaitingSurface startedAt={attemptStartedAt} className={surfaceClassName} />;\n }\n return <StepLogsLoadingSurface label=\"Waiting for logs\" className={surfaceClassName} />;\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetch()} />;\n }\n\n return (\n <div className=\"flex min-w-0 flex-col gap-inline\">\n {staleError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetch()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n ariaLive={attemptStatus === 'running' ? 'polite' : 'off'}\n className={surfaceClassName}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({label, className}: {label: string; className: string}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={className} />\n </div>\n );\n}\n\nfunction StepLogsWaitingSurface({startedAt, className}: {startedAt: string; className: string}) {\n return (\n <div role=\"status\" aria-label=\"Waiting for logs\" className={className}>\n <Text size=\"xs\" className=\"px-tight py-row text-foreground-neutral-muted\">\n Waiting for output ·{' '}\n <span className=\"font-code tabular-nums\" aria-hidden=\"true\">\n <JobExecutionTimeText time={{state: 'live', fromIso: startedAt}} />\n </span>\n </Text>\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isNearPageBottom(element: HTMLElement): boolean {\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n return distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button type=\"button\" size=\"2xs\" variant=\"secondary\" isLoading={retrying} onClick={onRetry}>\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","JobExecutionTimeText","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","defaultLogSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","attemptStartedAt","pageScrollRef","surfaceClassName","initialErrorRetryCount","initialErrorRetryDelayMs","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","scrollElement","current","onScroll","isNearPageBottom","addEventListener","passive","removeEventListener","frame","scheduleAnimationFrame","scrollTop","scrollHeight","cancelScheduledFrame","isPending","StepLogsLoadingSurface","label","className","StepLogsWaitingSurface","startedAt","StepLogsError","retrying","isFetching","onRetry","refetch","div","role","type","variant","size","isLoading","onClick","emptyState","complete","ariaLive","aria-label","span","aria-hidden","time","state","fromIso","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","element","distanceFromBottom","clientHeight","status"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/job-detail/step-attempt-log-panel.tsx"],"sourcesContent":["import {\n isMissingStepLogStreamError,\n LogView,\n LogViewSkeleton,\n useStepAttemptLogsQuery,\n} from '@shipfox/client-logs';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {type RefObject, useEffect, useRef} from 'react';\nimport {JobExecutionTimeText} from './job-execution-time-text.js';\n\nconst TAIL_FOLLOW_THRESHOLD_PX = 24;\nconst INITIAL_LOG_ERROR_RETRY_COUNT = 5;\nconst INITIAL_LOG_ERROR_RETRY_DELAY_MS = 1_500;\nconst defaultLogSurfaceClasses = 'rounded-none border-0 bg-background-contrast-base shadow-none';\n\nexport interface StepAttemptLogPanelProps {\n stepId: string;\n attempt: number;\n attemptStatus: string;\n /** The start time used by the page's quiet waiting-for-output state. */\n attemptStartedAt?: string | undefined;\n /** The page scroll column used by the uncapped job-detail log surface. */\n pageScrollRef: RefObject<HTMLElement | null>;\n surfaceClassName?: string | undefined;\n search?: string | undefined;\n wrap?: boolean | undefined;\n showLineNumbers?: boolean | undefined;\n attemptId?: string | undefined;\n refreshToken?: number | undefined;\n onFetchingChange?: ((attemptId: string, isFetching: boolean) => void) | undefined;\n initialErrorRetryCount?: number | undefined;\n initialErrorRetryDelayMs?: number | undefined;\n}\n\nexport function StepAttemptLogPanel({\n stepId,\n attempt,\n attemptStatus,\n attemptStartedAt,\n pageScrollRef,\n surfaceClassName = defaultLogSurfaceClasses,\n search = '',\n wrap = false,\n showLineNumbers = true,\n attemptId,\n refreshToken = 0,\n onFetchingChange,\n initialErrorRetryCount = INITIAL_LOG_ERROR_RETRY_COUNT,\n initialErrorRetryDelayMs = INITIAL_LOG_ERROR_RETRY_DELAY_MS,\n}: StepAttemptLogPanelProps) {\n const shouldFollowTailRef = useRef(true);\n const missingStreamRetryCount = attemptStatus === 'running' ? undefined : initialErrorRetryCount;\n const retryMissingStream = attemptStatus === 'running' || isTerminalAttemptStatus(attemptStatus);\n const query = useStepAttemptLogsQuery(stepId, attempt, {\n retryMissingStream,\n missingStreamRetryCount,\n missingStreamRetryDelayMs: initialErrorRetryDelayMs,\n initialErrorRetryCount,\n initialErrorRetryDelayMs,\n });\n const records = query.data?.records ?? [];\n const recordCount = records.length;\n const anchorToFailure = attemptStatus === 'failed';\n const missingActiveStream =\n retryMissingStream && query.data === undefined && isMissingStepLogStreamError(query.error);\n const initialError = query.isError && query.data === undefined && !missingActiveStream;\n const staleError = query.isError && query.data !== undefined;\n const lastRefreshTokenRef = useRef(refreshToken);\n\n useEffect(() => {\n if (refreshToken === lastRefreshTokenRef.current) return;\n lastRefreshTokenRef.current = refreshToken;\n void query.refetchLogs();\n }, [query.refetchLogs, refreshToken]);\n\n useEffect(() => {\n if (!attemptId) return;\n onFetchingChange?.(attemptId, query.isFetching);\n }, [attemptId, onFetchingChange, query.isFetching]);\n\n useEffect(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return undefined;\n\n const onScroll = () => {\n shouldFollowTailRef.current = isNearPageBottom(scrollElement);\n };\n scrollElement.addEventListener('scroll', onScroll, {passive: true});\n onScroll();\n return () => scrollElement.removeEventListener('scroll', onScroll);\n }, [pageScrollRef]);\n\n useEffect(() => {\n if (recordCount === 0) return undefined;\n if (anchorToFailure) return undefined;\n if (!shouldFollowTailRef.current) return undefined;\n\n const frame = scheduleAnimationFrame(() => {\n const scrollElement = pageScrollRef.current;\n if (!scrollElement) return;\n scrollElement.scrollTop = scrollElement.scrollHeight;\n });\n\n return () => {\n cancelScheduledFrame(frame);\n };\n }, [anchorToFailure, pageScrollRef, recordCount]);\n\n if (query.isPending) {\n return (\n <StepLogsLoadingSurface\n label=\"Loading logs\"\n className={surfaceClassName}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n />\n );\n }\n\n if (missingActiveStream) {\n if (attemptStatus === 'running' && attemptStartedAt) {\n return <StepLogsWaitingSurface startedAt={attemptStartedAt} className={surfaceClassName} />;\n }\n return (\n <StepLogsLoadingSurface\n label=\"Waiting for logs\"\n className={surfaceClassName}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n />\n );\n }\n\n if (initialError) {\n return <StepLogsError retrying={query.isFetching} onRetry={() => void query.refetchLogs()} />;\n }\n\n return (\n <div className=\"flex min-w-0 flex-col gap-inline\">\n {staleError ? (\n <Callout\n role=\"alert\"\n type=\"warning\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => void query.refetchLogs()}\n >\n Retry\n </Button>\n </div>\n </Callout>\n ) : null}\n <LogView\n records={records}\n search={search}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n emptyState={query.data?.complete ? 'complete' : 'pending'}\n anchorToFailure={anchorToFailure}\n ariaLive={search.trim() ? 'off' : attemptStatus === 'running' ? 'polite' : 'off'}\n className={surfaceClassName}\n />\n </div>\n );\n}\n\nfunction StepLogsLoadingSurface({\n label,\n className,\n wrap,\n showLineNumbers,\n}: {\n label: string;\n className: string;\n wrap: boolean;\n showLineNumbers: boolean;\n}) {\n return (\n <div role=\"status\" aria-label={label}>\n <LogViewSkeleton className={className} wrap={wrap} showLineNumbers={showLineNumbers} />\n </div>\n );\n}\n\nfunction StepLogsWaitingSurface({startedAt, className}: {startedAt: string; className: string}) {\n return (\n <div role=\"status\" aria-label=\"Waiting for logs\" className={className}>\n <Text size=\"xs\" className=\"px-tight py-row text-foreground-contrast-secondary\">\n Waiting for output ·{' '}\n <span className=\"font-code tabular-nums\" aria-hidden=\"true\">\n <JobExecutionTimeText time={{state: 'live', fromIso: startedAt}} />\n </span>\n </Text>\n </div>\n );\n}\n\nfunction scheduleAnimationFrame(callback: FrameRequestCallback): number {\n if (typeof globalThis.requestAnimationFrame === 'function') {\n return globalThis.requestAnimationFrame(callback);\n }\n return window.setTimeout(() => callback(Date.now()), 0);\n}\n\nfunction cancelScheduledFrame(frame: number) {\n if (typeof globalThis.cancelAnimationFrame === 'function') {\n globalThis.cancelAnimationFrame(frame);\n return;\n }\n window.clearTimeout(frame);\n}\n\nfunction isNearPageBottom(element: HTMLElement): boolean {\n const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;\n return distanceFromBottom <= TAIL_FOLLOW_THRESHOLD_PX;\n}\n\nfunction isTerminalAttemptStatus(status: string): boolean {\n return status === 'succeeded' || status === 'failed' || status === 'cancelled';\n}\n\nfunction StepLogsError({retrying, onRetry}: {retrying: boolean; onRetry: () => void}) {\n return (\n <Callout\n role=\"alert\"\n type=\"error\"\n variant=\"secondary\"\n className=\"rounded-none border-b border-border-neutral-base px-0 py-row shadow-none\"\n >\n <div className=\"flex min-w-0 flex-1 items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not load logs.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={retrying}\n disabled={retrying}\n onClick={onRetry}\n >\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n"],"names":["isMissingStepLogStreamError","LogView","LogViewSkeleton","useStepAttemptLogsQuery","Button","Callout","Text","useEffect","useRef","JobExecutionTimeText","TAIL_FOLLOW_THRESHOLD_PX","INITIAL_LOG_ERROR_RETRY_COUNT","INITIAL_LOG_ERROR_RETRY_DELAY_MS","defaultLogSurfaceClasses","StepAttemptLogPanel","stepId","attempt","attemptStatus","attemptStartedAt","pageScrollRef","surfaceClassName","search","wrap","showLineNumbers","attemptId","refreshToken","onFetchingChange","initialErrorRetryCount","initialErrorRetryDelayMs","shouldFollowTailRef","missingStreamRetryCount","undefined","retryMissingStream","isTerminalAttemptStatus","query","missingStreamRetryDelayMs","records","data","recordCount","length","anchorToFailure","missingActiveStream","error","initialError","isError","staleError","lastRefreshTokenRef","current","refetchLogs","isFetching","scrollElement","onScroll","isNearPageBottom","addEventListener","passive","removeEventListener","frame","scheduleAnimationFrame","scrollTop","scrollHeight","cancelScheduledFrame","isPending","StepLogsLoadingSurface","label","className","StepLogsWaitingSurface","startedAt","StepLogsError","retrying","onRetry","div","role","type","variant","size","isLoading","onClick","emptyState","complete","ariaLive","trim","aria-label","span","aria-hidden","time","state","fromIso","callback","globalThis","requestAnimationFrame","window","setTimeout","Date","now","cancelAnimationFrame","clearTimeout","element","distanceFromBottom","clientHeight","status","disabled"],"mappings":"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"}
@@ -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));