@shipfox/client-workflows 21.0.0 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/dist/components/job-detail/job-detail-view.js +1 -2
  4. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  5. package/dist/components/job-detail/job-empty-states.js +2 -2
  6. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  7. package/dist/components/job-graph/job-graph-content.js +2 -2
  8. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  9. package/dist/components/step-list/step-list.js +1 -2
  10. package/dist/components/step-list/step-list.js.map +1 -1
  11. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  12. package/dist/components/workflow-run-list/workflow-run-list-content.js +13 -7
  13. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  14. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  15. package/dist/components/workflow-run-list/workflow-run-list-states.js +42 -37
  16. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  17. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  18. package/dist/components/workflow-run-list/workflow-run-row.js +14 -10
  19. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  20. package/dist/components/workflow-run-summary/workflow-run-summary.js +3 -3
  21. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  22. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  23. package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
  24. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  25. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  26. package/dist/components/workflow-run-view/workflow-run-view.js +68 -50
  27. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  28. package/dist/pages/project-workflows-page.js +170 -158
  29. package/dist/pages/project-workflows-page.js.map +1 -1
  30. package/dist/tsconfig.test.tsbuildinfo +1 -1
  31. package/package.json +8 -8
  32. package/src/components/job-detail/job-detail-view.tsx +1 -2
  33. package/src/components/job-detail/job-empty-states.tsx +2 -2
  34. package/src/components/job-graph/job-graph-content.tsx +2 -2
  35. package/src/components/step-list/step-list.tsx +1 -2
  36. package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
  37. package/src/components/workflow-run-list/workflow-run-list-states.tsx +41 -40
  38. package/src/components/workflow-run-list/workflow-run-row.tsx +11 -8
  39. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +1 -1
  40. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +9 -1
  41. package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
  42. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
  43. package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
  44. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +3 -1
  45. package/src/components/workflow-run-view/workflow-run-view.test.tsx +11 -8
  46. package/src/components/workflow-run-view/workflow-run-view.tsx +78 -70
  47. package/src/pages/project-workflows-page.tsx +134 -125
  48. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useNavigate} from '@tanstack/react-router';\nimport {type ReactNode, useEffect, useMemo} from 'react';\nimport {buildRunAnnotationList, type RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n type EvaluationTraceEntry,\n isWorkflowRunTerminal,\n type Job,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunsSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport type {JobGraphSelectionSource} from '../job-graph/types.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {\n type DerivedRunAnnotation,\n RunAnnotationList,\n RunAnnotationSummaryLine,\n} from '../workflow-run-tabs/index.js';\nimport {WorkflowSourceContent} from '../workflow-source-panel/index.js';\nimport {RunWorkspaceNav} from './run-workspace-nav.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunContentSkeleton,\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\nconst RUN_WORKSPACE_FRAME_CLASS_NAME = 'mx-auto w-full max-w-[calc(240px_+_1120px)]';\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n runAttempt?: number | undefined;\n selection?: WorkflowRunsSearch | undefined;\n tab?: WorkflowRunTab | undefined;\n activeJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n jobContent?: ReactNode | undefined;\n}\n\n/**\n * The persistent run workspace. Run-level sections and dedicated job routes share the same\n * header and job index, while the all-jobs Summary always opens on the dependency graph.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n selection,\n tab,\n activeJobId,\n jobSearch,\n jobContent,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({\n workflowRunId,\n runAttempt: selection?.runAttempt ?? runAttempt,\n });\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n // Annotations are read on their own cadence rather than hydrated into the run, so the two\n // modules stay decoupled. The key is shared, so the job page's count chip costs no extra fetch.\n //\n // Polling is scoped to the surface that renders bodies. The read has no counts-only mode, so\n // every poll transfers whole Markdown bodies; doing that every four seconds behind Summary,\n // Source, and the job log spends megabytes to keep one rail number warm. Off that surface the\n // counts still refresh on navigation and on window focus.\n const annotationsQuery = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: runQuery.data?.runAttempt.attempt,\n live:\n runWorkspaceSection(tab) === 'annotations' &&\n !activeJobId &&\n Boolean(runQuery.data) &&\n !isWorkflowRunTerminal(runQuery.data?.runAttempt.status ?? 'pending'),\n });\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n annotations={annotationsQuery}\n rerunMutation={rerunMutation}\n runAttempt={runAttempt}\n selection={selection}\n tab={tab}\n activeJobId={activeJobId}\n jobSearch={jobSearch}\n jobContent={jobContent}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n annotations,\n rerunMutation,\n runAttempt,\n selection,\n tab,\n activeJobId,\n jobSearch,\n jobContent,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n runAttempt: number | undefined;\n selection: WorkflowRunsSearch | undefined;\n tab: WorkflowRunTab | undefined;\n activeJobId: string | undefined;\n jobSearch: WorkflowJobSearch | undefined;\n jobContent: ReactNode | undefined;\n}) {\n const navigate = useNavigate();\n const runData = query.data;\n const annotationSummary = annotations.summary;\n const activeSection = runWorkspaceSection(tab);\n const cancelMutation = useCancelWorkflowRunMutation(runData);\n const sourceSnapshot = runData?.sourceSnapshot ?? null;\n const hasJobContent = Boolean(jobContent);\n const resolvedSelection =\n runData && selection ? resolveWorkflowRunSelection({run: runData, selection}) : undefined;\n const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;\n\n useEffect(() => {\n const hasLegacyJobSelection = Boolean(\n selection?.jobId ||\n selection?.jobExecutionId ||\n selection?.stepId ||\n selection?.stepAttemptId,\n );\n if (\n activeJobId ||\n activeSection !== 'summary' ||\n !hasLegacyJobSelection ||\n !resolvedSelection?.job ||\n !runData ||\n !workspaceSlug ||\n !projectSlug\n ) {\n return;\n }\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: runData.id,\n jobId: resolvedSelection.job.id,\n },\n search: workflowJobSearchParams({\n jobExecutionId: selection?.jobExecutionId,\n stepId: selection?.stepId,\n stepAttemptId: selection?.stepAttemptId,\n runAttempt: selection?.runAttempt,\n }) as never,\n replace: true,\n });\n }, [\n activeJobId,\n activeSection,\n navigate,\n projectSlug,\n resolvedSelection?.job,\n runData,\n selection,\n workspaceSlug,\n ]);\n\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!runData || !workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: run.id},\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectGraphJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {\n if (source !== 'pointer' || !jobId || !runData || !workspaceSlug || !projectSlug) return;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id, jobId},\n search: workflowJobSearchParams({\n runAttempt: selection?.runAttempt ?? runAttempt ?? runData.runAttempt.attempt,\n }) as never,\n });\n }\n\n function selectAnnotationJob(jobId: string | undefined) {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n if (jobId) nextSearch.jobId = jobId;\n else delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n });\n }\n\n function clearAnnotationFilters() {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n delete nextSearch.severity;\n delete nextSearch.annotation;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n replace: true,\n });\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => toast.error(cancelErrorMessage(error)),\n });\n }\n\n const fatalError = query.isError && runData === undefined;\n const loadingOrError = fatalError ? (\n query.error instanceof ApiError && query.error.status === 404 ? (\n <WorkflowRunNotFound />\n ) : (\n <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />\n )\n ) : query.isPending || runData === undefined ? (\n <WorkflowRunContentSkeleton />\n ) : null;\n\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {runData ? (\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n ) : (\n <WorkflowRunSkeleton />\n )}\n {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}\n\n <div\n data-run-workspace-layout\n className=\"flex min-h-0 min-w-0 flex-1 flex-col border-t border-border-neutral-base\"\n >\n <div\n data-run-workspace-frame\n className={cn(\n 'flex min-h-0 min-w-0 w-full flex-1 flex-col min-[768px]:flex-row',\n !hasJobContent && RUN_WORKSPACE_FRAME_CLASS_NAME,\n )}\n >\n {runData && workspaceSlug && projectSlug ? (\n <RunWorkspaceNav\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n activeSection={activeSection}\n currentJobId={activeJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n />\n ) : (\n <RunWorkspaceNavSkeleton />\n )}\n\n <div data-run-workspace-content className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {runData && jobContent ? (\n jobContent\n ) : runData ? (\n <RunSectionContent\n section={activeSection}\n run={runData}\n annotations={annotations}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selection?.jobId}\n onSelectGraphJob={selectGraphJob}\n onSelectAnnotationJob={selectAnnotationJob}\n onClearAnnotationFilters={clearAnnotationFilters}\n sourceSnapshot={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n />\n ) : (\n <div className=\"min-h-0 flex-1 overflow-auto p-panel\">{loadingOrError}</div>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction RunSectionContent({\n section,\n run,\n annotations,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectGraphJob,\n onSelectAnnotationJob,\n onClearAnnotationFilters,\n sourceSnapshot,\n highlightedLineRange,\n}: {\n section: RunWorkspaceSection;\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectGraphJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n onClearAnnotationFilters: () => void;\n sourceSnapshot: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['sourceSnapshot'];\n highlightedLineRange: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['jobs'][number]['jobExecutions'][number]['steps'][number]['sourceLocation'];\n}) {\n if (section === 'summary') {\n return (\n <section\n aria-label=\"All jobs summary\"\n className=\"min-h-0 flex-1 overflow-auto pb-panel pt-[16px]\"\n >\n <div className=\"mx-auto flex w-full flex-col gap-group px-frame\">\n <Text as=\"h2\" className=\"sr-only\">\n All jobs summary\n </Text>\n <JobGraph\n run={run}\n selectedJobId={selectedJobId}\n onSelectedJobChange={onSelectGraphJob}\n className=\"min-h-160 overflow-hidden\"\n />\n </div>\n </section>\n );\n }\n\n if (section === 'annotations') {\n return (\n <RunAnnotationsSection\n run={run}\n annotations={annotations}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selectedJobId}\n onSelectAnnotationJob={onSelectAnnotationJob}\n onClearAnnotationFilters={onClearAnnotationFilters}\n />\n );\n }\n\n return (\n <section\n aria-label=\"Workflow source\"\n className=\"min-h-0 flex-1 overflow-auto pb-panel pt-[16px]\"\n >\n <div className=\"mx-auto flex min-h-full w-full flex-col px-frame\">\n <Text as=\"h2\" className=\"sr-only\">\n Workflow source\n </Text>\n {sourceSnapshot ? (\n <WorkflowSourceContent\n source={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n ) : (\n <EmptyState\n className=\"min-h-160 w-full\"\n icon=\"fileDamageLine\"\n title=\"Source snapshot unavailable\"\n description={\n run.isTemporary\n ? 'Temporary runs do not capture workflow source.'\n : 'This run was created before workflow source snapshots were captured.'\n }\n />\n )}\n </div>\n </section>\n );\n}\n\nfunction RunAnnotationsSection({\n run,\n annotations,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectAnnotationJob,\n onClearAnnotationFilters,\n}: {\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n onClearAnnotationFilters: () => void;\n}) {\n const selectedJob = run.jobs.find((job) => job.id === selectedJobId);\n const severity = selection?.severity;\n const records = annotations.annotations;\n\n const entries = useMemo(\n () =>\n records\n ? buildRunAnnotationList({\n annotations: records,\n jobs: run.jobs,\n severity,\n jobId: selectedJob?.id,\n })\n : undefined,\n [records, run.jobs, selectedJob?.id, severity],\n );\n const derivedAnnotations = useMemo<readonly DerivedRunAnnotation[] | undefined>(() => {\n if (!records) return undefined;\n const jobsWithAnnotations = new Set(records.map((annotation) => annotation.jobId));\n\n return run.jobs\n .filter((job) => {\n const style = job.status === 'failed' ? 'error' : 'warning';\n return (\n (!selectedJob || selectedJob.id === job.id) &&\n !jobsWithAnnotations.has(job.id) &&\n (job.status === 'failed' || job.status === 'skipped') &&\n job.jobExecutions.length === 0 &&\n matchesDerivedAnnotationFilters(style, selection)\n );\n })\n .map((job) => ({\n id: `derived-${job.id}`,\n style: job.status === 'failed' ? 'error' : 'warning',\n body: derivedJobAnnotation(job),\n }));\n }, [records, run.jobs, selectedJob, selection]);\n const hasSynthesizedJobAnnotations = run.jobs.some(\n (job) =>\n (job.status === 'failed' || job.status === 'skipped') && job.jobExecutions.length === 0,\n );\n\n return (\n <section\n aria-label=\"Run annotations\"\n className=\"min-h-0 flex-1 overflow-auto pb-panel pt-[16px]\"\n >\n <div className=\"mx-auto flex w-full flex-col gap-group px-frame\">\n <Text as=\"h2\" className=\"sr-only\">\n Annotations\n </Text>\n <div className=\"flex flex-wrap items-center justify-between gap-inline\">\n <RunAnnotationSummaryLine\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n search={selection}\n />\n <AnnotationJobFilter\n jobs={run.jobs}\n selectedJobId={selectedJob?.id}\n onSelect={onSelectAnnotationJob}\n />\n </div>\n <RunAnnotationList\n // Remounting on a filter or run-attempt change resets the render window, so the next\n // list never inherits a \"show more\" position from different data.\n key={`${run.id}:${run.runAttempt.attempt}:${severity ?? 'all'}:${selectedJob?.id ?? 'all'}`}\n query={annotations.query}\n entries={entries}\n derivedAnnotations={derivedAnnotations}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n // A run with no annotations at all offers no filter to clear, whatever the URL says.\n filtered={Boolean(\n (severity || selectedJob || selection?.annotation) &&\n ((annotationSummary?.total ?? 0) > 0 || hasSynthesizedJobAnnotations),\n )}\n filteredJobName={selectedJob?.displayName}\n filteredSeverity={severity}\n onClearFilters={onClearAnnotationFilters}\n selectedAnnotationId={selection?.annotation}\n />\n </div>\n </section>\n );\n}\n\nfunction matchesDerivedAnnotationFilters(\n style: 'warning' | 'error',\n selection: WorkflowRunsSearch | undefined,\n): boolean {\n if (selection?.severity && style !== selection.severity) return false;\n // A selected annotation is a deep-link to a real annotation record. Synthetic diagnostics\n // have no id or context, so they must not remain visible behind that selection.\n return !selection?.annotation;\n}\n\nfunction derivedJobAnnotation(job: Job): string {\n const reason = job.statusReason ? `Reason: \\`${job.statusReason}\\`` : null;\n const traceSummary = formatConditionEvaluation(job.evaluationTrace);\n const details = [reason, traceSummary].filter(Boolean).join('\\n');\n\n if (job.status === 'skipped') {\n return [\n `**${job.displayName}** was skipped before an execution was created.`,\n '',\n 'Review its dependencies or condition before re-running.',\n details,\n ]\n .filter(Boolean)\n .join('\\n');\n }\n return [\n `**${job.displayName}** failed before an execution was created.`,\n '',\n 'Check runner availability and workflow configuration before re-running.',\n details,\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction formatConditionEvaluation(\n trace: readonly EvaluationTraceEntry[] | null | undefined,\n): string | null {\n if (!trace?.length) return null;\n\n return [\n 'Condition evaluation:',\n ...trace.map((entry) => {\n if ('dropped' in entry) {\n return `- ${entry.dropped} additional evaluation${entry.dropped === 1 ? '' : 's'} not recorded`;\n }\n const value =\n entry.value === undefined || entry.value === '' ? '(empty)' : `\\`${entry.value}\\``;\n return `- \\`${entry.field}\\` evaluated \\`${entry.expression}\\` to ${value}${entry.degraded ? ' (degraded)' : ''}`;\n }),\n ].join('\\n');\n}\n\nconst ALL_ANNOTATION_JOBS = 'all-jobs';\n\nfunction AnnotationJobFilter({\n jobs,\n selectedJobId,\n onSelect,\n}: {\n jobs: Job[];\n selectedJobId: string | undefined;\n onSelect: (jobId: string | undefined) => void;\n}) {\n const sortedJobs = [...jobs].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n\n return (\n <Select\n value={selectedJobId ?? ALL_ANNOTATION_JOBS}\n onValueChange={(value) => onSelect(value === ALL_ANNOTATION_JOBS ? undefined : value)}\n >\n <SelectTrigger\n size=\"small\"\n aria-label=\"Filter annotations by job\"\n className=\"w-full min-[480px]:w-240\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent align=\"end\">\n <SelectItem value={ALL_ANNOTATION_JOBS}>All jobs</SelectItem>\n {sortedJobs.map((job) => (\n <SelectItem key={job.id} value={job.id}>\n {job.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction RunWorkspaceNavSkeleton() {\n return (\n <aside\n aria-label=\"Loading run navigation\"\n className=\"hidden w-240 shrink-0 border-r border-border-neutral-base p-panel-compact min-[768px]:block\"\n >\n <div className=\"flex flex-col gap-group\">\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"flex flex-col gap-inline\">\n <div className=\"h-16 w-64 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n </div>\n </div>\n </aside>\n );\n}\n\nfunction runWorkspaceSection(tab: WorkflowRunTab | undefined): RunWorkspaceSection {\n return tab === 'annotations' || tab === 'source' ? tab : 'summary';\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","RelativeTimeProvider","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","toast","Text","cn","useNavigate","useEffect","useMemo","buildRunAnnotationList","isWorkflowRunTerminal","withoutWorkflowRunSelectionSearch","useRunAnnotationsQuery","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","workflowJobSearchParams","workflowRunSearchParams","JobGraph","WorkflowRunSummary","RunAnnotationList","RunAnnotationSummaryLine","WorkflowSourceContent","RunWorkspaceNav","resolveWorkflowRunSelection","WorkflowRunContentSkeleton","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","RUN_WORKSPACE_FRAME_CLASS_NAME","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","runAttempt","selection","tab","activeJobId","jobSearch","jobContent","runQuery","rerunMutation","annotationsQuery","data","attempt","live","runWorkspaceSection","Boolean","status","div","className","RunViewContent","query","annotations","navigate","runData","annotationSummary","summary","activeSection","cancelMutation","sourceSnapshot","hasJobContent","resolvedSelection","run","undefined","highlightedLineRange","step","sourceLocation","hasLegacyJobSelection","jobId","jobExecutionId","stepId","stepAttemptId","job","to","params","id","search","replace","rerun","mode","error","mutateAsync","success","previous","message","selectGraphJob","source","selectAnnotationJob","nextSearch","clearAnnotationFilters","severity","annotation","cancelRun","mutate","onError","cancelErrorMessage","fatalError","isError","loadingOrError","subject","icon","isPending","cancelling","onCancel","rerunPending","onRerun","latestAttempt","data-run-workspace-layout","data-run-workspace-frame","currentJobId","RunWorkspaceNavSkeleton","data-run-workspace-content","RunSectionContent","section","selectedJobId","onSelectGraphJob","onSelectAnnotationJob","onClearAnnotationFilters","aria-label","as","onSelectedJobChange","RunAnnotationsSection","scrollHighlightedIntoView","title","description","isTemporary","selectedJob","jobs","find","records","entries","derivedAnnotations","jobsWithAnnotations","Set","map","filter","style","has","jobExecutions","length","matchesDerivedAnnotationFilters","body","derivedJobAnnotation","hasSynthesizedJobAnnotations","some","AnnotationJobFilter","onSelect","filtered","total","filteredJobName","displayName","filteredSeverity","onClearFilters","selectedAnnotationId","reason","statusReason","traceSummary","formatConditionEvaluation","evaluationTrace","details","join","trace","entry","dropped","value","field","expression","degraded","ALL_ANNOTATION_JOBS","sortedJobs","sort","left","right","position","localeCompare","onValueChange","size","align","aside","code"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Panel, PanelBody, PanelHeader} from '@shipfox/react-ui/panel';\nimport {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@shipfox/react-ui/select';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useNavigate} from '@tanstack/react-router';\nimport {type ReactNode, useEffect, useMemo} from 'react';\nimport {buildRunAnnotationList, type RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n type EvaluationTraceEntry,\n isWorkflowRunTerminal,\n type Job,\n type WorkflowRunRerunMode,\n} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {useRunAnnotationsQuery} from '#hooks/api/run-annotations.js';\nimport {\n useCancelWorkflowRunMutation,\n useRerunWorkflowRunMutation,\n useWorkflowRunAttemptQuery,\n} from '#hooks/api/workflow-runs.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunsSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobGraph} from '../job-graph/index.js';\nimport type {JobGraphSelectionSource} from '../job-graph/types.js';\nimport {WorkflowRunSummary} from '../workflow-run-summary/index.js';\nimport {\n type DerivedRunAnnotation,\n RunAnnotationList,\n RunAnnotationSummaryLine,\n} from '../workflow-run-tabs/index.js';\nimport {WorkflowSourceContent} from '../workflow-source-panel/index.js';\nimport {RunWorkspaceNav} from './run-workspace-nav.js';\nimport {resolveWorkflowRunSelection} from './workflow-run-selection.js';\nimport {\n WorkflowRunContentSkeleton,\n WorkflowRunNotFound,\n WorkflowRunSkeleton,\n WorkflowRunStaleError,\n} from './workflow-run-states.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface WorkflowRunViewProps {\n projectId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n runAttempt?: number | undefined;\n selection?: WorkflowRunsSearch | undefined;\n tab?: WorkflowRunTab | undefined;\n activeJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n jobContent?: ReactNode | undefined;\n}\n\n/**\n * The persistent run workspace. Run-level sections and dedicated job routes share the same\n * header and job index, while the all-jobs Summary always opens on the dependency graph.\n */\nexport function WorkflowRunView({\n projectId,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n runAttempt,\n selection,\n tab,\n activeJobId,\n jobSearch,\n jobContent,\n}: WorkflowRunViewProps) {\n const runQuery = useWorkflowRunAttemptQuery({\n workflowRunId,\n runAttempt: selection?.runAttempt ?? runAttempt,\n });\n const rerunMutation = useRerunWorkflowRunMutation(projectId);\n // Annotations are read on their own cadence rather than hydrated into the run, so the two\n // modules stay decoupled. The key is shared, so the job page's count chip costs no extra fetch.\n //\n // Polling is scoped to the surface that renders bodies. The read has no counts-only mode, so\n // every poll transfers whole Markdown bodies; doing that every four seconds behind Summary,\n // Source, and the job log spends megabytes to keep one rail number warm. Off that surface the\n // counts still refresh on navigation and on window focus.\n const annotationsQuery = useRunAnnotationsQuery({\n workflowRunId,\n runAttempt: runQuery.data?.runAttempt.attempt,\n live:\n runWorkspaceSection(tab) === 'annotations' &&\n !activeJobId &&\n Boolean(runQuery.data) &&\n !isWorkflowRunTerminal(runQuery.data?.runAttempt.status ?? 'pending'),\n });\n\n return (\n <RelativeTimeProvider>\n <div className=\"flex min-h-0 min-w-0 flex-1 overflow-hidden\">\n <RunViewContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n query={runQuery}\n annotations={annotationsQuery}\n rerunMutation={rerunMutation}\n runAttempt={runAttempt}\n selection={selection}\n tab={tab}\n activeJobId={activeJobId}\n jobSearch={jobSearch}\n jobContent={jobContent}\n />\n </div>\n </RelativeTimeProvider>\n );\n}\n\nfunction RunViewContent({\n workspaceSlug,\n projectSlug,\n query,\n annotations,\n rerunMutation,\n runAttempt,\n selection,\n tab,\n activeJobId,\n jobSearch,\n jobContent,\n}: {\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n query: ReturnType<typeof useWorkflowRunAttemptQuery>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n rerunMutation: ReturnType<typeof useRerunWorkflowRunMutation>;\n runAttempt: number | undefined;\n selection: WorkflowRunsSearch | undefined;\n tab: WorkflowRunTab | undefined;\n activeJobId: string | undefined;\n jobSearch: WorkflowJobSearch | undefined;\n jobContent: ReactNode | undefined;\n}) {\n const navigate = useNavigate();\n const runData = query.data;\n const annotationSummary = annotations.summary;\n const activeSection = runWorkspaceSection(tab);\n const cancelMutation = useCancelWorkflowRunMutation(runData);\n const sourceSnapshot = runData?.sourceSnapshot ?? null;\n const resolvedSelection =\n runData && selection ? resolveWorkflowRunSelection({run: runData, selection}) : undefined;\n const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;\n\n useEffect(() => {\n const hasLegacyJobSelection = Boolean(\n selection?.jobId ||\n selection?.jobExecutionId ||\n selection?.stepId ||\n selection?.stepAttemptId,\n );\n if (\n activeJobId ||\n activeSection !== 'summary' ||\n !hasLegacyJobSelection ||\n !resolvedSelection?.job ||\n !runData ||\n !workspaceSlug ||\n !projectSlug\n ) {\n return;\n }\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {\n workspaceSlug,\n projectSlug,\n workflowRunId: runData.id,\n jobId: resolvedSelection.job.id,\n },\n search: workflowJobSearchParams({\n jobExecutionId: selection?.jobExecutionId,\n stepId: selection?.stepId,\n stepAttemptId: selection?.stepAttemptId,\n runAttempt: selection?.runAttempt,\n }) as never,\n replace: true,\n });\n }, [\n activeJobId,\n activeSection,\n navigate,\n projectSlug,\n resolvedSelection?.job,\n runData,\n selection,\n workspaceSlug,\n ]);\n\n async function rerun(mode: WorkflowRunRerunMode) {\n if (!runData || !workspaceSlug || !projectSlug) {\n toast.error('Could not start re-run from this route.');\n return;\n }\n try {\n const run = await rerunMutation.mutateAsync({workflowRunId: runData.id, mode});\n toast.success('Re-run started');\n await navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: run.id},\n search: ((previous: Record<string, unknown>) =>\n withoutWorkflowRunSelectionSearch(previous)) as never,\n });\n } catch (error) {\n toast.error(error instanceof ApiError ? error.message : 'Could not start re-run');\n }\n }\n\n function selectGraphJob(jobId: string | undefined, source: JobGraphSelectionSource = 'pointer') {\n if (source !== 'pointer' || !jobId || !runData || !workspaceSlug || !projectSlug) return;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id, jobId},\n search: workflowJobSearchParams({\n runAttempt: selection?.runAttempt ?? runAttempt ?? runData.runAttempt.attempt,\n }) as never,\n });\n }\n\n function selectAnnotationJob(jobId: string | undefined) {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n if (jobId) nextSearch.jobId = jobId;\n else delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n });\n }\n\n function clearAnnotationFilters() {\n if (!runData || !workspaceSlug || !projectSlug) return;\n const nextSearch: WorkflowRunsSearch = {...selection, tab: 'annotations'};\n delete nextSearch.jobId;\n delete nextSearch.jobExecutionId;\n delete nextSearch.stepId;\n delete nextSearch.stepAttemptId;\n delete nextSearch.severity;\n delete nextSearch.annotation;\n\n void navigate({\n to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',\n params: {workspaceSlug, projectSlug, workflowRunId: runData.id},\n search: workflowRunSearchParams(nextSearch, nextSearch) as never,\n replace: true,\n });\n }\n\n function cancelRun() {\n cancelMutation.mutate(undefined, {\n onError: (error) => toast.error(cancelErrorMessage(error)),\n });\n }\n\n const fatalError = query.isError && runData === undefined;\n const loadingOrError = fatalError ? (\n query.error instanceof ApiError && query.error.status === 404 ? (\n <WorkflowRunNotFound />\n ) : (\n <QueryLoadError query={query} subject=\"workflow run\" icon=\"pulseLine\" />\n )\n ) : query.isPending || runData === undefined ? (\n <WorkflowRunContentSkeleton />\n ) : null;\n\n return (\n <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {runData ? (\n <WorkflowRunSummary\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n cancelling={cancelMutation.isPending}\n onCancel={cancelRun}\n rerunPending={rerunMutation.isPending}\n onRerun={(mode) => void rerun(mode)}\n latestAttempt={runData.latestAttempt}\n />\n ) : (\n <WorkflowRunSkeleton />\n )}\n {runData && query.isError ? <WorkflowRunStaleError query={query} /> : null}\n\n <div\n data-run-workspace-layout\n className=\"flex min-h-0 min-w-0 flex-1 flex-col border-t border-border-neutral-base\"\n >\n <div\n data-run-workspace-frame\n className=\"flex min-h-0 min-w-0 w-full flex-1 flex-col min-[768px]:flex-row\"\n >\n {runData && workspaceSlug && projectSlug ? (\n <RunWorkspaceNav\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={runData}\n activeSection={activeSection}\n currentJobId={activeJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n />\n ) : (\n <RunWorkspaceNavSkeleton />\n )}\n\n <div data-run-workspace-content className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {runData && jobContent ? (\n jobContent\n ) : runData ? (\n <RunSectionContent\n section={activeSection}\n run={runData}\n annotations={annotations}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selection?.jobId}\n onSelectGraphJob={selectGraphJob}\n onSelectAnnotationJob={selectAnnotationJob}\n onClearAnnotationFilters={clearAnnotationFilters}\n sourceSnapshot={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n />\n ) : (\n <div className=\"min-h-0 flex-1 overflow-auto p-panel\">{loadingOrError}</div>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction RunSectionContent({\n section,\n run,\n annotations,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectGraphJob,\n onSelectAnnotationJob,\n onClearAnnotationFilters,\n sourceSnapshot,\n highlightedLineRange,\n}: {\n section: RunWorkspaceSection;\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectGraphJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n onClearAnnotationFilters: () => void;\n sourceSnapshot: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['sourceSnapshot'];\n highlightedLineRange: NonNullable<\n ReturnType<typeof useWorkflowRunAttemptQuery>['data']\n >['jobs'][number]['jobExecutions'][number]['steps'][number]['sourceLocation'];\n}) {\n if (section === 'summary') {\n return (\n <section\n aria-label=\"All jobs summary\"\n className=\"min-h-0 flex-1 overflow-auto pb-panel pt-[16px]\"\n >\n <div className=\"flex w-full flex-col gap-group\">\n <Text as=\"h2\" className=\"sr-only\">\n All jobs summary\n </Text>\n <Panel className=\"min-h-160\">\n <PanelBody className=\"min-h-160 bg-background-components-base p-0\">\n <JobGraph\n run={run}\n selectedJobId={selectedJobId}\n onSelectedJobChange={onSelectGraphJob}\n className=\"min-h-160 overflow-hidden\"\n />\n </PanelBody>\n </Panel>\n </div>\n </section>\n );\n }\n\n if (section === 'annotations') {\n return (\n <RunAnnotationsSection\n run={run}\n annotations={annotations}\n annotationSummary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n selection={selection}\n selectedJobId={selectedJobId}\n onSelectAnnotationJob={onSelectAnnotationJob}\n onClearAnnotationFilters={onClearAnnotationFilters}\n />\n );\n }\n\n return (\n <section\n aria-label=\"Workflow source\"\n className=\"min-h-0 flex-1 overflow-auto pb-panel pt-[16px]\"\n >\n <div className=\"flex min-h-full w-full flex-col\">\n <Text as=\"h2\" className=\"sr-only\">\n Workflow source\n </Text>\n <Panel className=\"min-h-160 flex-1\">\n <PanelBody className=\"min-h-160 flex-1 p-0\">\n {sourceSnapshot ? (\n <WorkflowSourceContent\n source={sourceSnapshot}\n highlightedLineRange={highlightedLineRange}\n scrollHighlightedIntoView\n />\n ) : (\n <EmptyState\n variant=\"panel\"\n className=\"min-h-160\"\n icon=\"fileDamageLine\"\n title=\"Source snapshot unavailable\"\n description={\n run.isTemporary\n ? 'Temporary runs do not capture workflow source.'\n : 'This run was created before workflow source snapshots were captured.'\n }\n />\n )}\n </PanelBody>\n </Panel>\n </div>\n </section>\n );\n}\n\nfunction RunAnnotationsSection({\n run,\n annotations,\n annotationSummary,\n workspaceSlug,\n projectSlug,\n selection,\n selectedJobId,\n onSelectAnnotationJob,\n onClearAnnotationFilters,\n}: {\n run: NonNullable<ReturnType<typeof useWorkflowRunAttemptQuery>['data']>;\n annotations: ReturnType<typeof useRunAnnotationsQuery>;\n annotationSummary: RunAnnotationSummary | undefined;\n workspaceSlug: string | undefined;\n projectSlug: string | undefined;\n selection: WorkflowRunsSearch | undefined;\n selectedJobId: string | undefined;\n onSelectAnnotationJob: (jobId: string | undefined) => void;\n onClearAnnotationFilters: () => void;\n}) {\n const selectedJob = run.jobs.find((job) => job.id === selectedJobId);\n const severity = selection?.severity;\n const records = annotations.annotations;\n\n const entries = useMemo(\n () =>\n records\n ? buildRunAnnotationList({\n annotations: records,\n jobs: run.jobs,\n severity,\n jobId: selectedJob?.id,\n })\n : undefined,\n [records, run.jobs, selectedJob?.id, severity],\n );\n const derivedAnnotations = useMemo<readonly DerivedRunAnnotation[] | undefined>(() => {\n if (!records) return undefined;\n const jobsWithAnnotations = new Set(records.map((annotation) => annotation.jobId));\n\n return run.jobs\n .filter((job) => {\n const style = job.status === 'failed' ? 'error' : 'warning';\n return (\n (!selectedJob || selectedJob.id === job.id) &&\n !jobsWithAnnotations.has(job.id) &&\n (job.status === 'failed' || job.status === 'skipped') &&\n job.jobExecutions.length === 0 &&\n matchesDerivedAnnotationFilters(style, selection)\n );\n })\n .map((job) => ({\n id: `derived-${job.id}`,\n style: job.status === 'failed' ? 'error' : 'warning',\n body: derivedJobAnnotation(job),\n }));\n }, [records, run.jobs, selectedJob, selection]);\n const hasSynthesizedJobAnnotations = run.jobs.some(\n (job) =>\n (job.status === 'failed' || job.status === 'skipped') && job.jobExecutions.length === 0,\n );\n\n return (\n <section\n aria-label=\"Run annotations\"\n className=\"min-h-0 flex-1 overflow-auto pb-panel pt-[16px]\"\n >\n <div className=\"flex w-full flex-col\">\n <Text as=\"h2\" className=\"sr-only\">\n Annotations\n </Text>\n <Panel>\n <PanelHeader className=\"flex-wrap\">\n <RunAnnotationSummaryLine\n summary={annotationSummary}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n search={selection}\n />\n <AnnotationJobFilter\n jobs={run.jobs}\n selectedJobId={selectedJob?.id}\n onSelect={onSelectAnnotationJob}\n />\n </PanelHeader>\n <PanelBody className=\"gap-group p-panel\">\n <RunAnnotationList\n // Remounting on a filter or run-attempt change resets the render window, so the next\n // list never inherits a \"show more\" position from different data.\n key={`${run.id}:${run.runAttempt.attempt}:${severity ?? 'all'}:${selectedJob?.id ?? 'all'}`}\n query={annotations.query}\n entries={entries}\n derivedAnnotations={derivedAnnotations}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={run.id}\n runAttempt={run.runAttempt.attempt}\n // A run with no annotations at all offers no filter to clear, whatever the URL says.\n filtered={Boolean(\n (severity || selectedJob || selection?.annotation) &&\n ((annotationSummary?.total ?? 0) > 0 || hasSynthesizedJobAnnotations),\n )}\n filteredJobName={selectedJob?.displayName}\n filteredSeverity={severity}\n onClearFilters={onClearAnnotationFilters}\n selectedAnnotationId={selection?.annotation}\n />\n </PanelBody>\n </Panel>\n </div>\n </section>\n );\n}\n\nfunction matchesDerivedAnnotationFilters(\n style: 'warning' | 'error',\n selection: WorkflowRunsSearch | undefined,\n): boolean {\n if (selection?.severity && style !== selection.severity) return false;\n // A selected annotation is a deep-link to a real annotation record. Synthetic diagnostics\n // have no id or context, so they must not remain visible behind that selection.\n return !selection?.annotation;\n}\n\nfunction derivedJobAnnotation(job: Job): string {\n const reason = job.statusReason ? `Reason: \\`${job.statusReason}\\`` : null;\n const traceSummary = formatConditionEvaluation(job.evaluationTrace);\n const details = [reason, traceSummary].filter(Boolean).join('\\n');\n\n if (job.status === 'skipped') {\n return [\n `**${job.displayName}** was skipped before an execution was created.`,\n '',\n 'Review its dependencies or condition before re-running.',\n details,\n ]\n .filter(Boolean)\n .join('\\n');\n }\n return [\n `**${job.displayName}** failed before an execution was created.`,\n '',\n 'Check runner availability and workflow configuration before re-running.',\n details,\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction formatConditionEvaluation(\n trace: readonly EvaluationTraceEntry[] | null | undefined,\n): string | null {\n if (!trace?.length) return null;\n\n return [\n 'Condition evaluation:',\n ...trace.map((entry) => {\n if ('dropped' in entry) {\n return `- ${entry.dropped} additional evaluation${entry.dropped === 1 ? '' : 's'} not recorded`;\n }\n const value =\n entry.value === undefined || entry.value === '' ? '(empty)' : `\\`${entry.value}\\``;\n return `- \\`${entry.field}\\` evaluated \\`${entry.expression}\\` to ${value}${entry.degraded ? ' (degraded)' : ''}`;\n }),\n ].join('\\n');\n}\n\nconst ALL_ANNOTATION_JOBS = 'all-jobs';\n\nfunction AnnotationJobFilter({\n jobs,\n selectedJobId,\n onSelect,\n}: {\n jobs: Job[];\n selectedJobId: string | undefined;\n onSelect: (jobId: string | undefined) => void;\n}) {\n const sortedJobs = [...jobs].sort(\n (left, right) => left.position - right.position || left.id.localeCompare(right.id),\n );\n\n return (\n <Select\n value={selectedJobId ?? ALL_ANNOTATION_JOBS}\n onValueChange={(value) => onSelect(value === ALL_ANNOTATION_JOBS ? undefined : value)}\n >\n <SelectTrigger\n size=\"small\"\n aria-label=\"Filter annotations by job\"\n className=\"w-full min-[480px]:w-240\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent align=\"end\">\n <SelectItem value={ALL_ANNOTATION_JOBS}>All jobs</SelectItem>\n {sortedJobs.map((job) => (\n <SelectItem key={job.id} value={job.id}>\n {job.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n}\n\nfunction RunWorkspaceNavSkeleton() {\n return (\n <aside\n aria-label=\"Loading run navigation\"\n className=\"hidden w-240 shrink-0 p-panel-compact min-[768px]:block\"\n >\n <div className=\"flex flex-col gap-group\">\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"flex flex-col gap-inline\">\n <div className=\"h-16 w-64 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n <div className=\"h-32 rounded-4 bg-background-components-subtle\" />\n </div>\n </div>\n </aside>\n );\n}\n\nfunction runWorkspaceSection(tab: WorkflowRunTab | undefined): RunWorkspaceSection {\n return tab === 'annotations' || tab === 'source' ? tab : 'summary';\n}\n\nfunction cancelErrorMessage(error: unknown): string {\n if (error instanceof ApiError && error.code === 'run-already-finished') {\n return 'This workflow run has already finished.';\n }\n return 'Could not cancel workflow run.';\n}\n"],"names":["ApiError","QueryLoadError","EmptyState","Panel","PanelBody","PanelHeader","RelativeTimeProvider","Select","SelectContent","SelectItem","SelectTrigger","SelectValue","toast","Text","useNavigate","useEffect","useMemo","buildRunAnnotationList","isWorkflowRunTerminal","withoutWorkflowRunSelectionSearch","useRunAnnotationsQuery","useCancelWorkflowRunMutation","useRerunWorkflowRunMutation","useWorkflowRunAttemptQuery","workflowJobSearchParams","workflowRunSearchParams","JobGraph","WorkflowRunSummary","RunAnnotationList","RunAnnotationSummaryLine","WorkflowSourceContent","RunWorkspaceNav","resolveWorkflowRunSelection","WorkflowRunContentSkeleton","WorkflowRunNotFound","WorkflowRunSkeleton","WorkflowRunStaleError","WorkflowRunView","projectId","workspaceSlug","projectSlug","workflowRunId","runAttempt","selection","tab","activeJobId","jobSearch","jobContent","runQuery","rerunMutation","annotationsQuery","data","attempt","live","runWorkspaceSection","Boolean","status","div","className","RunViewContent","query","annotations","navigate","runData","annotationSummary","summary","activeSection","cancelMutation","sourceSnapshot","resolvedSelection","run","undefined","highlightedLineRange","step","sourceLocation","hasLegacyJobSelection","jobId","jobExecutionId","stepId","stepAttemptId","job","to","params","id","search","replace","rerun","mode","error","mutateAsync","success","previous","message","selectGraphJob","source","selectAnnotationJob","nextSearch","clearAnnotationFilters","severity","annotation","cancelRun","mutate","onError","cancelErrorMessage","fatalError","isError","loadingOrError","subject","icon","isPending","cancelling","onCancel","rerunPending","onRerun","latestAttempt","data-run-workspace-layout","data-run-workspace-frame","currentJobId","RunWorkspaceNavSkeleton","data-run-workspace-content","RunSectionContent","section","selectedJobId","onSelectGraphJob","onSelectAnnotationJob","onClearAnnotationFilters","aria-label","as","onSelectedJobChange","RunAnnotationsSection","scrollHighlightedIntoView","variant","title","description","isTemporary","selectedJob","jobs","find","records","entries","derivedAnnotations","jobsWithAnnotations","Set","map","filter","style","has","jobExecutions","length","matchesDerivedAnnotationFilters","body","derivedJobAnnotation","hasSynthesizedJobAnnotations","some","AnnotationJobFilter","onSelect","filtered","total","filteredJobName","displayName","filteredSeverity","onClearFilters","selectedAnnotationId","reason","statusReason","traceSummary","formatConditionEvaluation","evaluationTrace","details","join","trace","entry","dropped","value","field","expression","degraded","ALL_ANNOTATION_JOBS","sortedJobs","sort","left","right","position","localeCompare","onValueChange","size","align","aside","code"],"mappings":"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"}
@@ -129,198 +129,209 @@ function ProjectWorkflowsPageInner({ projectId }) {
129
129
  }
130
130
  function WorkflowDefinitionsList({ definitions, isPending, isError, sync, runError, runningDefinitionId, hasNextPage, isFetchingNextPage, isFetchNextPageError, onRetry, onLoadMore, onOpenDefinition, onRun }) {
131
131
  if (isPending) {
132
- return /*#__PURE__*/ _jsxs("div", {
133
- className: "flex flex-col gap-inline",
132
+ return /*#__PURE__*/ _jsxs(Panel, {
133
+ role: "status",
134
+ "aria-label": "Loading workflows",
135
+ className: "divide-y",
134
136
  children: [
135
137
  /*#__PURE__*/ _jsx(Skeleton, {
136
- className: "h-40 w-full"
138
+ className: "h-44 w-full rounded-none"
137
139
  }),
138
140
  /*#__PURE__*/ _jsx(Skeleton, {
139
- className: "h-40 w-full"
141
+ className: "h-44 w-full rounded-none"
140
142
  }),
141
143
  /*#__PURE__*/ _jsx(Skeleton, {
142
- className: "h-40 w-full"
144
+ className: "h-44 w-full rounded-none"
143
145
  })
144
146
  ]
145
147
  });
146
148
  }
147
149
  if (isError && definitions.length === 0) {
148
- return /*#__PURE__*/ _jsx(LoadErrorState, {
149
- title: "Couldn't load workflows",
150
- description: "Definitions could not be loaded. Source metadata remains visible.",
151
- onRetry: onRetry,
152
- retryLabel: "Retry loading workflows"
150
+ return /*#__PURE__*/ _jsx(Panel, {
151
+ children: /*#__PURE__*/ _jsx(LoadErrorState, {
152
+ title: "Couldn't load workflows",
153
+ description: "Definitions could not be loaded. Source metadata remains visible.",
154
+ onRetry: onRetry,
155
+ retryLabel: "Retry loading workflows",
156
+ variant: "panel"
157
+ })
153
158
  });
154
159
  }
155
160
  if (definitions.length === 0) {
156
- return /*#__PURE__*/ _jsx(WorkflowEmptyState, {
157
- sync: sync
161
+ return /*#__PURE__*/ _jsx(Panel, {
162
+ children: /*#__PURE__*/ _jsx(WorkflowEmptyState, {
163
+ sync: sync
164
+ })
158
165
  });
159
166
  }
160
167
  return /*#__PURE__*/ _jsxs(_Fragment, {
161
168
  children: [
162
- /*#__PURE__*/ _jsx(Panel, {
163
- className: "hidden md:flex",
164
- children: /*#__PURE__*/ _jsxs(Table, {
165
- children: [
166
- /*#__PURE__*/ _jsx(TableHeader, {
167
- children: /*#__PURE__*/ _jsxs(TableRow, {
168
- children: [
169
- /*#__PURE__*/ _jsx(TableHead, {
170
- className: "w-40"
171
- }),
172
- /*#__PURE__*/ _jsx(TableHead, {
173
- children: "Workflow"
174
- }),
175
- /*#__PURE__*/ _jsx(TableHead, {
176
- className: "w-180",
177
- children: "Updated"
178
- }),
179
- /*#__PURE__*/ _jsx(TableHead, {
180
- className: "w-80 text-right"
181
- })
182
- ]
183
- })
184
- }),
185
- /*#__PURE__*/ _jsx(TableBody, {
186
- children: definitions.map((definition)=>{
187
- const runErrorMessage = runError?.definitionId === definition.id ? runError.message : null;
188
- const isRunning = runningDefinitionId === definition.id;
189
- return(// The workflow-name cell holds a `<button>` so the row is
190
- // keyboard-reachable (Tab focuses, Enter/Space activates
191
- // via native button semantics). The TableRow itself is no
192
- // longer clickable: a row-level onClick would be invisible
193
- // to keyboard users and require custom keydown handling.
194
- // The `group` class on the row still drives the Run button
195
- // reveal on hover or focus-within.
196
- /*#__PURE__*/ _jsxs(TableRow, {
197
- className: "group",
198
- children: [
199
- /*#__PURE__*/ _jsx(TableCell, {
200
- children: /*#__PURE__*/ _jsx(Icon, {
201
- name: sourceIcon(definition.source),
202
- className: "size-16 text-foreground-neutral-muted",
203
- "aria-hidden": "true"
169
+ /*#__PURE__*/ _jsxs(Panel, {
170
+ children: [
171
+ /*#__PURE__*/ _jsx("div", {
172
+ className: "hidden md:block",
173
+ children: /*#__PURE__*/ _jsxs(Table, {
174
+ children: [
175
+ /*#__PURE__*/ _jsx(TableHeader, {
176
+ children: /*#__PURE__*/ _jsxs(TableRow, {
177
+ children: [
178
+ /*#__PURE__*/ _jsx(TableHead, {
179
+ className: "w-40"
180
+ }),
181
+ /*#__PURE__*/ _jsx(TableHead, {
182
+ children: "Workflow"
183
+ }),
184
+ /*#__PURE__*/ _jsx(TableHead, {
185
+ className: "w-180",
186
+ children: "Updated"
187
+ }),
188
+ /*#__PURE__*/ _jsx(TableHead, {
189
+ className: "w-80 text-right"
204
190
  })
205
- }),
206
- /*#__PURE__*/ _jsx(TableCell, {
207
- className: "max-w-260",
208
- children: /*#__PURE__*/ _jsxs("div", {
209
- className: "flex min-w-0 flex-col gap-tight",
210
- children: [
211
- /*#__PURE__*/ _jsxs("button", {
212
- type: "button",
213
- onClick: ()=>onOpenDefinition(definition),
214
- className: "flex min-w-0 flex-col gap-tight text-left outline-none focus-visible:shadow-border-interactive-with-active rounded-4",
191
+ ]
192
+ })
193
+ }),
194
+ /*#__PURE__*/ _jsx(TableBody, {
195
+ children: definitions.map((definition)=>{
196
+ const runErrorMessage = runError?.definitionId === definition.id ? runError.message : null;
197
+ const isRunning = runningDefinitionId === definition.id;
198
+ return(// The workflow-name cell holds a `<button>` so the row is
199
+ // keyboard-reachable (Tab focuses, Enter/Space activates
200
+ // via native button semantics). The TableRow itself is no
201
+ // longer clickable: a row-level onClick would be invisible
202
+ // to keyboard users and require custom keydown handling.
203
+ // The `group` class on the row still drives the Run button
204
+ // reveal on hover or focus-within.
205
+ /*#__PURE__*/ _jsxs(TableRow, {
206
+ className: "group",
207
+ children: [
208
+ /*#__PURE__*/ _jsx(TableCell, {
209
+ children: /*#__PURE__*/ _jsx(Icon, {
210
+ name: sourceIcon(definition.source),
211
+ className: "size-16 text-foreground-neutral-muted",
212
+ "aria-hidden": "true"
213
+ })
214
+ }),
215
+ /*#__PURE__*/ _jsx(TableCell, {
216
+ className: "max-w-260",
217
+ children: /*#__PURE__*/ _jsxs("div", {
218
+ className: "flex min-w-0 flex-col gap-tight",
215
219
  children: [
216
- /*#__PURE__*/ _jsx(Text, {
217
- size: "sm",
218
- bold: true,
219
- className: "truncate",
220
- children: definition.name
220
+ /*#__PURE__*/ _jsxs("button", {
221
+ type: "button",
222
+ onClick: ()=>onOpenDefinition(definition),
223
+ className: "flex min-w-0 flex-col gap-tight rounded-4 text-left outline-none focus-visible:shadow-border-interactive-with-active",
224
+ children: [
225
+ /*#__PURE__*/ _jsx(Text, {
226
+ size: "sm",
227
+ bold: true,
228
+ className: "truncate",
229
+ children: definition.name
230
+ }),
231
+ /*#__PURE__*/ _jsx(Code, {
232
+ className: "truncate text-foreground-neutral-muted",
233
+ children: definition.configPath ?? 'Manual definition'
234
+ })
235
+ ]
221
236
  }),
222
- /*#__PURE__*/ _jsx(Code, {
223
- className: "truncate text-foreground-neutral-muted",
224
- children: definition.configPath ?? 'Manual definition'
225
- })
237
+ runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
238
+ size: "xs",
239
+ className: "text-tag-error-text",
240
+ children: runErrorMessage
241
+ }) : null
226
242
  ]
227
- }),
228
- runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
229
- size: "xs",
230
- className: "text-tag-error-text",
231
- children: runErrorMessage
243
+ })
244
+ }),
245
+ /*#__PURE__*/ _jsx(TableCell, {
246
+ className: "text-foreground-neutral-muted",
247
+ children: /*#__PURE__*/ _jsx(RelativeTime, {
248
+ value: definition.updatedAt
249
+ })
250
+ }),
251
+ /*#__PURE__*/ _jsx(TableCell, {
252
+ children: definition.manualTrigger ? /*#__PURE__*/ _jsx("div", {
253
+ className: "flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100",
254
+ children: /*#__PURE__*/ _jsx(Button, {
255
+ size: "xs",
256
+ isLoading: isRunning,
257
+ onClick: ()=>onRun(definition),
258
+ children: "Run"
259
+ })
232
260
  }) : null
233
- ]
234
- })
235
- }),
236
- /*#__PURE__*/ _jsx(TableCell, {
237
- className: "text-foreground-neutral-muted",
238
- children: /*#__PURE__*/ _jsx(RelativeTime, {
239
- value: definition.updatedAt
240
- })
241
- }),
242
- /*#__PURE__*/ _jsx(TableCell, {
243
- children: definition.manualTrigger ? /*#__PURE__*/ _jsx("div", {
244
- className: "flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100",
245
- children: /*#__PURE__*/ _jsx(Button, {
246
- size: "xs",
247
- isLoading: isRunning,
248
- onClick: ()=>onRun(definition),
249
- children: "Run"
250
261
  })
251
- }) : null
252
- })
253
- ]
254
- }, definition.id));
255
- })
262
+ ]
263
+ }, definition.id));
264
+ })
265
+ })
266
+ ]
256
267
  })
257
- ]
258
- })
259
- }),
260
- /*#__PURE__*/ _jsx("div", {
261
- className: "flex flex-col rounded-8 border border-border-neutral-base md:hidden",
262
- children: definitions.map((definition)=>{
263
- const runErrorMessage = runError?.definitionId === definition.id ? runError.message : null;
264
- const isRunning = runningDefinitionId === definition.id;
265
- return /*#__PURE__*/ _jsxs("div", {
266
- className: "flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0",
267
- children: [
268
- /*#__PURE__*/ _jsxs("button", {
269
- type: "button",
270
- className: "flex min-w-0 items-start gap-inline text-left",
271
- onClick: ()=>onOpenDefinition(definition),
268
+ }),
269
+ /*#__PURE__*/ _jsx("div", {
270
+ className: "flex flex-col md:hidden",
271
+ children: definitions.map((definition)=>{
272
+ const runErrorMessage = runError?.definitionId === definition.id ? runError.message : null;
273
+ const isRunning = runningDefinitionId === definition.id;
274
+ return /*#__PURE__*/ _jsxs("div", {
275
+ className: "flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0",
272
276
  children: [
273
- /*#__PURE__*/ _jsx(Icon, {
274
- name: sourceIcon(definition.source),
275
- className: "size-16 shrink-0 text-foreground-neutral-muted",
276
- "aria-hidden": "true"
277
- }),
278
- /*#__PURE__*/ _jsxs("div", {
279
- className: "flex min-w-0 flex-col gap-tight",
277
+ /*#__PURE__*/ _jsxs("button", {
278
+ type: "button",
279
+ className: "flex min-w-0 items-start gap-inline text-left",
280
+ onClick: ()=>onOpenDefinition(definition),
280
281
  children: [
281
- /*#__PURE__*/ _jsx(Text, {
282
- size: "sm",
283
- bold: true,
284
- className: "break-words",
285
- children: definition.name
282
+ /*#__PURE__*/ _jsx(Icon, {
283
+ name: sourceIcon(definition.source),
284
+ className: "size-16 shrink-0 text-foreground-neutral-muted",
285
+ "aria-hidden": "true"
286
286
  }),
287
- /*#__PURE__*/ _jsx(Code, {
288
- className: "break-words text-foreground-neutral-muted",
289
- children: definition.configPath ?? 'Manual definition'
287
+ /*#__PURE__*/ _jsxs("div", {
288
+ className: "flex min-w-0 flex-col gap-tight",
289
+ children: [
290
+ /*#__PURE__*/ _jsx(Text, {
291
+ size: "sm",
292
+ bold: true,
293
+ className: "break-words",
294
+ children: definition.name
295
+ }),
296
+ /*#__PURE__*/ _jsx(Code, {
297
+ className: "break-words text-foreground-neutral-muted",
298
+ children: definition.configPath ?? 'Manual definition'
299
+ })
300
+ ]
290
301
  })
291
302
  ]
292
- })
293
- ]
294
- }),
295
- /*#__PURE__*/ _jsxs("div", {
296
- className: "flex items-center justify-between gap-inline",
297
- children: [
298
- /*#__PURE__*/ _jsxs(Text, {
299
- size: "xs",
300
- className: "text-foreground-neutral-muted",
303
+ }),
304
+ /*#__PURE__*/ _jsxs("div", {
305
+ className: "flex items-center justify-between gap-inline",
301
306
  children: [
302
- "Updated ",
303
- /*#__PURE__*/ _jsx(RelativeTime, {
304
- value: definition.updatedAt
305
- })
307
+ /*#__PURE__*/ _jsxs(Text, {
308
+ size: "xs",
309
+ className: "text-foreground-neutral-muted",
310
+ children: [
311
+ "Updated ",
312
+ /*#__PURE__*/ _jsx(RelativeTime, {
313
+ value: definition.updatedAt
314
+ })
315
+ ]
316
+ }),
317
+ definition.manualTrigger ? /*#__PURE__*/ _jsx(Button, {
318
+ size: "sm",
319
+ isLoading: isRunning,
320
+ onClick: ()=>onRun(definition),
321
+ children: "Run"
322
+ }) : null
306
323
  ]
307
324
  }),
308
- definition.manualTrigger ? /*#__PURE__*/ _jsx(Button, {
309
- size: "sm",
310
- isLoading: isRunning,
311
- onClick: ()=>onRun(definition),
312
- children: "Run"
325
+ runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
326
+ size: "xs",
327
+ className: "text-tag-error-text",
328
+ children: runErrorMessage
313
329
  }) : null
314
330
  ]
315
- }),
316
- runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
317
- size: "xs",
318
- className: "text-tag-error-text",
319
- children: runErrorMessage
320
- }) : null
321
- ]
322
- }, definition.id);
323
- })
331
+ }, definition.id);
332
+ })
333
+ })
334
+ ]
324
335
  }),
325
336
  isFetchNextPageError ? /*#__PURE__*/ _jsx(Callout, {
326
337
  role: "alert",
@@ -362,7 +373,8 @@ function WorkflowEmptyState({ sync }) {
362
373
  return /*#__PURE__*/ _jsx(EmptyState, {
363
374
  icon: "flowChart",
364
375
  title: "No workflows",
365
- description: message
376
+ description: message,
377
+ variant: "panel"
366
378
  });
367
379
  }
368
380
  function WorkflowSyncAlert({ sync }) {