@shipfox/client-workflows 21.0.0 → 22.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +33 -0
  3. package/dist/components/job-detail/job-detail-view.js +1 -2
  4. package/dist/components/job-detail/job-detail-view.js.map +1 -1
  5. package/dist/components/job-detail/job-empty-states.js +2 -2
  6. package/dist/components/job-detail/job-empty-states.js.map +1 -1
  7. package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -1
  8. package/dist/components/job-detail/job-execution-time-text.js +2 -15
  9. package/dist/components/job-detail/job-execution-time-text.js.map +1 -1
  10. package/dist/components/job-graph/job-duration-format.js +1 -1
  11. package/dist/components/job-graph/job-duration-format.js.map +1 -1
  12. package/dist/components/job-graph/job-graph-content.js +2 -2
  13. package/dist/components/job-graph/job-graph-content.js.map +1 -1
  14. package/dist/components/step-list/step-list.js +1 -2
  15. package/dist/components/step-list/step-list.js.map +1 -1
  16. package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
  17. package/dist/components/workflow-run-list/workflow-run-list-content.js +13 -7
  18. package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
  19. package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
  20. package/dist/components/workflow-run-list/workflow-run-list-states.js +42 -37
  21. package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
  22. package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
  23. package/dist/components/workflow-run-list/workflow-run-row.js +14 -10
  24. package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
  25. package/dist/components/workflow-run-summary/workflow-run-summary.js +3 -3
  26. package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
  27. package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
  28. package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
  29. package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
  30. package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
  31. package/dist/components/workflow-run-view/workflow-run-view.js +68 -50
  32. package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
  33. package/dist/pages/project-workflows-page.js +181 -172
  34. package/dist/pages/project-workflows-page.js.map +1 -1
  35. package/dist/tsconfig.test.tsbuildinfo +1 -1
  36. package/package.json +8 -8
  37. package/src/components/job-detail/job-detail-view.tsx +1 -2
  38. package/src/components/job-detail/job-empty-states.tsx +2 -2
  39. package/src/components/job-detail/job-execution-time-text.test.ts +46 -0
  40. package/src/components/job-detail/job-execution-time-text.tsx +2 -23
  41. package/src/components/job-graph/job-duration-format.ts +1 -1
  42. package/src/components/job-graph/job-graph-content.tsx +2 -2
  43. package/src/components/step-list/step-list.tsx +1 -2
  44. package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
  45. package/src/components/workflow-run-list/workflow-run-list-states.tsx +41 -40
  46. package/src/components/workflow-run-list/workflow-run-row.tsx +11 -8
  47. package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +1 -1
  48. package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +9 -1
  49. package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
  50. package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
  51. package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
  52. package/src/components/workflow-run-view/workflow-run-view.stories.tsx +3 -1
  53. package/src/components/workflow-run-view/workflow-run-view.test.tsx +11 -8
  54. package/src/components/workflow-run-view/workflow-run-view.tsx +78 -70
  55. package/src/pages/project-workflows-page.test.tsx +14 -4
  56. package/src/pages/project-workflows-page.tsx +138 -132
  57. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +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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"}
