@shipfox/client-workflows 19.0.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +43 -0
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +231 -42
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts +1 -0
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +1 -1
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +24 -7
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.d.ts +8 -7
- package/dist/components/workflow-run-duration-label.d.ts.map +1 -1
- package/dist/components/workflow-run-duration-label.js +15 -31
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +22 -11
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +2 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +22 -16
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +8 -35
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +43 -36
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +0 -9
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +0 -11
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +10 -55
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +1 -94
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run.d.ts +2 -2
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +3 -1
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.js +1 -0
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/pages/project-workflows-page.d.ts.map +1 -1
- package/dist/pages/project-workflows-page.js +3 -2
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
- package/dist/pages/workflow-job-detail-page.js +3 -2
- package/dist/pages/workflow-job-detail-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.js +2 -1
- package/dist/pages/workflow-run-detail-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-list-page.js +9 -11
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/routes/job-detail.d.ts +1 -1
- package/dist/routes/job-detail.js +1 -1
- package/dist/routes/job-detail.js.map +1 -1
- package/dist/routes/run-detail.d.ts +1 -1
- package/dist/routes/run-detail.js +1 -1
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts +1 -1
- package/dist/routes/runs.js +1 -1
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts +3 -0
- package/dist/routes/workflows.js +3 -0
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/components/job-detail/job-detail-view.tsx +263 -47
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
- package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
- package/src/components/job-graph/job-graph.stories.tsx +1 -1
- package/src/components/job-graph/job-node.stories.tsx +1 -1
- package/src/components/step-list/step-list-model.ts +1 -1
- package/src/components/step-list/step-list.test.tsx +29 -1
- package/src/components/step-list/step-list.tsx +28 -9
- package/src/components/workflow-run-duration-label.tsx +21 -44
- package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
- package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +2 -2
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +101 -4
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
- package/src/components/workflow-run-list/workflow-run-row.tsx +24 -19
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +2 -40
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +75 -22
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +7 -31
- package/src/components/workflow-run-view/run-workspace-nav.tsx +1 -1
- package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
- package/src/components/workflow-run-view/workflow-run-view.stories.tsx +140 -0
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +38 -7
- package/src/components/workflow-run-view/workflow-run-view.tsx +45 -37
- package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
- package/src/components/workflow-status/status-visuals.ts +0 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +0 -1
- package/src/components/workflow-status/workflow-status-icon.tsx +0 -18
- package/src/core/entities/workflow-run.ts +9 -136
- package/src/core/workflow-run.test.ts +2 -0
- package/src/core/workflow-run.ts +0 -8
- package/src/hooks/api/workflow-run-mapper.ts +2 -0
- package/src/hooks/api/workflow-runs.ts +1 -1
- package/src/pages/project-workflows-page.tsx +3 -2
- package/src/pages/workflow-job-detail-page.test.tsx +32 -0
- package/src/pages/workflow-job-detail-page.tsx +5 -1
- package/src/pages/workflow-run-detail-page.tsx +1 -1
- package/src/pages/workflow-run-list-page.tsx +9 -11
- package/src/pages/workflow-run-pages.test.tsx +26 -0
- package/src/routes/job-detail.tsx +1 -1
- package/src/routes/run-detail.tsx +1 -1
- package/src/routes/runs.tsx +1 -1
- package/src/routes/workflows.tsx +1 -0
- package/test/fixtures/workflow-run.ts +30 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/src/core/entities/workflow-run-display.test.ts +0 -187
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent\"\n renderExpandedStep={(context) => (\n <ExpandedStep context={context} pageScrollRef={pageScrollRef} />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-neutral-base outline-none focus-visible:shadow-border-interactive-with-active dark:bg-background-contrast-subtle\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n surfaceClassName=\"rounded-none border-0 bg-transparent shadow-none\"\n />\n </section>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {stepId: step.id, attemptId: attempt.id, stepLabel: step.name || step.key || step.id};\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Icon","Skeleton","TimeTickerProvider","Text","Link","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","MaterializedOutputFailureNotice","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","as","defaultSelectedAttemptId","onSelectedAttemptChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","stepLabel","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","surfaceClassName","title","description","variant","to","params","name","aria-hidden","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-detail/job-detail-view.tsx"],"sourcesContent":["// biome-ignore-all lint/a11y/noRedundantRoles: the job log region keeps an explicit role for the public accessibility contract.\n// biome-ignore-all lint/a11y/noNoninteractiveTabindex: the job log region is intentionally keyboard focusable.\n\nimport {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {IconButton} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Panel, PanelActions, PanelBody, PanelHeader} from '@shipfox/react-ui/panel';\nimport {SearchInline} from '@shipfox/react-ui/search';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {type RefObject, useCallback, useEffect, useRef, useState} from 'react';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {summarizeJobAnnotations} from '#core/run-annotation.js';\nimport {isWorkflowRunTerminal, type Job, type JobExecution} from '#core/workflow-run.js';\nimport {useWorkflowRunAnnotationSummaryQuery} from '#hooks/api/annotations.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport type {useWorkflowRunAttemptQuery} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {type StepExpandedContext, StepList} from '../step-list/index.js';\nimport {\n buildStepListModel,\n getStepStatusVisual,\n type StepListModel,\n} from '../step-list/step-list-model.js';\nimport {\n WorkflowRunNotFound,\n WorkflowRunStaleError,\n} from '../workflow-run-view/workflow-run-states.js';\nimport {JobContextPanel} from './job-context-panel.js';\nimport {JobDetailHeader} from './job-detail-header.js';\nimport {\n CarriedOverStepPanel,\n emptyStateForJob,\n emptyStateForMissingExecution,\n jobSucceededSummary,\n MaterializedOutputFailureNotice,\n} from './job-empty-states.js';\nimport {\n resolveWorkflowJobSelection,\n type WorkflowJobLandingSelection,\n workflowJobLandingSelection,\n} from './job-selection.js';\nimport {StepAttemptLogPanel} from './step-attempt-log-panel.js';\nimport {StepInspectorSheet} from './step-troubleshooting.js';\n\ntype InspectorState = {key: string; attemptId: string | null};\n\nexport interface JobDetailViewProps {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n jobId: string;\n search: WorkflowJobSearch;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n newerAttempt?: number | undefined;\n newerJob?: Job | undefined;\n onSelectionChange: (selection: WorkflowJobSearch) => void;\n}\n\nexport function JobDetailView({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n jobId,\n search,\n query,\n newerAttempt,\n newerJob,\n onSelectionChange,\n}: JobDetailViewProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const pageScrollRef = useRef<HTMLDivElement>(null);\n const landingSelectionRef = useRef<FrozenLandingSelection | undefined>(undefined);\n const [logSearch, setLogSearch] = useState('');\n const [wrapLogs, setWrapLogs] = useState(false);\n const [showLineNumbers, setShowLineNumbers] = useState(true);\n const [expandedLogAttemptIds, setExpandedLogAttemptIds] = useState<readonly string[]>([]);\n const [logRefreshTokens, setLogRefreshTokens] = useState<Record<string, number>>({});\n const [logFetchingByAttemptId, setLogFetchingByAttemptId] = useState<Record<string, boolean>>({});\n const hasLoadedData = query.data !== undefined;\n // Reuse the run workspace's bounded annotation read for the job header chip. The separate\n // summary query below stays counts-only and is scoped to the inspector's selected execution.\n const annotations = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: query.data?.runAttempt.attempt,\n });\n const jobAnnotationSummary = annotations.annotations\n ? summarizeJobAnnotations(annotations.annotations, jobId, {\n truncated: annotations.summary?.truncated ?? false,\n })\n : undefined;\n const inspectorResetKey = `${jobId}:${search.jobExecutionId ?? ''}`;\n const [inspectorState, setInspectorState] = useState<InspectorState>(() => ({\n key: inspectorResetKey,\n attemptId: null,\n }));\n const inspectorOpenAttemptId =\n inspectorState.key === inspectorResetKey ? inspectorState.attemptId : null;\n const annotationJob = query.data?.jobs.find((candidate) => candidate.id === jobId);\n const annotationExecutionId = annotationJob\n ? resolveWorkflowJobSelection({job: annotationJob, selection: search}).jobExecution?.id\n : search.jobExecutionId;\n const annotationPolling = query.data\n ? !isWorkflowRunTerminal(query.data.runAttempt.status)\n : true;\n const annotationSummaryQuery = useWorkflowRunAnnotationSummaryQuery(\n query.data?.id,\n query.data?.runAttempt.attempt,\n annotationExecutionId,\n {polling: annotationPolling},\n );\n\n useEffect(() => {\n if (!jobId || !hasLoadedData) return;\n const heading = rootRef.current?.querySelector<HTMLElement>('[data-job-heading]');\n // The heading is intentionally the route-change focus target: it announces the new job\n // after rail navigation without putting focus on a decorative status element.\n heading?.focus({preventScroll: true});\n }, [hasLoadedData, jobId]);\n\n const handleLogFetchingChange = useCallback((attemptId: string, isFetching: boolean) => {\n setLogFetchingByAttemptId((current) => {\n if (current[attemptId] === isFetching) return current;\n return {...current, [attemptId]: isFetching};\n });\n }, []);\n\n if (query.isPending) return <JobDetailSkeleton />;\n\n if (query.isError && query.data === undefined) {\n if (query.error instanceof ApiError && query.error.status === 404) {\n return <WorkflowRunNotFound />;\n }\n return <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />;\n }\n\n if (query.data === undefined) return <JobDetailSkeleton />;\n\n const run = query.data;\n const job = run.jobs.find((candidate) => candidate.id === jobId);\n if (!job) {\n return (\n <JobNotFoundState\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n );\n }\n\n const resolvedSelection = resolveWorkflowJobSelection({job, selection: search});\n const selectedJobExecution = resolvedSelection.jobExecution;\n const hasExplicitStep = Boolean(search.stepId && resolvedSelection.step);\n const stepListModel = buildStepListModel({job, jobExecution: selectedJobExecution});\n const currentLandingSelection = workflowJobLandingSelection(selectedJobExecution);\n if (selectedJobExecution) {\n const frozenLanding = landingSelectionRef.current;\n if (\n !frozenLanding ||\n frozenLanding.jobId !== job.id ||\n frozenLanding.jobExecutionId !== selectedJobExecution.id\n ) {\n landingSelectionRef.current = {\n jobId: job.id,\n jobExecutionId: selectedJobExecution.id,\n selection: currentLandingSelection,\n hasSelection: currentLandingSelection !== undefined,\n };\n } else if (!frozenLanding.hasSelection && currentLandingSelection) {\n landingSelectionRef.current = {\n ...frozenLanding,\n selection: currentLandingSelection,\n hasSelection: true,\n };\n }\n } else {\n landingSelectionRef.current = undefined;\n }\n const landingSelection = landingSelectionRef.current?.selection;\n const selectedAttemptId = hasExplicitStep ? resolvedSelection.selectedAttemptId : undefined;\n const runningSelection = runningStepSelection(selectedJobExecution, stepListModel);\n const selectedStepId = resolvedSelection.step?.id ?? landingSelection?.stepId;\n const selectedAttemptForNotice = hasExplicitStep\n ? resolvedSelection.selectedAttemptId\n : landingSelection?.attemptId;\n const expandedLogSelection = findExpandedLogSelection(\n selectedJobExecution,\n expandedLogAttemptIds,\n stepListModel,\n );\n const selectedLogStep = expandedLogSelection?.step;\n const selectedLogAttempt = expandedLogSelection?.attempt;\n const selectedLogStatus = selectedLogAttempt?.status ?? selectedLogStep?.status;\n const logIsFetching = Boolean(\n selectedLogAttempt && logFetchingByAttemptId[selectedLogAttempt.id],\n );\n const showRetargetNotice =\n runningSelection !== undefined &&\n (selectedStepId !== runningSelection.stepId ||\n selectedAttemptForNotice !== runningSelection.attemptId);\n const succeededSummary =\n !hasExplicitStep && landingSelection === undefined && selectedJobExecution\n ? jobSucceededSummary(job, selectedJobExecution)\n : undefined;\n\n function selectExecution(jobExecutionId: string) {\n onSelectionChange({\n ...search,\n jobExecutionId,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n }\n\n function selectAttempt(attemptId: string | undefined) {\n if (!selectedJobExecution) return;\n if (!attemptId) {\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: undefined,\n stepAttemptId: undefined,\n });\n return;\n }\n\n const match = findAttempt(selectedJobExecution, attemptId);\n if (!match) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: match.step.id,\n stepAttemptId: match.attemptId,\n });\n }\n\n function onInspectorOpenChange(attemptId: string | null) {\n setInspectorState({key: inspectorResetKey, attemptId});\n }\n\n function retargetToRunningStep() {\n if (!runningSelection || !selectedJobExecution) return;\n onSelectionChange({\n ...search,\n jobExecutionId: selectedJobExecution.id,\n stepId: runningSelection.stepId,\n stepAttemptId: runningSelection.attemptId,\n });\n }\n\n function refreshLogs() {\n if (!selectedLogAttempt) return;\n setLogRefreshTokens((current) => ({\n ...current,\n [selectedLogAttempt.id]: (current[selectedLogAttempt.id] ?? 0) + 1,\n }));\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div ref={rootRef} className=\"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden\">\n {query.isError ? <WorkflowRunStaleError query={query} /> : null}\n {newerJob && newerAttempt && newerAttempt > run.runAttempt.attempt ? (\n <NewerAttemptNotice\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n jobId={newerJob.id}\n attempt={newerAttempt}\n />\n ) : null}\n <div ref={pageScrollRef} className=\"@container min-h-0 flex-1 overflow-auto pb-panel\">\n <section aria-label={`${job.displayName} logs`} className=\"flex w-full flex-col\">\n <section className=\"min-w-0 overflow-hidden\">\n <JobDetailHeader\n job={job}\n selectedJobExecution={selectedJobExecution}\n onSelectedJobExecutionChange={selectExecution}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n annotationSummary={jobAnnotationSummary}\n jobContext={\n selectedJobExecution ? (\n <JobContextPanel job={job} execution={selectedJobExecution} />\n ) : undefined\n }\n />\n <Panel data-job-log-panel className=\"min-w-0\">\n <JobLogPanelHeader\n stepLabel={expandedLogSelection?.stepLabel}\n attempt={selectedLogAttempt?.attempt}\n status={selectedLogStatus}\n search={logSearch}\n onSearchChange={setLogSearch}\n onRefresh={refreshLogs}\n refreshing={logIsFetching}\n showLineNumbers={showLineNumbers}\n onShowLineNumbersChange={setShowLineNumbers}\n wrap={wrapLogs}\n onWrapChange={setWrapLogs}\n disabled={!selectedLogAttempt}\n />\n <PanelBody className=\"min-w-0 p-0\">\n <Text as=\"h2\" className=\"sr-only\">\n Logs\n </Text>\n {selectedJobExecution ? (\n <>\n <MaterializedOutputFailureNotice jobExecution={selectedJobExecution} />\n <StepList\n job={job}\n jobExecution={selectedJobExecution}\n selectedAttemptId={selectedAttemptId}\n defaultSelectedAttemptId={landingSelection?.attemptId}\n onSelectedAttemptChange={selectAttempt}\n onExpandedAttemptIdsChange={setExpandedLogAttemptIds}\n inspectorOpenAttemptId={inspectorOpenAttemptId}\n onInspectorOpenChange={onInspectorOpenChange}\n autoSelectActiveAttempt\n emptyState={emptyStateForJob(job, selectedJobExecution)}\n showHeader={false}\n className=\"rounded-none border-0 bg-transparent shadow-none\"\n renderExpandedStep={(context) => (\n <ExpandedStep\n context={context}\n pageScrollRef={pageScrollRef}\n search={logSearch}\n wrap={wrapLogs}\n showLineNumbers={showLineNumbers}\n attemptId={context.attemptId}\n refreshToken={logRefreshTokens[context.attemptId] ?? 0}\n onFetchingChange={handleLogFetchingChange}\n />\n )}\n renderInspector={(entry) => (\n <StepInspectorSheet\n entry={entry}\n open\n onOpenChange={(open) => onInspectorOpenChange(open ? entry.id : null)}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n jobId={job.id}\n annotationCount={annotationCountForStep(\n annotationSummaryQuery.data,\n entry.step.id,\n entry.attempt,\n )}\n />\n )}\n />\n </>\n ) : (\n <EmptyStateForMissingExecution job={job} />\n )}\n </PanelBody>\n </Panel>\n </section>\n {showRetargetNotice && runningSelection ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className=\"flex min-w-0 items-center justify-between gap-inline border-t border-border-neutral-base px-row py-row\"\n >\n <Text size=\"xs\" className=\"min-w-0 text-foreground-neutral-muted\">\n Run moved on to{' '}\n <span className=\"font-code text-foreground-neutral-base\">\n {runningSelection.stepLabel}\n </span>\n .\n </Text>\n <button\n type=\"button\"\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n onClick={retargetToRunningStep}\n >\n Jump to it\n </button>\n </div>\n ) : null}\n {succeededSummary ? (\n <Text size=\"xs\" className=\"px-row py-row text-foreground-neutral-muted\">\n {succeededSummary}\n </Text>\n ) : null}\n </section>\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction ExpandedStep({\n context,\n pageScrollRef,\n search,\n wrap,\n showLineNumbers,\n attemptId,\n refreshToken,\n onFetchingChange,\n}: {\n context: StepExpandedContext;\n pageScrollRef: RefObject<HTMLDivElement | null>;\n search: string;\n wrap: boolean;\n showLineNumbers: boolean;\n attemptId: string;\n refreshToken: number;\n onFetchingChange: (attemptId: string, isFetching: boolean) => void;\n}) {\n if (context.carriedOver) return <CarriedOverStepPanel />;\n\n return (\n <section\n role=\"region\"\n tabIndex={0}\n aria-label={`${context.stepLabel} output, attempt ${context.attempt}`}\n className=\"flex min-w-0 flex-col border-t border-border-neutral-base bg-background-contrast-base outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <StepAttemptLogPanel\n stepId={context.stepId}\n attempt={context.attempt}\n attemptStatus={context.attemptStatus}\n attemptStartedAt={context.attemptStartedAt}\n pageScrollRef={pageScrollRef}\n search={search}\n wrap={wrap}\n showLineNumbers={showLineNumbers}\n attemptId={attemptId}\n refreshToken={refreshToken}\n onFetchingChange={onFetchingChange}\n />\n </section>\n );\n}\n\nfunction JobLogPanelHeader({\n stepLabel,\n attempt,\n status,\n search,\n onSearchChange,\n onRefresh,\n refreshing,\n showLineNumbers,\n onShowLineNumbersChange,\n wrap,\n onWrapChange,\n disabled,\n}: {\n stepLabel: string | undefined;\n attempt: number | undefined;\n status: string | undefined;\n search: string;\n onSearchChange: (value: string) => void;\n onRefresh: () => void;\n refreshing: boolean;\n showLineNumbers: boolean;\n onShowLineNumbersChange: (value: boolean) => void;\n wrap: boolean;\n onWrapChange: (value: boolean) => void;\n disabled: boolean;\n}) {\n const statusVisual = status ? getStepStatusVisual(status) : undefined;\n\n return (\n <PanelHeader className=\"flex flex-wrap items-center gap-group\">\n <div className=\"min-w-0 flex-1\">\n <Text size=\"sm\" bold className=\"truncate text-foreground-neutral-base\">\n {stepLabel ?? 'Logs'}\n </Text>\n {statusVisual ? (\n <div className=\"flex min-w-0 items-center gap-inline text-foreground-neutral-muted\">\n <Badge variant={statusVisual.badge} size=\"2xs\" radius=\"rounded\">\n {statusVisual.label}\n </Badge>\n {attempt ? (\n <span className=\"font-code text-xs leading-20 tabular-nums\">attempt {attempt}</span>\n ) : null}\n </div>\n ) : (\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n Select a step to view its logs.\n </Text>\n )}\n </div>\n <PanelActions className=\"min-w-0 flex-wrap justify-end\">\n <SearchInline\n value={search}\n onChange={(event) => onSearchChange(event.target.value)}\n aria-label=\"Search logs\"\n placeholder=\"Search logs\"\n size=\"small\"\n className=\"w-180 max-w-full\"\n disabled={disabled}\n />\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n icon=\"refreshLine\"\n aria-label=\"Refresh logs\"\n onClick={onRefresh}\n disabled={disabled || refreshing}\n isLoading={refreshing}\n />\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n type=\"button\"\n variant=\"transparent\"\n size=\"sm\"\n icon=\"settings3Line\"\n aria-label=\"Log settings\"\n disabled={disabled}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" size=\"sm\">\n <DropdownMenuLabel>Log display</DropdownMenuLabel>\n <DropdownMenuCheckboxItem\n checked={showLineNumbers}\n onCheckedChange={onShowLineNumbersChange}\n >\n Line numbers\n </DropdownMenuCheckboxItem>\n <DropdownMenuCheckboxItem checked={wrap} onCheckedChange={onWrapChange}>\n Wrap long lines\n </DropdownMenuCheckboxItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </PanelActions>\n </PanelHeader>\n );\n}\n\nfunction EmptyStateForMissingExecution({job}: {job: Job}) {\n const emptyState = emptyStateForMissingExecution(job);\n return (\n <EmptyState\n className=\"min-h-120 p-panel\"\n icon=\"componentLine\"\n title={emptyState.title}\n description={emptyState.description}\n variant=\"compact\"\n />\n );\n}\n\nfunction JobNotFoundState({\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n workflowRunId: string;\n search: WorkflowJobSearch;\n}) {\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col items-center justify-center gap-cluster p-panel\">\n <EmptyState\n icon=\"componentLine\"\n title=\"Job not found\"\n description=\"This job is not part of the selected workflow run.\"\n />\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {runAttempt: search.runAttempt},\n {runAttempt: search.runAttempt},\n ) as never\n }\n className=\"inline-flex items-center gap-inline rounded-6 px-tight py-[6px] text-sm text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n <Icon name=\"arrowLeftLine\" size={14} aria-hidden=\"true\" />\n Back to run summary\n </Link>\n </div>\n );\n}\n\nfunction NewerAttemptNotice({\n workspaceSlug,\n projectSlug,\n runId,\n jobId,\n attempt,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n jobId: string;\n attempt: number;\n}) {\n return (\n <div role=\"status\" className=\"border-b border-border-neutral-base px-row py-row\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\" className=\"text-foreground-neutral-muted\">\n A newer run attempt is available.\n </Text>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId, jobId}}\n search={workflowJobSearchParams({runAttempt: attempt}) as never}\n className=\"shrink-0 rounded-4 px-tight py-[4px] text-xs font-medium text-foreground-highlight-interactive outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n View attempt #{attempt}\n </Link>\n </div>\n </div>\n );\n}\n\nfunction runningStepSelection(jobExecution: JobExecution | undefined, model: StepListModel) {\n if (!jobExecution) return undefined;\n const steps = [...jobExecution.steps].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {\n const step = steps[stepIndex];\n if (!step) continue;\n const attempt = [...step.attempts]\n .sort((left, right) => left.attempt - right.attempt || left.id.localeCompare(right.id))\n .reverse()\n .find((candidate) => candidate.status === 'running');\n if (attempt)\n return {\n stepId: step.id,\n attemptId: attempt.id,\n stepLabel: model.entries.find((entry) => entry.id === attempt.id)?.step.label ?? 'Step',\n };\n }\n return undefined;\n}\n\nfunction findAttempt(jobExecution: JobExecution, attemptId: string) {\n for (const step of jobExecution.steps) {\n const attempt = step.attempts.find((candidate) => candidate.id === attemptId);\n if (attempt) return {step, attemptId: attempt.id};\n }\n return undefined;\n}\n\nfunction findExpandedLogSelection(\n jobExecution: JobExecution | undefined,\n attemptIds: readonly string[],\n model: StepListModel,\n) {\n if (!jobExecution) return undefined;\n\n for (let index = attemptIds.length - 1; index >= 0; index -= 1) {\n const attemptId = attemptIds[index];\n if (!attemptId) continue;\n const match = findAttempt(jobExecution, attemptId);\n const attempt = match?.step.attempts.find((candidate) => candidate.id === attemptId);\n const entry = model.entries.find((candidate) => candidate.id === attemptId);\n if (match && attempt && entry) return {step: match.step, attempt, stepLabel: entry.step.label};\n }\n\n return undefined;\n}\n\nfunction annotationCountForStep(\n summary: RunAnnotationSummary | undefined,\n stepId: string,\n attempt: number,\n): number | undefined {\n return summary?.stepCounts?.find((entry) => entry.stepId === stepId && entry.attempt === attempt)\n ?.total;\n}\n\nfunction JobDetailSkeleton() {\n return (\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto pb-panel\">\n <div className=\"w-full\">\n <header className=\"flex items-center gap-cluster border-b border-border-neutral-base px-row py-row\">\n <Skeleton className=\"size-20 rounded-full\" />\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-24 w-72 rounded-6\" />\n <Skeleton className=\"h-24 w-180 rounded-4\" />\n </header>\n <div className=\"p-panel-compact\">\n {JOB_DETAIL_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex min-h-44 items-center gap-inline border-b border-border-neutral-base last:border-b-0\"\n >\n <Skeleton className=\"size-14 rounded-full\" />\n <Skeleton className=\"h-16 w-180 rounded-4\" />\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst JOB_DETAIL_SKELETON_ROWS = ['step-1', 'step-2', 'step-3', 'step-4', 'step-5'];\n\ninterface FrozenLandingSelection {\n jobId: string;\n jobExecutionId: string;\n selection: WorkflowJobLandingSelection | undefined;\n hasSelection: boolean;\n}\n"],"names":["ApiError","QueryLoadError","Badge","IconButton","DropdownMenu","DropdownMenuCheckboxItem","DropdownMenuContent","DropdownMenuLabel","DropdownMenuTrigger","EmptyState","Icon","Panel","PanelActions","PanelBody","PanelHeader","SearchInline","Skeleton","TimeTickerProvider","Text","Link","useCallback","useEffect","useRef","useState","summarizeJobAnnotations","isWorkflowRunTerminal","useWorkflowRunAnnotationSummaryQuery","useRunAnnotationsQuery","workflowJobSearchParams","workflowRunSearchParams","StepList","buildStepListModel","getStepStatusVisual","WorkflowRunNotFound","WorkflowRunStaleError","JobContextPanel","JobDetailHeader","CarriedOverStepPanel","emptyStateForJob","emptyStateForMissingExecution","jobSucceededSummary","MaterializedOutputFailureNotice","resolveWorkflowJobSelection","workflowJobLandingSelection","StepAttemptLogPanel","StepInspectorSheet","JobDetailView","workspaceSlug","projectSlug","workflowRunId","jobId","search","query","newerAttempt","newerJob","onSelectionChange","rootRef","pageScrollRef","landingSelectionRef","undefined","logSearch","setLogSearch","wrapLogs","setWrapLogs","showLineNumbers","setShowLineNumbers","expandedLogAttemptIds","setExpandedLogAttemptIds","logRefreshTokens","setLogRefreshTokens","logFetchingByAttemptId","setLogFetchingByAttemptId","hasLoadedData","data","annotations","runAttempt","attempt","jobAnnotationSummary","truncated","summary","inspectorResetKey","jobExecutionId","inspectorState","setInspectorState","key","attemptId","inspectorOpenAttemptId","annotationJob","jobs","find","candidate","id","annotationExecutionId","job","selection","jobExecution","annotationPolling","status","annotationSummaryQuery","polling","heading","current","querySelector","focus","preventScroll","handleLogFetchingChange","isFetching","isPending","JobDetailSkeleton","isError","error","subject","icon","run","JobNotFoundState","resolvedSelection","selectedJobExecution","hasExplicitStep","Boolean","stepId","step","stepListModel","currentLandingSelection","frozenLanding","hasSelection","landingSelection","selectedAttemptId","runningSelection","runningStepSelection","selectedStepId","selectedAttemptForNotice","expandedLogSelection","findExpandedLogSelection","selectedLogStep","selectedLogAttempt","selectedLogStatus","logIsFetching","showRetargetNotice","succeededSummary","selectExecution","stepAttemptId","selectAttempt","match","findAttempt","onInspectorOpenChange","retargetToRunningStep","refreshLogs","intervalMs","reducedMotionIntervalMs","div","ref","className","NewerAttemptNotice","runId","section","aria-label","displayName","onSelectedJobExecutionChange","annotationSummary","jobContext","execution","data-job-log-panel","JobLogPanelHeader","stepLabel","onSearchChange","onRefresh","refreshing","onShowLineNumbersChange","wrap","onWrapChange","disabled","as","defaultSelectedAttemptId","onSelectedAttemptChange","onExpandedAttemptIdsChange","autoSelectActiveAttempt","emptyState","showHeader","renderExpandedStep","context","ExpandedStep","refreshToken","onFetchingChange","renderInspector","entry","open","onOpenChange","annotationCount","annotationCountForStep","EmptyStateForMissingExecution","role","aria-live","size","span","button","type","onClick","carriedOver","tabIndex","attemptStatus","attemptStartedAt","statusVisual","bold","variant","badge","radius","label","value","onChange","event","target","placeholder","isLoading","asChild","align","checked","onCheckedChange","title","description","to","params","name","aria-hidden","model","steps","sort","left","right","position","localeCompare","stepIndex","length","attempts","reverse","entries","attemptIds","index","stepCounts","total","header","JOB_DETAIL_SKELETON_ROWS","map","row"],"mappings":"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"}
|
|
@@ -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,+
|
|
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-
|
|
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.
|
|
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.
|
|
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-
|
|
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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"}
|
|
@@ -33,4 +33,5 @@ export declare function defaultStepListJobExecution(job: Job): JobExecution;
|
|
|
33
33
|
export declare function emptyJobExecutionForJob(job: Job): JobExecution;
|
|
34
34
|
export declare function getStepStatusVisual(status: string): StepStatusVisual;
|
|
35
35
|
export declare function humanizeStatus(status: string): string;
|
|
36
|
+
export declare function stepLabel(step: Step, index: number): string;
|
|
36
37
|
//# sourceMappingURL=step-list-model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-list-model.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,GAAG,EACR,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC,GAAG,aAAa,CAehB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAElE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAqB9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAepE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
|
1
|
+
{"version":3,"file":"step-list-model.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,KAAK,GAAG,EACR,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACnD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED,wBAAgB,kBAAkB,CAAC,EACjC,GAAG,EACH,YAAY,GACb,EAAE;IACD,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACzC,GAAG,aAAa,CAehB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAElE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAqB9D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAepE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrD;AAkBD,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ3D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/step-list/step-list-model.ts"],"sourcesContent":["import {\n getWorkflowStatusVisual,\n type WorkflowStatusVisual,\n} from '#components/workflow-status/status-visuals.js';\nimport {\n defaultJobExecution,\n isWorkflowStatus as isKnownWorkflowStatus,\n type Job,\n JobExecution,\n type Step,\n type StepAttempt,\n type StepAttemptDisplayDuration,\n} from '#core/workflow-run.js';\n\nexport interface StepStatusVisual extends Omit<WorkflowStatusVisual, 'kind'> {\n kind: string;\n ripple: boolean;\n}\n\nexport interface StepAttemptModel extends StepAttempt {\n displayDuration: StepAttemptDisplayDuration | null;\n statusVisual: StepStatusVisual;\n carriedOver: boolean;\n}\n\nexport interface StepModel extends Omit<Step, 'attempts'> {\n index: number;\n label: string;\n attempts: StepAttemptModel[];\n}\n\nexport interface StepListEntryModel extends StepAttemptModel {\n step: StepModel;\n}\n\nexport interface StepListModel {\n jobId: string;\n jobName: string;\n jobExecutionId: string;\n stepCount: number;\n activeEntryId: string | undefined;\n entries: StepListEntryModel[];\n}\n\nexport function buildStepListModel({\n job,\n jobExecution,\n}: {\n job: Job;\n jobExecution?: JobExecution | undefined;\n}): StepListModel {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const steps = [...selectedJobExecution.steps].sort(compareSteps).map(toStepModel);\n const entries = steps\n .flatMap((step) => toStepEntries(step, job.carriedOver))\n .sort(compareEntries);\n\n return {\n jobId: job.id,\n jobName: job.name ?? job.key,\n jobExecutionId: selectedJobExecution.id,\n stepCount: steps.length,\n activeEntryId: landingEntryId(entries),\n entries,\n };\n}\n\nexport function defaultStepListJobExecution(job: Job): JobExecution {\n return defaultJobExecution(job) ?? emptyJobExecutionForJob(job);\n}\n\nexport function emptyJobExecutionForJob(job: Job): JobExecution {\n return new JobExecution({\n id: `missing:${job.id}`,\n jobId: job.id,\n sequence: 1,\n name: job.name ?? job.key,\n status: job.status === 'skipped' ? 'cancelled' : job.status,\n statusReason: job.statusReason,\n statusReasonMessage: null,\n runner: job.runner,\n outputs: job.outputs,\n triggerEvents: [],\n queuedAt: null,\n startedAt: null,\n finishedAt: null,\n timedOutAt: null,\n evaluationTrace: job.evaluationTrace,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n steps: [],\n });\n}\n\nexport function getStepStatusVisual(status: string): StepStatusVisual {\n const normalized = normalizeStatus(status);\n\n if (isKnownWorkflowStatus(normalized)) {\n const visual = getWorkflowStatusVisual(normalized);\n return {...visual, ripple: normalized === 'running'};\n }\n\n return {\n kind: status,\n label: humanizeStatus(status),\n dot: 'neutral',\n badge: 'neutral',\n ripple: false,\n };\n}\n\nexport function humanizeStatus(status: string): string {\n const words = status.trim().replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').toLowerCase();\n if (!words) return 'Unknown';\n const firstLetter = words.at(0);\n return firstLetter === undefined ? 'Unknown' : firstLetter.toUpperCase() + words.slice(1);\n}\n\nfunction toStepModel(step: Step, index: number): StepModel {\n const attempts = [...step.attempts].sort(compareAttempts).map((attempt) => ({\n ...attempt,\n displayDuration: attempt.displayDuration,\n statusVisual: getStepStatusVisual(attempt.status),\n carriedOver: false,\n }));\n\n return {\n ...step,\n index: index + 1,\n label: stepLabel(step, index),\n attempts,\n };\n}\n\nfunction stepLabel(step: Step, index: number): string {\n const name = step.name.trim();\n if (name) return name;\n\n const key = step.key?.trim();\n if (key) return key;\n\n return `Step ${index + 1}`;\n}\n\nfunction toStepEntries(step: StepModel, carriedOverJob: boolean): StepListEntryModel[] {\n if (step.attempts.length > 0) {\n return step.attempts.map((attempt) => ({\n ...attempt,\n step,\n }));\n }\n\n if (!carriedOverJob) return [];\n\n return [\n {\n id: `carried-over:${step.id}`,\n stepId: step.id,\n jobExecutionId: step.jobExecutionId,\n attempt: step.currentAttempt,\n executionOrder: step.position,\n status: step.status,\n exitCode: null,\n output: null,\n outputs: null,\n response: null,\n error: null,\n gateResult: null,\n restartFeedback: null,\n startedAt: step.createdAt,\n finishedAt: null,\n displayDuration: null,\n statusVisual: getStepStatusVisual(step.status),\n carriedOver: true,\n step,\n },\n ];\n}\n\nfunction compareSteps(left: Step, right: Step): number {\n return (\n left.position - right.position ||\n (left.name ?? '').localeCompare(right.name ?? '') ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction compareAttempts(left: Step['attempts'][number], right: Step['attempts'][number]) {\n return left.attempt - right.attempt || left.id.localeCompare(right.id);\n}\n\nfunction compareEntries(left: StepListEntryModel, right: StepListEntryModel): number {\n return (\n left.executionOrder - right.executionOrder ||\n left.step.index - right.step.index ||\n left.attempt - right.attempt ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction landingEntryId(entries: readonly StepListEntryModel[]): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.statusVisual.kind === 'running') return entry.id;\n }\n return undefined;\n}\n\nfunction normalizeStatus(status: string): string {\n return status.trim().toLowerCase().replace(/-/g, '_');\n}\n"],"names":["getWorkflowStatusVisual","defaultJobExecution","isWorkflowStatus","isKnownWorkflowStatus","JobExecution","buildStepListModel","job","jobExecution","selectedJobExecution","defaultStepListJobExecution","steps","sort","compareSteps","map","toStepModel","entries","flatMap","step","toStepEntries","carriedOver","compareEntries","jobId","id","jobName","name","key","jobExecutionId","stepCount","length","activeEntryId","landingEntryId","emptyJobExecutionForJob","sequence","status","statusReason","statusReasonMessage","runner","outputs","triggerEvents","queuedAt","startedAt","finishedAt","timedOutAt","evaluationTrace","createdAt","updatedAt","getStepStatusVisual","normalized","normalizeStatus","visual","ripple","kind","label","humanizeStatus","dot","badge","words","trim","replace","toLowerCase","firstLetter","at","undefined","toUpperCase","slice","index","attempts","compareAttempts","attempt","displayDuration","statusVisual","stepLabel","carriedOverJob","stepId","currentAttempt","executionOrder","position","exitCode","output","response","error","gateResult","restartFeedback","left","right","localeCompare","entry"],"mappings":"AAAA,SACEA,uBAAuB,QAElB,gDAAgD;AACvD,SACEC,mBAAmB,EACnBC,oBAAoBC,qBAAqB,EAEzCC,YAAY,QAIP,wBAAwB;AAgC/B,OAAO,SAASC,mBAAmB,EACjCC,GAAG,EACHC,YAAY,EAIb;IACC,MAAMC,uBAAuBD,gBAAgBE,4BAA4BH;IACzE,MAAMI,QAAQ;WAAIF,qBAAqBE,KAAK;KAAC,CAACC,IAAI,CAACC,cAAcC,GAAG,CAACC;IACrE,MAAMC,UAAUL,MACbM,OAAO,CAAC,CAACC,OAASC,cAAcD,MAAMX,IAAIa,WAAW,GACrDR,IAAI,CAACS;IAER,OAAO;QACLC,OAAOf,IAAIgB,EAAE;QACbC,SAASjB,IAAIkB,IAAI,IAAIlB,IAAImB,GAAG;QAC5BC,gBAAgBlB,qBAAqBc,EAAE;QACvCK,WAAWjB,MAAMkB,MAAM;QACvBC,eAAeC,eAAef;QAC9BA;IACF;AACF;AAEA,OAAO,SAASN,4BAA4BH,GAAQ;IAClD,OAAOL,oBAAoBK,QAAQyB,wBAAwBzB;AAC7D;AAEA,OAAO,SAASyB,wBAAwBzB,GAAQ;IAC9C,OAAO,IAAIF,aAAa;QACtBkB,IAAI,CAAC,QAAQ,EAAEhB,IAAIgB,EAAE,EAAE;QACvBD,OAAOf,IAAIgB,EAAE;QACbU,UAAU;QACVR,MAAMlB,IAAIkB,IAAI,IAAIlB,IAAImB,GAAG;QACzBQ,QAAQ3B,IAAI2B,MAAM,KAAK,YAAY,cAAc3B,IAAI2B,MAAM;QAC3DC,cAAc5B,IAAI4B,YAAY;QAC9BC,qBAAqB;QACrBC,QAAQ9B,IAAI8B,MAAM;QAClBC,SAAS/B,IAAI+B,OAAO;QACpBC,eAAe,EAAE;QACjBC,UAAU;QACVC,WAAW;QACXC,YAAY;QACZC,YAAY;QACZC,iBAAiBrC,IAAIqC,eAAe;QACpCC,WAAWtC,IAAIsC,SAAS;QACxBC,WAAWvC,IAAIuC,SAAS;QACxBnC,OAAO,EAAE;IACX;AACF;AAEA,OAAO,SAASoC,oBAAoBb,MAAc;IAChD,MAAMc,aAAaC,gBAAgBf;IAEnC,IAAI9B,sBAAsB4C,aAAa;QACrC,MAAME,SAASjD,wBAAwB+C;QACvC,OAAO;YAAC,GAAGE,MAAM;YAAEC,QAAQH,eAAe;QAAS;IACrD;IAEA,OAAO;QACLI,MAAMlB;QACNmB,OAAOC,eAAepB;QACtBqB,KAAK;QACLC,OAAO;QACPL,QAAQ;IACV;AACF;AAEA,OAAO,SAASG,eAAepB,MAAc;IAC3C,MAAMuB,QAAQvB,OAAOwB,IAAI,GAAGC,OAAO,CAAC,UAAU,KAAKA,OAAO,CAAC,QAAQ,KAAKC,WAAW;IACnF,IAAI,CAACH,OAAO,OAAO;IACnB,MAAMI,cAAcJ,MAAMK,EAAE,CAAC;IAC7B,OAAOD,gBAAgBE,YAAY,YAAYF,YAAYG,WAAW,KAAKP,MAAMQ,KAAK,CAAC;AACzF;AAEA,SAASlD,YAAYG,IAAU,EAAEgD,KAAa;IAC5C,MAAMC,WAAW;WAAIjD,KAAKiD,QAAQ;KAAC,CAACvD,IAAI,CAACwD,iBAAiBtD,GAAG,CAAC,CAACuD,UAAa,CAAA;YAC1E,GAAGA,OAAO;YACVC,iBAAiBD,QAAQC,eAAe;YACxCC,cAAcxB,oBAAoBsB,QAAQnC,MAAM;YAChDd,aAAa;QACf,CAAA;IAEA,OAAO;QACL,GAAGF,IAAI;QACPgD,OAAOA,QAAQ;QACfb,OAAOmB,UAAUtD,MAAMgD;QACvBC;IACF;AACF;AAEA,SAASK,UAAUtD,IAAU,EAAEgD,KAAa;IAC1C,MAAMzC,OAAOP,KAAKO,IAAI,CAACiC,IAAI;IAC3B,IAAIjC,MAAM,OAAOA;IAEjB,MAAMC,MAAMR,KAAKQ,GAAG,EAAEgC;IACtB,IAAIhC,KAAK,OAAOA;IAEhB,OAAO,CAAC,KAAK,EAAEwC,QAAQ,GAAG;AAC5B;AAEA,SAAS/C,cAAcD,IAAe,EAAEuD,cAAuB;IAC7D,IAAIvD,KAAKiD,QAAQ,CAACtC,MAAM,GAAG,GAAG;QAC5B,OAAOX,KAAKiD,QAAQ,CAACrD,GAAG,CAAC,CAACuD,UAAa,CAAA;gBACrC,GAAGA,OAAO;gBACVnD;YACF,CAAA;IACF;IAEA,IAAI,CAACuD,gBAAgB,OAAO,EAAE;IAE9B,OAAO;QACL;YACElD,IAAI,CAAC,aAAa,EAAEL,KAAKK,EAAE,EAAE;YAC7BmD,QAAQxD,KAAKK,EAAE;YACfI,gBAAgBT,KAAKS,cAAc;YACnC0C,SAASnD,KAAKyD,cAAc;YAC5BC,gBAAgB1D,KAAK2D,QAAQ;YAC7B3C,QAAQhB,KAAKgB,MAAM;YACnB4C,UAAU;YACVC,QAAQ;YACRzC,SAAS;YACT0C,UAAU;YACVC,OAAO;YACPC,YAAY;YACZC,iBAAiB;YACjB1C,WAAWvB,KAAK2B,SAAS;YACzBH,YAAY;YACZ4B,iBAAiB;YACjBC,cAAcxB,oBAAoB7B,KAAKgB,MAAM;YAC7Cd,aAAa;YACbF;QACF;KACD;AACH;AAEA,SAASL,aAAauE,IAAU,EAAEC,KAAW;IAC3C,OACED,KAAKP,QAAQ,GAAGQ,MAAMR,QAAQ,IAC9B,AAACO,CAAAA,KAAK3D,IAAI,IAAI,EAAC,EAAG6D,aAAa,CAACD,MAAM5D,IAAI,IAAI,OAC9C2D,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AAElC;AAEA,SAAS6C,gBAAgBgB,IAA8B,EAAEC,KAA+B;IACtF,OAAOD,KAAKf,OAAO,GAAGgB,MAAMhB,OAAO,IAAIe,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AACvE;AAEA,SAASF,eAAe+D,IAAwB,EAAEC,KAAyB;IACzE,OACED,KAAKR,cAAc,GAAGS,MAAMT,cAAc,IAC1CQ,KAAKlE,IAAI,CAACgD,KAAK,GAAGmB,MAAMnE,IAAI,CAACgD,KAAK,IAClCkB,KAAKf,OAAO,GAAGgB,MAAMhB,OAAO,IAC5Be,KAAK7D,EAAE,CAAC+D,aAAa,CAACD,MAAM9D,EAAE;AAElC;AAEA,SAASQ,eAAef,OAAsC;IAC5D,IAAK,IAAIkD,QAAQlD,QAAQa,MAAM,GAAG,GAAGqC,SAAS,GAAGA,SAAS,EAAG;QAC3D,MAAMqB,QAAQvE,OAAO,CAACkD,MAAM;QAC5B,IAAIqB,OAAOhB,aAAanB,SAAS,WAAW,OAAOmC,MAAMhE,EAAE;IAC7D;IACA,OAAOwC;AACT;AAEA,SAASd,gBAAgBf,MAAc;IACrC,OAAOA,OAAOwB,IAAI,GAAGE,WAAW,GAAGD,OAAO,CAAC,MAAM;AACnD"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/step-list/step-list-model.ts"],"sourcesContent":["import {\n getWorkflowStatusVisual,\n type WorkflowStatusVisual,\n} from '#components/workflow-status/status-visuals.js';\nimport {\n defaultJobExecution,\n isWorkflowStatus as isKnownWorkflowStatus,\n type Job,\n JobExecution,\n type Step,\n type StepAttempt,\n type StepAttemptDisplayDuration,\n} from '#core/workflow-run.js';\n\nexport interface StepStatusVisual extends Omit<WorkflowStatusVisual, 'kind'> {\n kind: string;\n ripple: boolean;\n}\n\nexport interface StepAttemptModel extends StepAttempt {\n displayDuration: StepAttemptDisplayDuration | null;\n statusVisual: StepStatusVisual;\n carriedOver: boolean;\n}\n\nexport interface StepModel extends Omit<Step, 'attempts'> {\n index: number;\n label: string;\n attempts: StepAttemptModel[];\n}\n\nexport interface StepListEntryModel extends StepAttemptModel {\n step: StepModel;\n}\n\nexport interface StepListModel {\n jobId: string;\n jobName: string;\n jobExecutionId: string;\n stepCount: number;\n activeEntryId: string | undefined;\n entries: StepListEntryModel[];\n}\n\nexport function buildStepListModel({\n job,\n jobExecution,\n}: {\n job: Job;\n jobExecution?: JobExecution | undefined;\n}): StepListModel {\n const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);\n const steps = [...selectedJobExecution.steps].sort(compareSteps).map(toStepModel);\n const entries = steps\n .flatMap((step) => toStepEntries(step, job.carriedOver))\n .sort(compareEntries);\n\n return {\n jobId: job.id,\n jobName: job.name ?? job.key,\n jobExecutionId: selectedJobExecution.id,\n stepCount: steps.length,\n activeEntryId: landingEntryId(entries),\n entries,\n };\n}\n\nexport function defaultStepListJobExecution(job: Job): JobExecution {\n return defaultJobExecution(job) ?? emptyJobExecutionForJob(job);\n}\n\nexport function emptyJobExecutionForJob(job: Job): JobExecution {\n return new JobExecution({\n id: `missing:${job.id}`,\n jobId: job.id,\n sequence: 1,\n name: job.name ?? job.key,\n status: job.status === 'skipped' ? 'cancelled' : job.status,\n statusReason: job.statusReason,\n statusReasonMessage: null,\n runner: job.runner,\n outputs: job.outputs,\n triggerEvents: [],\n queuedAt: null,\n startedAt: null,\n finishedAt: null,\n timedOutAt: null,\n evaluationTrace: job.evaluationTrace,\n createdAt: job.createdAt,\n updatedAt: job.updatedAt,\n steps: [],\n });\n}\n\nexport function getStepStatusVisual(status: string): StepStatusVisual {\n const normalized = normalizeStatus(status);\n\n if (isKnownWorkflowStatus(normalized)) {\n const visual = getWorkflowStatusVisual(normalized);\n return {...visual, ripple: normalized === 'running'};\n }\n\n return {\n kind: status,\n label: humanizeStatus(status),\n dot: 'neutral',\n badge: 'neutral',\n ripple: false,\n };\n}\n\nexport function humanizeStatus(status: string): string {\n const words = status.trim().replace(/[_-]+/g, ' ').replace(/\\s+/g, ' ').toLowerCase();\n if (!words) return 'Unknown';\n const firstLetter = words.at(0);\n return firstLetter === undefined ? 'Unknown' : firstLetter.toUpperCase() + words.slice(1);\n}\n\nfunction toStepModel(step: Step, index: number): StepModel {\n const attempts = [...step.attempts].sort(compareAttempts).map((attempt) => ({\n ...attempt,\n displayDuration: attempt.displayDuration,\n statusVisual: getStepStatusVisual(attempt.status),\n carriedOver: false,\n }));\n\n return {\n ...step,\n index: index + 1,\n label: stepLabel(step, index),\n attempts,\n };\n}\n\nexport function stepLabel(step: Step, index: number): string {\n const name = step.name.trim();\n if (name) return name;\n\n const key = step.key?.trim();\n if (key) return key;\n\n return `Step ${index + 1}`;\n}\n\nfunction toStepEntries(step: StepModel, carriedOverJob: boolean): StepListEntryModel[] {\n if (step.attempts.length > 0) {\n return step.attempts.map((attempt) => ({\n ...attempt,\n step,\n }));\n }\n\n if (!carriedOverJob) return [];\n\n return [\n {\n id: `carried-over:${step.id}`,\n stepId: step.id,\n jobExecutionId: step.jobExecutionId,\n attempt: step.currentAttempt,\n executionOrder: step.position,\n status: step.status,\n exitCode: null,\n output: null,\n outputs: null,\n response: null,\n error: null,\n gateResult: null,\n restartFeedback: null,\n startedAt: step.createdAt,\n finishedAt: null,\n displayDuration: null,\n statusVisual: getStepStatusVisual(step.status),\n carriedOver: true,\n step,\n },\n ];\n}\n\nfunction compareSteps(left: Step, right: Step): number {\n return (\n left.position - right.position ||\n (left.name ?? '').localeCompare(right.name ?? '') ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction compareAttempts(left: Step['attempts'][number], right: Step['attempts'][number]) {\n return left.attempt - right.attempt || left.id.localeCompare(right.id);\n}\n\nfunction compareEntries(left: StepListEntryModel, right: StepListEntryModel): number {\n return (\n left.executionOrder - right.executionOrder ||\n left.step.index - right.step.index ||\n left.attempt - right.attempt ||\n left.id.localeCompare(right.id)\n );\n}\n\nfunction landingEntryId(entries: readonly StepListEntryModel[]): string | undefined {\n for (let index = entries.length - 1; index >= 0; index -= 1) {\n const entry = entries[index];\n if (entry?.statusVisual.kind === 'running') return entry.id;\n }\n return undefined;\n}\n\nfunction normalizeStatus(status: string): string {\n return status.trim().toLowerCase().replace(/-/g, '_');\n}\n"],"names":["getWorkflowStatusVisual","defaultJobExecution","isWorkflowStatus","isKnownWorkflowStatus","JobExecution","buildStepListModel","job","jobExecution","selectedJobExecution","defaultStepListJobExecution","steps","sort","compareSteps","map","toStepModel","entries","flatMap","step","toStepEntries","carriedOver","compareEntries","jobId","id","jobName","name","key","jobExecutionId","stepCount","length","activeEntryId","landingEntryId","emptyJobExecutionForJob","sequence","status","statusReason","statusReasonMessage","runner","outputs","triggerEvents","queuedAt","startedAt","finishedAt","timedOutAt","evaluationTrace","createdAt","updatedAt","getStepStatusVisual","normalized","normalizeStatus","visual","ripple","kind","label","humanizeStatus","dot","badge","words","trim","replace","toLowerCase","firstLetter","at","undefined","toUpperCase","slice","index","attempts","compareAttempts","attempt","displayDuration","statusVisual","stepLabel","carriedOverJob","stepId","currentAttempt","executionOrder","position","exitCode","output","response","error","gateResult","restartFeedback","left","right","localeCompare","entry"],"mappings":"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"}
|
|
@@ -24,6 +24,7 @@ export interface StepListProps {
|
|
|
24
24
|
selectedAttemptId?: string | null | undefined;
|
|
25
25
|
defaultSelectedAttemptId?: string | undefined;
|
|
26
26
|
onSelectedAttemptChange?: ((attemptId: string | undefined) => void) | undefined;
|
|
27
|
+
onExpandedAttemptIdsChange?: ((attemptIds: readonly string[]) => void) | undefined;
|
|
27
28
|
inspectorOpenAttemptId?: string | null | undefined;
|
|
28
29
|
onInspectorOpenChange?: ((attemptId: string | null) => void) | undefined;
|
|
29
30
|
autoSelectActiveAttempt?: boolean | undefined;
|
|
@@ -33,5 +34,5 @@ export interface StepListProps {
|
|
|
33
34
|
showHeader?: boolean | undefined;
|
|
34
35
|
className?: string | undefined;
|
|
35
36
|
}
|
|
36
|
-
export declare function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className, }: StepListProps): import("react").JSX.Element;
|
|
37
|
+
export declare function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, onExpandedAttemptIdsChange, inspectorOpenAttemptId, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className, }: StepListProps): import("react").JSX.Element;
|
|
37
38
|
//# sourceMappingURL=step-list.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-list.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,uBAAuB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChF,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnD,qBAAqB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzE,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,mBAAmB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IAC/E,eAAe,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACzE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,sBAA6B,EAC7B,qBAAqB,EACrB,uBAA+B,EAC/B,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,UAAiB,EACjB,SAAS,GACV,EAAE,aAAa,+
|
|
1
|
+
{"version":3,"file":"step-list.d.ts","sourceRoot":"","sources":["../../../src/components/step-list/step-list.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,IAAI,EACT,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9C,uBAAuB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAChF,0BAA0B,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnF,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnD,qBAAqB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzE,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,mBAAmB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IAC/E,eAAe,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;IACzE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,sBAA6B,EAC7B,qBAAqB,EACrB,uBAA+B,EAC/B,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,UAAiB,EACjB,SAAS,GACV,EAAE,aAAa,+BAyBf"}
|
|
@@ -13,7 +13,7 @@ import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-
|
|
|
13
13
|
import { isWorkflowStatus } from '#core/workflow-run.js';
|
|
14
14
|
import { formatJobExecutionTimeLabel } from '../job-graph/job-duration-format.js';
|
|
15
15
|
import { buildStepListModel, defaultStepListJobExecution, humanizeStatus } from './step-list-model.js';
|
|
16
|
-
export function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt = false, emptyState, renderExpandedStep, renderInspector, showHeader = true, className }) {
|
|
16
|
+
export function StepList({ job, jobExecution, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, onExpandedAttemptIdsChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt = false, emptyState, renderExpandedStep, renderInspector, showHeader = true, className }) {
|
|
17
17
|
const selectedJobExecution = jobExecution ?? defaultStepListJobExecution(job);
|
|
18
18
|
const model = useMemo(()=>buildStepListModel({
|
|
19
19
|
job,
|
|
@@ -27,6 +27,7 @@ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelected
|
|
|
27
27
|
selectedAttemptId: selectedAttemptId,
|
|
28
28
|
defaultSelectedAttemptId: defaultSelectedAttemptId,
|
|
29
29
|
onSelectedAttemptChange: onSelectedAttemptChange,
|
|
30
|
+
onExpandedAttemptIdsChange: onExpandedAttemptIdsChange,
|
|
30
31
|
inspectorOpenAttemptId: inspectorOpenAttemptId,
|
|
31
32
|
onInspectorOpenChange: onInspectorOpenChange,
|
|
32
33
|
autoSelectActiveAttempt: autoSelectActiveAttempt,
|
|
@@ -37,7 +38,7 @@ export function StepList({ job, jobExecution, selectedAttemptId, defaultSelected
|
|
|
37
38
|
className: className
|
|
38
39
|
}, model.jobExecutionId);
|
|
39
40
|
}
|
|
40
|
-
function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className }) {
|
|
41
|
+
function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, onSelectedAttemptChange, onExpandedAttemptIdsChange, inspectorOpenAttemptId = null, onInspectorOpenChange, autoSelectActiveAttempt, emptyState, renderExpandedStep, renderInspector, showHeader, className }) {
|
|
41
42
|
const titleId = useId();
|
|
42
43
|
const [localSelectedAttemptIds, setLocalSelectedAttemptIds] = useState(()=>selectedAttemptId ? [
|
|
43
44
|
selectedAttemptId
|
|
@@ -48,13 +49,29 @@ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, o
|
|
|
48
49
|
const lastNotifiedSelectedAttemptId = useRef(null);
|
|
49
50
|
const autoSelectedAttemptIdRef = useRef(undefined);
|
|
50
51
|
const previousSelectedAttemptIdRef = useRef(selectedAttemptId);
|
|
52
|
+
const lastReportedExpandedAttemptIdsKeyRef = useRef(null);
|
|
51
53
|
const shouldUseControlledCollapsedState = selectedAttemptId === null && lastNotifiedSelectedAttemptId.current === null;
|
|
52
54
|
const autoSelectedAttemptId = selectedAttemptId === undefined && autoSelectActiveAttempt && !userSelectedAttempt ? autoSelectedAttemptIdRef.current ?? model.activeEntryId : undefined;
|
|
53
|
-
const autoSelectedAttemptIds = autoSelectedAttemptId ? [
|
|
55
|
+
const autoSelectedAttemptIds = useMemo(()=>autoSelectedAttemptId ? [
|
|
56
|
+
autoSelectedAttemptId
|
|
57
|
+
] : [], [
|
|
54
58
|
autoSelectedAttemptId
|
|
55
|
-
]
|
|
56
|
-
const selectedAttemptIds = shouldUseControlledCollapsedState ? [] : localSelectedAttemptIds.length > 0 ? localSelectedAttemptIds : autoSelectedAttemptIds
|
|
59
|
+
]);
|
|
60
|
+
const selectedAttemptIds = useMemo(()=>shouldUseControlledCollapsedState ? [] : localSelectedAttemptIds.length > 0 ? localSelectedAttemptIds : autoSelectedAttemptIds, [
|
|
61
|
+
autoSelectedAttemptIds,
|
|
62
|
+
localSelectedAttemptIds,
|
|
63
|
+
shouldUseControlledCollapsedState
|
|
64
|
+
]);
|
|
57
65
|
const hasExpandedContent = renderExpandedStep !== undefined;
|
|
66
|
+
useEffect(()=>{
|
|
67
|
+
const key = selectedAttemptIds.join('|');
|
|
68
|
+
if (lastReportedExpandedAttemptIdsKeyRef.current === key) return;
|
|
69
|
+
lastReportedExpandedAttemptIdsKeyRef.current = key;
|
|
70
|
+
onExpandedAttemptIdsChange?.(selectedAttemptIds);
|
|
71
|
+
}, [
|
|
72
|
+
onExpandedAttemptIdsChange,
|
|
73
|
+
selectedAttemptIds
|
|
74
|
+
]);
|
|
58
75
|
useEffect(()=>{
|
|
59
76
|
if (selectedAttemptId !== undefined) {
|
|
60
77
|
previousSelectedAttemptIdRef.current = selectedAttemptId;
|
|
@@ -123,7 +140,7 @@ function StepListContent({ model, selectedAttemptId, defaultSelectedAttemptId, o
|
|
|
123
140
|
children: [
|
|
124
141
|
/*#__PURE__*/ _jsxs("section", {
|
|
125
142
|
"aria-labelledby": showHeader ? titleId : undefined,
|
|
126
|
-
className: cn('flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-
|
|
143
|
+
className: cn('flex min-h-0 flex-col rounded-8 border border-border-neutral-base bg-background-neutral-base', className),
|
|
127
144
|
children: [
|
|
128
145
|
showHeader ? /*#__PURE__*/ _jsx("div", {
|
|
129
146
|
className: "flex min-h-40 items-center border-b border-border-neutral-base px-row py-row",
|
|
@@ -302,7 +319,7 @@ function StepRow({ entry, selected, hasExpandedContent, isLast, onSelect, onInsp
|
|
|
302
319
|
const row = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
303
320
|
children: [
|
|
304
321
|
/*#__PURE__*/ _jsxs("div", {
|
|
305
|
-
className: cn('group flex min-w-0 items-center gap-inline pr-[8px] transition-colors hover:bg-background-
|
|
322
|
+
className: cn('group flex min-w-0 items-center gap-inline bg-background-neutral-base pr-[8px] transition-colors hover:bg-background-neutral-hover active:bg-background-neutral-pressed', selected && 'bg-background-neutral-hover', !hasExpandedContent && [
|
|
306
323
|
'border-b border-border-neutral-base',
|
|
307
324
|
isLast && 'border-b-0'
|
|
308
325
|
]),
|