@@ -52,6 +52,11 @@ function ProjectWorkflowsPageInner({ projectId }) {
52
52
  return /*#__PURE__*/ _jsxs("div", {
53
53
  className: "flex w-full flex-col gap-section",
54
54
  children: [
55
+ /*#__PURE__*/ _jsx(Header, {
56
+ variant: "h1",
57
+ className: "sr-only",
58
+ children: "Workflows"
59
+ }),
55
60
  projectQuery.isPending ? /*#__PURE__*/ _jsxs("div", {
56
61
  className: "flex flex-col gap-cluster",
57
62
  children: [
@@ -73,20 +78,6 @@ function ProjectWorkflowsPageInner({ projectId }) {
73
78
  }) : null,
74
79
  projectQuery.data ? /*#__PURE__*/ _jsxs(_Fragment, {
75
80
  children: [
76
- /*#__PURE__*/ _jsxs("header", {
77
- className: "flex flex-col gap-tight",
78
- children: [
79
- /*#__PURE__*/ _jsx(Header, {
80
- variant: "h2",
81
- children: "Workflows"
82
- }),
83
- /*#__PURE__*/ _jsx(Text, {
84
- size: "sm",
85
- className: "text-foreground-neutral-muted",
86
- children: "Synced workflow definitions for this project source."
87
- })
88
- ]
89
- }),
90
81
  /*#__PURE__*/ _jsx(SourceStrip, {
91
82
  connectionId: projectQuery.data.source.connectionId,
92
83
  externalRepositoryId: projectQuery.data.source.externalRepositoryId,
@@ -129,198 +120,215 @@ function ProjectWorkflowsPageInner({ projectId }) {
129
120
  }
130
121
  function WorkflowDefinitionsList({ definitions, isPending, isError, sync, runError, runningDefinitionId, hasNextPage, isFetchingNextPage, isFetchNextPageError, onRetry, onLoadMore, onOpenDefinition, onRun }) {
131
122
  if (isPending) {
132
- return /*#__PURE__*/ _jsxs("div", {
133
- className: "flex flex-col gap-inline",
123
+ return /*#__PURE__*/ _jsxs(Panel, {
124
+ role: "status",
125
+ "aria-label": "Loading workflows",
126
+ className: "divide-y",
134
127
  children: [
135
128
  /*#__PURE__*/ _jsx(Skeleton, {
136
- className: "h-40 w-full"
129
+ className: "h-44 w-full rounded-none"
137
130
  }),
138
131
  /*#__PURE__*/ _jsx(Skeleton, {
139
- className: "h-40 w-full"
132
+ className: "h-44 w-full rounded-none"
140
133
  }),
141
134
  /*#__PURE__*/ _jsx(Skeleton, {
142
- className: "h-40 w-full"
135
+ className: "h-44 w-full rounded-none"
143
136
  })
144
137
  ]
145
138
  });
146
139
  }
147
140
  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"
141
+ return /*#__PURE__*/ _jsx(Panel, {
142
+ role: "region",
143
+ "aria-label": "Workflow definitions",
144
+ children: /*#__PURE__*/ _jsx(LoadErrorState, {
145
+ title: "Couldn't load workflows",
146
+ description: "Definitions could not be loaded. Source metadata remains visible.",
147
+ onRetry: onRetry,
148
+ retryLabel: "Retry loading workflows",
149
+ variant: "panel"
150
+ })
153
151
  });
154
152
  }
155
153
  if (definitions.length === 0) {
156
- return /*#__PURE__*/ _jsx(WorkflowEmptyState, {
157
- sync: sync
154
+ return /*#__PURE__*/ _jsx(Panel, {
155
+ role: "region",
156
+ "aria-label": "Workflow definitions",
157
+ children: /*#__PURE__*/ _jsx(WorkflowEmptyState, {
158
+ sync: sync
159
+ })
158
160
  });
159
161
  }
160
162
  return /*#__PURE__*/ _jsxs(_Fragment, {
161
163
  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"
164
+ /*#__PURE__*/ _jsxs(Panel, {
165
+ role: "region",
166
+ "aria-label": "Workflow definitions",
167
+ children: [
168
+ /*#__PURE__*/ _jsx("div", {
169
+ className: "hidden md:block",
170
+ children: /*#__PURE__*/ _jsxs(Table, {
171
+ children: [
172
+ /*#__PURE__*/ _jsx(TableHeader, {
173
+ children: /*#__PURE__*/ _jsxs(TableRow, {
174
+ children: [
175
+ /*#__PURE__*/ _jsx(TableHead, {
176
+ className: "w-40"
177
+ }),
178
+ /*#__PURE__*/ _jsx(TableHead, {
179
+ children: "Workflow"
180
+ }),
181
+ /*#__PURE__*/ _jsx(TableHead, {
182
+ className: "w-180",
183
+ children: "Updated"
184
+ }),
185
+ /*#__PURE__*/ _jsx(TableHead, {
186
+ className: "w-80 text-right"
204
187
  })
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",
188
+ ]
189
+ })
190
+ }),
191
+ /*#__PURE__*/ _jsx(TableBody, {
192
+ children: definitions.map((definition)=>{
193
+ const runErrorMessage = runError?.definitionId === definition.id ? runError.message : null;
194
+ const isRunning = runningDefinitionId === definition.id;
195
+ return(// The workflow-name cell holds a `<button>` so the row is
196
+ // keyboard-reachable (Tab focuses, Enter/Space activates
197
+ // via native button semantics). The TableRow itself is no
198
+ // longer clickable: a row-level onClick would be invisible
199
+ // to keyboard users and require custom keydown handling.
200
+ // The `group` class on the row still drives the Run button
201
+ // reveal on hover or focus-within.
202
+ /*#__PURE__*/ _jsxs(TableRow, {
203
+ className: "group",
204
+ children: [
205
+ /*#__PURE__*/ _jsx(TableCell, {
206
+ children: /*#__PURE__*/ _jsx(Icon, {
207
+ name: sourceIcon(definition.source),
208
+ className: "size-16 text-foreground-neutral-muted",
209
+ "aria-hidden": "true"
210
+ })
211
+ }),
212
+ /*#__PURE__*/ _jsx(TableCell, {
213
+ className: "max-w-260",
214
+ children: /*#__PURE__*/ _jsxs("div", {
215
+ className: "flex min-w-0 flex-col gap-tight",
215
216
  children: [
216
- /*#__PURE__*/ _jsx(Text, {
217
- size: "sm",
218
- bold: true,
219
- className: "truncate",
220
- children: definition.name
217
+ /*#__PURE__*/ _jsxs("button", {
218
+ type: "button",
219
+ onClick: ()=>onOpenDefinition(definition),
220
+ className: "flex min-w-0 flex-col gap-tight rounded-4 text-left outline-none focus-visible:shadow-border-interactive-with-active",
221
+ children: [
222
+ /*#__PURE__*/ _jsx(Text, {
223
+ size: "sm",
224
+ bold: true,
225
+ className: "truncate",
226
+ children: definition.name
227
+ }),
228
+ /*#__PURE__*/ _jsx(Code, {
229
+ className: "truncate text-foreground-neutral-muted",
230
+ children: definition.configPath ?? 'Manual definition'
231
+ })
232
+ ]
221
233
  }),
222
- /*#__PURE__*/ _jsx(Code, {
223
- className: "truncate text-foreground-neutral-muted",
224
- children: definition.configPath ?? 'Manual definition'
225
- })
234
+ runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
235
+ size: "xs",
236
+ className: "text-tag-error-text",
237
+ children: runErrorMessage
238
+ }) : null
226
239
  ]
227
- }),
228
- runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
229
- size: "xs",
230
- className: "text-tag-error-text",
231
- children: runErrorMessage
240
+ })
241
+ }),
242
+ /*#__PURE__*/ _jsx(TableCell, {
243
+ className: "text-foreground-neutral-muted",
244
+ children: /*#__PURE__*/ _jsx(RelativeTime, {
245
+ value: definition.updatedAt
246
+ })
247
+ }),
248
+ /*#__PURE__*/ _jsx(TableCell, {
249
+ children: definition.manualTrigger ? /*#__PURE__*/ _jsx("div", {
250
+ className: "flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100",
251
+ children: /*#__PURE__*/ _jsx(Button, {
252
+ size: "xs",
253
+ isLoading: isRunning,
254
+ onClick: ()=>onRun(definition),
255
+ children: "Run"
256
+ })
232
257
  }) : 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
258
  })
251
- }) : null
252
- })
253
- ]
254
- }, definition.id));
255
- })
259
+ ]
260
+ }, definition.id));
261
+ })
262
+ })
263
+ ]
256
264
  })
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),
265
+ }),
266
+ /*#__PURE__*/ _jsx("div", {
267
+ className: "flex flex-col md:hidden",
268
+ children: definitions.map((definition)=>{
269
+ const runErrorMessage = runError?.definitionId === definition.id ? runError.message : null;
270
+ const isRunning = runningDefinitionId === definition.id;
271
+ return /*#__PURE__*/ _jsxs("div", {
272
+ className: "flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0",
272
273
  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",
274
+ /*#__PURE__*/ _jsxs("button", {
275
+ type: "button",
276
+ className: "flex min-w-0 items-start gap-inline text-left",
277
+ onClick: ()=>onOpenDefinition(definition),
280
278
  children: [
281
- /*#__PURE__*/ _jsx(Text, {
282
- size: "sm",
283
- bold: true,
284
- className: "break-words",
285
- children: definition.name
279
+ /*#__PURE__*/ _jsx(Icon, {
280
+ name: sourceIcon(definition.source),
281
+ className: "size-16 shrink-0 text-foreground-neutral-muted",
282
+ "aria-hidden": "true"
286
283
  }),
287
- /*#__PURE__*/ _jsx(Code, {
288
- className: "break-words text-foreground-neutral-muted",
289
- children: definition.configPath ?? 'Manual definition'
284
+ /*#__PURE__*/ _jsxs("div", {
285
+ className: "flex min-w-0 flex-col gap-tight",
286
+ children: [
287
+ /*#__PURE__*/ _jsx(Text, {
288
+ size: "sm",
289
+ bold: true,
290
+ className: "break-words",
291
+ children: definition.name
292
+ }),
293
+ /*#__PURE__*/ _jsx(Code, {
294
+ className: "break-words text-foreground-neutral-muted",
295
+ children: definition.configPath ?? 'Manual definition'
296
+ })
297
+ ]
290
298
  })
291
299
  ]
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",
300
+ }),
301
+ /*#__PURE__*/ _jsxs("div", {
302
+ className: "flex items-center justify-between gap-inline",
301
303
  children: [
302
- "Updated ",
303
- /*#__PURE__*/ _jsx(RelativeTime, {
304
- value: definition.updatedAt
305
- })
304
+ /*#__PURE__*/ _jsxs(Text, {
305
+ size: "xs",
306
+ className: "text-foreground-neutral-muted",
307
+ children: [
308
+ "Updated ",
309
+ /*#__PURE__*/ _jsx(RelativeTime, {
310
+ value: definition.updatedAt
311
+ })
312
+ ]
313
+ }),
314
+ definition.manualTrigger ? /*#__PURE__*/ _jsx(Button, {
315
+ size: "sm",
316
+ isLoading: isRunning,
317
+ onClick: ()=>onRun(definition),
318
+ children: "Run"
319
+ }) : null
306
320
  ]
307
321
  }),
308
- definition.manualTrigger ? /*#__PURE__*/ _jsx(Button, {
309
- size: "sm",
310
- isLoading: isRunning,
311
- onClick: ()=>onRun(definition),
312
- children: "Run"
322
+ runErrorMessage ? /*#__PURE__*/ _jsx(Text, {
323
+ size: "xs",
324
+ className: "text-tag-error-text",
325
+ children: runErrorMessage
313
326
  }) : null
314
327
  ]
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
- })
328
+ }, definition.id);
329
+ })
330
+ })
331
+ ]
324
332
  }),
325
333
  isFetchNextPageError ? /*#__PURE__*/ _jsx(Callout, {
326
334
  role: "alert",
@@ -362,7 +370,8 @@ function WorkflowEmptyState({ sync }) {
362
370
  return /*#__PURE__*/ _jsx(EmptyState, {
363
371
  icon: "flowChart",
364
372
  title: "No workflows",
365
- description: message
373
+ description: message,
374
+ variant: "panel"
366
375
  });
367
376
  }
368
377
  function WorkflowSyncAlert({ sync }) {