@shipfox/client-workflows 15.0.0 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.css +4 -0
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +41 -0
- package/dist/components/identifier/identifier.js +2 -2
- package/dist/components/identifier/identifier.js.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js +1 -1
- package/dist/components/job-detail/agent-config-failure-callout.js.map +1 -1
- package/dist/components/job-detail/job-context-panel.d.ts +6 -0
- package/dist/components/job-detail/job-context-panel.d.ts.map +1 -0
- package/dist/components/job-detail/job-context-panel.js +224 -0
- package/dist/components/job-detail/job-context-panel.js.map +1 -0
- package/dist/components/job-detail/job-detail-header.d.ts +10 -1
- package/dist/components/job-detail/job-detail-header.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-header.js +58 -39
- package/dist/components/job-detail/job-detail-header.js.map +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts +1 -1
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +92 -41
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.d.ts.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +19 -10
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-switcher.js +4 -4
- package/dist/components/job-detail/job-execution-switcher.js.map +1 -1
- package/dist/components/job-detail/json-code.d.ts +14 -0
- package/dist/components/job-detail/json-code.d.ts.map +1 -0
- package/dist/components/job-detail/json-code.js +93 -0
- package/dist/components/job-detail/json-code.js.map +1 -0
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +9 -6
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/job-detail/step-troubleshooting.d.ts +18 -0
- package/dist/components/job-detail/step-troubleshooting.d.ts.map +1 -0
- package/dist/components/job-detail/step-troubleshooting.js +493 -0
- package/dist/components/job-detail/step-troubleshooting.js.map +1 -0
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +5 -4
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-node.js +3 -3
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/step-list/index.d.ts +1 -0
- package/dist/components/step-list/index.d.ts.map +1 -1
- package/dist/components/step-list/index.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +5 -0
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +5 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +117 -82
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +6 -6
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +4 -4
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +6 -6
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +7 -7
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +0 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +9 -13
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +3 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/index.js +3 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts +16 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js +50 -0
- package/dist/components/workflow-run-tabs/run-annotation-count-chip.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.d.ts +20 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.js +102 -0
- package/dist/components/workflow-run-tabs/run-annotation-item.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts +39 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.js +211 -0
- package/dist/components/workflow-run-tabs/run-annotation-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -1
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +90 -25
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -1
- package/dist/components/workflow-run-tabs/severity-visuals.d.ts +20 -0
- package/dist/components/workflow-run-tabs/severity-visuals.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/severity-visuals.js +31 -0
- package/dist/components/workflow-run-tabs/severity-visuals.js.map +1 -0
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +16 -10
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +6 -6
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +1 -3
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +217 -59
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +7 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +9 -0
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step-attempt.d.ts +29 -0
- package/dist/core/entities/step-attempt.d.ts.map +1 -1
- package/dist/core/entities/step-attempt.js.map +1 -1
- package/dist/core/entities/step.d.ts +3 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/run-annotation.d.ts +79 -0
- package/dist/core/run-annotation.d.ts.map +1 -0
- package/dist/core/run-annotation.js +125 -0
- package/dist/core/run-annotation.js.map +1 -0
- package/dist/core/workflow-run.d.ts +1 -1
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/hooks/api/annotations.d.ts +63 -0
- package/dist/hooks/api/annotations.d.ts.map +1 -0
- package/dist/hooks/api/annotations.js +145 -0
- package/dist/hooks/api/annotations.js.map +1 -0
- package/dist/hooks/api/run-annotation-mapper.d.ts +10 -0
- package/dist/hooks/api/run-annotation-mapper.d.ts.map +1 -0
- package/dist/hooks/api/run-annotation-mapper.js +22 -0
- package/dist/hooks/api/run-annotation-mapper.js.map +1 -0
- package/dist/hooks/api/run-annotations.d.ts +25 -0
- package/dist/hooks/api/run-annotations.d.ts.map +1 -0
- package/dist/hooks/api/run-annotations.js +78 -0
- package/dist/hooks/api/run-annotations.js.map +1 -0
- package/dist/hooks/api/step-attempt-detail.d.ts +14 -0
- package/dist/hooks/api/step-attempt-detail.d.ts.map +1 -0
- package/dist/hooks/api/step-attempt-detail.js +43 -0
- package/dist/hooks/api/step-attempt-detail.js.map +1 -0
- package/dist/hooks/api/workflow-run-mapper.d.ts +9 -2
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +47 -0
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/pages/project-workflows-page.js +19 -19
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.js +1 -1
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +7 -2
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +5 -6
- package/dist/routes/inputs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -9
- package/src/components/identifier/identifier.tsx +2 -2
- package/src/components/job-detail/agent-config-failure-callout.tsx +5 -1
- package/src/components/job-detail/job-context-panel.test.tsx +72 -0
- package/src/components/job-detail/job-context-panel.tsx +237 -0
- package/src/components/job-detail/job-detail-header.test.tsx +3 -0
- package/src/components/job-detail/job-detail-header.tsx +65 -34
- package/src/components/job-detail/job-detail-view.stories.tsx +555 -48
- package/src/components/job-detail/job-detail-view.tsx +101 -42
- package/src/components/job-detail/job-empty-states.tsx +23 -10
- package/src/components/job-detail/job-execution-switcher.tsx +5 -5
- package/src/components/job-detail/json-code.tsx +120 -0
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +1 -0
- package/src/components/job-detail/step-attempt-log-panel.tsx +17 -6
- package/src/components/job-detail/step-troubleshooting.stories.tsx +120 -0
- package/src/components/job-detail/step-troubleshooting.test.tsx +337 -0
- package/src/components/job-detail/step-troubleshooting.tsx +537 -0
- package/src/components/job-graph/job-graph-content.tsx +4 -3
- package/src/components/job-graph/job-node.tsx +3 -3
- package/src/components/step-list/index.ts +1 -0
- package/src/components/step-list/step-list-model.ts +5 -0
- package/src/components/step-list/step-list.test.tsx +75 -2
- package/src/components/step-list/step-list.tsx +73 -20
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-filters.tsx +4 -4
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +6 -6
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-row.tsx +7 -7
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +12 -12
- package/src/components/workflow-run-tabs/index.ts +7 -0
- package/src/components/workflow-run-tabs/run-annotation-count-chip.tsx +63 -0
- package/src/components/workflow-run-tabs/run-annotation-item.tsx +123 -0
- package/src/components/workflow-run-tabs/run-annotation-list.tsx +273 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +77 -3
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +95 -20
- package/src/components/workflow-run-tabs/run-annotations.stories.tsx +293 -0
- package/src/components/workflow-run-tabs/severity-visuals.ts +37 -0
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +44 -0
- package/src/components/workflow-run-view/run-workspace-nav.tsx +22 -9
- package/src/components/workflow-run-view/workflow-run-states.tsx +6 -6
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +246 -20
- package/src/components/workflow-run-view/workflow-run-view.tsx +255 -37
- package/src/core/entities/job-execution.ts +7 -0
- package/src/core/entities/job.ts +9 -0
- package/src/core/entities/step-attempt.ts +33 -0
- package/src/core/entities/step.ts +3 -1
- package/src/core/run-annotation.test.ts +315 -0
- package/src/core/run-annotation.ts +244 -0
- package/src/core/workflow-run.ts +4 -0
- package/src/hooks/api/annotations.test.ts +84 -0
- package/src/hooks/api/annotations.ts +240 -0
- package/src/hooks/api/run-annotation-mapper.ts +30 -0
- package/src/hooks/api/run-annotations.ts +137 -0
- package/src/hooks/api/step-attempt-detail.ts +67 -0
- package/src/hooks/api/workflow-run-mapper.ts +46 -0
- package/src/hooks/api/workflow-runs.test.tsx +61 -1
- package/src/pages/project-workflows-page.tsx +19 -19
- package/src/pages/workflow-job-detail-page.test.tsx +118 -1
- package/src/pages/workflow-run-list-page.tsx +1 -1
- package/src/routes/inputs.ts +7 -2
- package/test/fixtures/workflow-run.ts +7 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/core/workflow-run-tabs.d.ts +0 -9
- package/dist/core/workflow-run-tabs.d.ts.map +0 -1
- package/dist/core/workflow-run-tabs.js +0 -3
- package/dist/core/workflow-run-tabs.js.map +0 -1
- package/src/core/workflow-run-tabs.ts +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"sourcesContent":["import {Collapsible, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n type Job,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobExecutionTimeText} from '../job-detail/job-execution-time-text.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface RunWorkspaceNavProps {\n workspaceSlug: string;\n projectSlug: string;\n run: WorkflowRunDetail;\n activeSection: RunWorkspaceSection;\n currentJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n}\n\n/**\n * The stable navigation model shared by run-level and job-level routes. The graph explains\n * topology; this compact index makes moving between dedicated job pages predictable.\n */\nexport function RunWorkspaceNav({\n workspaceSlug,\n projectSlug,\n run,\n activeSection,\n currentJobId,\n jobSearch = {},\n annotationSummary,\n}: RunWorkspaceNavProps) {\n const jobs = useMemo(() => [...run.jobs].sort(compareJobs), [run.jobs]);\n const [mobileOpen, setMobileOpen] = useState(false);\n const currentJob = jobs.find((job) => job.id === currentJobId);\n const currentLabel = currentJob?.displayName ?? sectionLabel(activeSection);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <aside className=\"w-full shrink-0 border-b border-border-neutral-base bg-background-neutral-background min-[768px]:w-240 min-[768px]:border-b-0 min-[768px]:border-r\">\n <Collapsible open={mobileOpen} onOpenChange={setMobileOpen}>\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"flex min-h-44 w-full items-center justify-between gap-12 px-16 text-left outline-none focus-visible:shadow-border-interactive-with-active min-[768px]:hidden\"\n aria-label=\"Toggle run navigation\"\n >\n <span className=\"min-w-0\">\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n Run navigation\n </Text>\n <span className=\"block truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {currentLabel}\n </span>\n </span>\n <Icon\n name=\"chevronDown\"\n size={14}\n aria-hidden=\"true\"\n className={cn(\n 'shrink-0 text-foreground-neutral-muted transition-transform',\n mobileOpen && 'rotate-180',\n )}\n />\n </button>\n </CollapsibleTrigger>\n <div\n className={cn(\n 'max-h-[50vh] overflow-y-auto p-8 min-[768px]:block min-[768px]:max-h-none min-[768px]:p-12',\n !mobileOpen && 'hidden',\n )}\n >\n <RunWorkspaceNavContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={run}\n jobs={jobs}\n activeSection={activeSection}\n currentJobId={currentJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n mobileOpen={mobileOpen}\n onNavigate={() => setMobileOpen(false)}\n />\n </div>\n </Collapsible>\n </aside>\n </TimeTickerProvider>\n );\n}\n\nfunction RunWorkspaceNavContent({\n workspaceSlug,\n projectSlug,\n run,\n jobs,\n activeSection,\n currentJobId,\n jobSearch,\n annotationSummary,\n mobileOpen,\n onNavigate,\n}: RunWorkspaceNavProps & {\n jobs: Job[];\n jobSearch: WorkflowJobSearch;\n mobileOpen: boolean;\n onNavigate: () => void;\n}) {\n const currentRowRef = useRef<HTMLAnchorElement>(null);\n const runAttempt = jobSearch.runAttempt ?? run.runAttempt.attempt;\n\n useEffect(() => {\n if (!currentJobId || mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n useEffect(() => {\n if (!currentJobId || !mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n return (\n <nav aria-label=\"Run workspace\" className=\"flex min-w-0 flex-col gap-16\">\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"summary\"\n current={!currentJobId && activeSection === 'summary'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n\n <section aria-labelledby=\"run-workspace-jobs-heading\" className=\"min-w-0\">\n <div className=\"flex items-center justify-between gap-8 px-8 pb-6\">\n <Text\n as=\"h2\"\n id=\"run-workspace-jobs-heading\"\n size=\"xs\"\n bold\n className=\"text-foreground-neutral-muted\"\n >\n Jobs\n </Text>\n <Text as=\"span\" size=\"xs\" className=\"font-code text-foreground-neutral-subtle\">\n {jobs.length}\n </Text>\n </div>\n <ol className=\"max-h-[320px] overflow-y-auto\">\n {jobs.map((job) => {\n const current = job.id === currentJobId;\n const execution = defaultJobExecution(job);\n const duration = execution?.displayDuration ?? job.displayDuration;\n\n return (\n <li key={job.id}>\n <Link\n ref={current ? currentRowRef : undefined}\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id, jobId: job.id}}\n search={workflowJobSearchParams({runAttempt}) as never}\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n className={cn(\n 'flex min-h-32 min-w-0 items-center gap-8 rounded-4 px-8 text-left outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n <WorkflowStatusIcon\n status={deriveJobDisplayStatus(job)}\n size={12}\n tooltip={false}\n />\n <span className=\"min-w-0 truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {job.displayName}\n </span>\n <span className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n {duration ? <JobExecutionTimeText time={duration} /> : '—'}\n </span>\n </Link>\n </li>\n );\n })}\n </ol>\n </section>\n\n <section aria-labelledby=\"run-workspace-details-heading\">\n <Text\n as=\"h2\"\n id=\"run-workspace-details-heading\"\n size=\"xs\"\n bold\n className=\"px-8 pb-6 text-foreground-neutral-muted\"\n >\n Run details\n </Text>\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"annotations\"\n current={!currentJobId && activeSection === 'annotations'}\n count={annotationSummary?.total}\n onNavigate={onNavigate}\n />\n </li>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"source\"\n current={!currentJobId && activeSection === 'source'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n </section>\n </nav>\n );\n}\n\nfunction RunSectionLink({\n workspaceSlug,\n projectSlug,\n runId,\n runAttempt,\n section,\n current,\n count,\n onNavigate,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n runAttempt: number;\n section: RunWorkspaceSection;\n current: boolean;\n count?: number | undefined;\n onNavigate?: (() => void) | undefined;\n}) {\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId}}\n search={\n workflowRunSearchParams(\n {runAttempt, ...(section === 'summary' ? {} : {tab: section})},\n {runAttempt},\n ) as never\n }\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n className={cn(\n 'flex min-h-32 items-center gap-8 rounded-4 px-8 text-xs font-medium text-foreground-neutral-base outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n {sectionLabel(section)}\n {count ? (\n <span className=\"ml-auto font-code text-xs text-foreground-neutral-muted tabular-nums\">\n {count}\n </span>\n ) : null}\n </Link>\n );\n}\n\nfunction sectionLabel(section: RunWorkspaceSection): string {\n if (section === 'annotations') return 'Annotations';\n if (section === 'source') return 'Source';\n return 'Summary';\n}\n\nfunction compareJobs(left: Job, right: Job): number {\n return left.position - right.position || left.id.localeCompare(right.id);\n}\n"],"names":["Collapsible","CollapsibleTrigger","Icon","TimeTickerProvider","Text","cn","Link","useEffect","useMemo","useRef","useState","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","workflowJobSearchParams","workflowRunSearchParams","JobExecutionTimeText","RunWorkspaceNav","workspaceSlug","projectSlug","run","activeSection","currentJobId","jobSearch","annotationSummary","jobs","sort","compareJobs","mobileOpen","setMobileOpen","currentJob","find","job","id","currentLabel","displayName","sectionLabel","intervalMs","reducedMotionIntervalMs","aside","className","open","onOpenChange","asChild","button","type","aria-label","span","as","size","name","aria-hidden","div","RunWorkspaceNavContent","onNavigate","currentRowRef","runAttempt","attempt","current","scrollIntoView","block","nav","ul","li","RunSectionLink","runId","section","aria-labelledby","bold","length","ol","map","execution","duration","displayDuration","ref","undefined","to","params","workflowRunId","jobId","search","onClick","aria-current","status","tooltip","time","count","total","tab","left","right","position","localeCompare"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"sourcesContent":["import {Collapsible, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n type Job,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport {\n type WorkflowJobSearch,\n type WorkflowRunTab,\n workflowJobSearchParams,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {JobExecutionTimeText} from '../job-detail/job-execution-time-text.js';\n\ntype RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;\n\nexport interface RunWorkspaceNavProps {\n workspaceSlug: string;\n projectSlug: string;\n run: WorkflowRunDetail;\n activeSection: RunWorkspaceSection;\n currentJobId?: string | undefined;\n jobSearch?: WorkflowJobSearch | undefined;\n annotationSummary?: RunAnnotationSummary | undefined;\n}\n\n/**\n * The stable navigation model shared by run-level and job-level routes. The graph explains\n * topology; this compact index makes moving between dedicated job pages predictable.\n */\nexport function RunWorkspaceNav({\n workspaceSlug,\n projectSlug,\n run,\n activeSection,\n currentJobId,\n jobSearch = {},\n annotationSummary,\n}: RunWorkspaceNavProps) {\n const jobs = useMemo(() => [...run.jobs].sort(compareJobs), [run.jobs]);\n const [mobileOpen, setMobileOpen] = useState(false);\n const currentJob = jobs.find((job) => job.id === currentJobId);\n const currentLabel = currentJob?.displayName ?? sectionLabel(activeSection);\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <aside className=\"w-full shrink-0 border-b border-border-neutral-base bg-background-neutral-background min-[768px]:w-240 min-[768px]:border-b-0 min-[768px]:border-r\">\n <Collapsible open={mobileOpen} onOpenChange={setMobileOpen}>\n <CollapsibleTrigger asChild>\n <button\n type=\"button\"\n className=\"flex min-h-44 w-full items-center justify-between gap-cluster px-row text-left outline-none focus-visible:shadow-border-interactive-with-active min-[768px]:hidden\"\n aria-label=\"Toggle run navigation\"\n >\n <span className=\"min-w-0\">\n <Text as=\"span\" size=\"xs\" className=\"block text-foreground-neutral-muted\">\n Run navigation\n </Text>\n <span className=\"block truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {currentLabel}\n </span>\n </span>\n <Icon\n name=\"chevronDown\"\n size={14}\n aria-hidden=\"true\"\n className={cn(\n 'shrink-0 text-foreground-neutral-muted transition-transform',\n mobileOpen && 'rotate-180',\n )}\n />\n </button>\n </CollapsibleTrigger>\n <div\n className={cn(\n 'max-h-[50vh] overflow-y-auto p-tight min-[768px]:block min-[768px]:max-h-none min-[768px]:p-[12px]',\n !mobileOpen && 'hidden',\n )}\n >\n <RunWorkspaceNavContent\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n run={run}\n jobs={jobs}\n activeSection={activeSection}\n currentJobId={currentJobId}\n jobSearch={jobSearch}\n annotationSummary={annotationSummary}\n mobileOpen={mobileOpen}\n onNavigate={() => setMobileOpen(false)}\n />\n </div>\n </Collapsible>\n </aside>\n </TimeTickerProvider>\n );\n}\n\nfunction RunWorkspaceNavContent({\n workspaceSlug,\n projectSlug,\n run,\n jobs,\n activeSection,\n currentJobId,\n jobSearch,\n annotationSummary,\n mobileOpen,\n onNavigate,\n}: RunWorkspaceNavProps & {\n jobs: Job[];\n jobSearch: WorkflowJobSearch;\n mobileOpen: boolean;\n onNavigate: () => void;\n}) {\n const currentRowRef = useRef<HTMLAnchorElement>(null);\n const runAttempt = jobSearch.runAttempt ?? run.runAttempt.attempt;\n\n useEffect(() => {\n if (!currentJobId || mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n useEffect(() => {\n if (!currentJobId || !mobileOpen) return;\n currentRowRef.current?.scrollIntoView({block: 'nearest'});\n }, [currentJobId, mobileOpen]);\n\n return (\n <nav aria-label=\"Run workspace\" className=\"flex min-w-0 flex-col gap-group\">\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"summary\"\n current={!currentJobId && activeSection === 'summary'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n\n <section aria-labelledby=\"run-workspace-jobs-heading\" className=\"min-w-0\">\n <div className=\"flex items-center justify-between gap-inline px-tight pb-[6px]\">\n <Text\n as=\"h2\"\n id=\"run-workspace-jobs-heading\"\n size=\"xs\"\n bold\n className=\"text-foreground-neutral-muted\"\n >\n Jobs\n </Text>\n <Text as=\"span\" size=\"xs\" className=\"font-code text-foreground-neutral-subtle\">\n {jobs.length}\n </Text>\n </div>\n <ol className=\"max-h-[320px] overflow-y-auto\">\n {jobs.map((job) => {\n const current = job.id === currentJobId;\n const execution = defaultJobExecution(job);\n const duration = execution?.displayDuration ?? job.displayDuration;\n\n return (\n <li key={job.id}>\n <Link\n ref={current ? currentRowRef : undefined}\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId/jobs/$jobId\"\n params={{workspaceSlug, projectSlug, workflowRunId: run.id, jobId: job.id}}\n search={workflowJobSearchParams({runAttempt}) as never}\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n className={cn(\n 'flex min-h-32 min-w-0 items-center gap-inline rounded-4 px-tight text-left outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n <WorkflowStatusIcon\n status={deriveJobDisplayStatus(job)}\n size={12}\n tooltip={false}\n />\n <span className=\"min-w-0 truncate font-code text-xs leading-20 text-foreground-neutral-base\">\n {job.displayName}\n </span>\n <span className=\"ml-auto shrink-0 font-code text-xs leading-20 text-foreground-neutral-muted tabular-nums\">\n {duration ? <JobExecutionTimeText time={duration} /> : '—'}\n </span>\n </Link>\n </li>\n );\n })}\n </ol>\n </section>\n\n <section aria-labelledby=\"run-workspace-details-heading\">\n <Text\n as=\"h2\"\n id=\"run-workspace-details-heading\"\n size=\"xs\"\n bold\n className=\"px-tight pb-[6px] text-foreground-neutral-muted\"\n >\n Run details\n </Text>\n <ul>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"annotations\"\n current={!currentJobId && activeSection === 'annotations'}\n count={annotationSummary?.total}\n countTruncated={annotationSummary?.truncated}\n onNavigate={onNavigate}\n />\n </li>\n <li>\n <RunSectionLink\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n runId={run.id}\n runAttempt={runAttempt}\n section=\"source\"\n current={!currentJobId && activeSection === 'source'}\n onNavigate={onNavigate}\n />\n </li>\n </ul>\n </section>\n </nav>\n );\n}\n\nfunction RunSectionLink({\n workspaceSlug,\n projectSlug,\n runId,\n runAttempt,\n section,\n current,\n count,\n countTruncated = false,\n onNavigate,\n}: {\n workspaceSlug: string;\n projectSlug: string;\n runId: string;\n runAttempt: number;\n section: RunWorkspaceSection;\n current: boolean;\n count?: number | undefined;\n /** The read hit its page budget, so the count is a lower bound and renders as `N+`. */\n countTruncated?: boolean | undefined;\n onNavigate?: (() => void) | undefined;\n}) {\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId: runId}}\n search={\n workflowRunSearchParams(\n {runAttempt, ...(section === 'summary' ? {} : {tab: section})},\n {runAttempt},\n ) as never\n }\n onClick={onNavigate}\n aria-current={current ? 'page' : undefined}\n aria-label={\n count\n ? `${sectionLabel(section)}, ${countTruncated ? `${count} or more` : count}`\n : undefined\n }\n className={cn(\n 'flex min-h-32 items-center gap-inline rounded-4 px-tight text-xs font-medium text-foreground-neutral-base outline-none transition-colors hover:bg-background-neutral-hover focus-visible:shadow-border-interactive-with-active @max-[767px]:min-h-44 [@media(pointer:coarse)]:min-h-44',\n current && 'bg-background-neutral-hover',\n )}\n >\n {sectionLabel(section)}\n {count ? (\n <span\n aria-hidden=\"true\"\n className=\"ml-auto font-code text-xs text-foreground-neutral-muted tabular-nums\"\n >\n {count}\n {countTruncated ? '+' : ''}\n </span>\n ) : null}\n </Link>\n );\n}\n\nfunction sectionLabel(section: RunWorkspaceSection): string {\n if (section === 'annotations') return 'Annotations';\n if (section === 'source') return 'Source';\n return 'Summary';\n}\n\nfunction compareJobs(left: Job, right: Job): number {\n return left.position - right.position || left.id.localeCompare(right.id);\n}\n"],"names":["Collapsible","CollapsibleTrigger","Icon","TimeTickerProvider","Text","cn","Link","useEffect","useMemo","useRef","useState","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","workflowJobSearchParams","workflowRunSearchParams","JobExecutionTimeText","RunWorkspaceNav","workspaceSlug","projectSlug","run","activeSection","currentJobId","jobSearch","annotationSummary","jobs","sort","compareJobs","mobileOpen","setMobileOpen","currentJob","find","job","id","currentLabel","displayName","sectionLabel","intervalMs","reducedMotionIntervalMs","aside","className","open","onOpenChange","asChild","button","type","aria-label","span","as","size","name","aria-hidden","div","RunWorkspaceNavContent","onNavigate","currentRowRef","runAttempt","attempt","current","scrollIntoView","block","nav","ul","li","RunSectionLink","runId","section","aria-labelledby","bold","length","ol","map","execution","duration","displayDuration","ref","undefined","to","params","workflowRunId","jobId","search","onClick","aria-current","status","tooltip","time","count","total","countTruncated","truncated","tab","left","right","position","localeCompare"],"mappings":"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"}
|
|
@@ -7,12 +7,12 @@ import { Text } from '@shipfox/react-ui/typography';
|
|
|
7
7
|
export function WorkflowRunSkeleton() {
|
|
8
8
|
return /*#__PURE__*/ _jsx("section", {
|
|
9
9
|
"aria-label": "Loading workflow run",
|
|
10
|
-
className: "bg-background-neutral-background px-
|
|
10
|
+
className: "bg-background-neutral-background px-row py-row",
|
|
11
11
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
12
|
-
className: "flex min-w-0 flex-wrap items-center gap-x-
|
|
12
|
+
className: "flex min-w-0 flex-wrap items-center gap-x-cluster gap-y-inline",
|
|
13
13
|
children: [
|
|
14
14
|
/*#__PURE__*/ _jsxs("div", {
|
|
15
|
-
className: "flex min-w-0 items-center gap-
|
|
15
|
+
className: "flex min-w-0 items-center gap-inline",
|
|
16
16
|
children: [
|
|
17
17
|
/*#__PURE__*/ _jsx(Skeleton, {
|
|
18
18
|
className: "size-8 rounded-full"
|
|
@@ -51,7 +51,7 @@ export function WorkflowRunSkeleton() {
|
|
|
51
51
|
export function WorkflowRunContentSkeleton() {
|
|
52
52
|
return /*#__PURE__*/ _jsx("section", {
|
|
53
53
|
"aria-label": "Loading workflow run content",
|
|
54
|
-
className: "min-h-160 rounded-8 border border-border-neutral-base bg-background-components-base p-
|
|
54
|
+
className: "min-h-160 rounded-8 border border-border-neutral-base bg-background-components-base p-panel-compact",
|
|
55
55
|
children: /*#__PURE__*/ _jsx(Skeleton, {
|
|
56
56
|
className: "h-160 w-full rounded-6"
|
|
57
57
|
})
|
|
@@ -70,12 +70,12 @@ export function WorkflowRunNotFound() {
|
|
|
70
70
|
* screen with an inline retry instead of being wiped by a full error placeholder.
|
|
71
71
|
*/ export function WorkflowRunStaleError({ query }) {
|
|
72
72
|
return /*#__PURE__*/ _jsx("div", {
|
|
73
|
-
className: "border-b border-border-neutral-base p-
|
|
73
|
+
className: "border-b border-border-neutral-base p-tight",
|
|
74
74
|
children: /*#__PURE__*/ _jsx(Callout, {
|
|
75
75
|
role: "alert",
|
|
76
76
|
type: "error",
|
|
77
77
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
78
|
-
className: "flex items-center justify-between gap-
|
|
78
|
+
className: "flex items-center justify-between gap-inline",
|
|
79
79
|
children: [
|
|
80
80
|
/*#__PURE__*/ _jsx(Text, {
|
|
81
81
|
size: "xs",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-states.tsx"],"sourcesContent":["import type {QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\n\nexport function WorkflowRunSkeleton() {\n return (\n <section\n aria-label=\"Loading workflow run\"\n className=\"bg-background-neutral-background px-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-view/workflow-run-states.tsx"],"sourcesContent":["import type {QueryLoadErrorQuery} from '@shipfox/client-ui';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {Text} from '@shipfox/react-ui/typography';\n\nexport function WorkflowRunSkeleton() {\n return (\n <section\n aria-label=\"Loading workflow run\"\n className=\"bg-background-neutral-background px-row py-row\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-x-cluster gap-y-inline\">\n <div className=\"flex min-w-0 items-center gap-inline\">\n <Skeleton className=\"size-8 rounded-full\" />\n <Skeleton className=\"h-24 w-180 rounded-6\" />\n </div>\n <Skeleton className=\"h-24 w-88 rounded-6\" />\n <span\n aria-hidden=\"true\"\n className=\"hidden h-20 w-px shrink-0 bg-border-neutral-base sm:block\"\n />\n <Skeleton className=\"h-20 w-64 rounded-4\" />\n <Skeleton className=\"h-20 w-112 rounded-4\" />\n <span className=\"min-w-0 flex-1\" />\n <Skeleton className=\"h-20 w-88 rounded-4\" />\n <Skeleton className=\"h-20 w-88 rounded-4\" />\n </div>\n </section>\n );\n}\n\nexport function WorkflowRunContentSkeleton() {\n return (\n <section\n aria-label=\"Loading workflow run content\"\n className=\"min-h-160 rounded-8 border border-border-neutral-base bg-background-components-base p-panel-compact\"\n >\n <Skeleton className=\"h-160 w-full rounded-6\" />\n </section>\n );\n}\n\nexport function WorkflowRunNotFound() {\n return (\n <EmptyState\n icon=\"pulseLine\"\n title=\"Run not found\"\n description=\"This run does not exist or is no longer available.\"\n />\n );\n}\n\n/**\n * Slim non-blocking banner shown when a background refetch fails after the run already\n * loaded (active-run polling can hit a transient API error), so the loaded run stays on\n * screen with an inline retry instead of being wiped by a full error placeholder.\n */\nexport function WorkflowRunStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <div className=\"border-b border-border-neutral-base p-tight\">\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh this run.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n </div>\n );\n}\n"],"names":["Button","Callout","EmptyState","Skeleton","Text","WorkflowRunSkeleton","section","aria-label","className","div","span","aria-hidden","WorkflowRunContentSkeleton","WorkflowRunNotFound","icon","title","description","WorkflowRunStaleError","query","role","type","size","variant","isLoading","isFetching","onClick","refetch"],"mappings":";AACA,SAAQA,MAAM,QAAO,2BAA2B;AAChD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,UAAU,QAAO,gCAAgC;AACzD,SAAQC,QAAQ,QAAO,6BAA6B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAElD,OAAO,SAASC;IACd,qBACE,KAACC;QACCC,cAAW;QACXC,WAAU;kBAEV,cAAA,MAACC;YAAID,WAAU;;8BACb,MAACC;oBAAID,WAAU;;sCACb,KAACL;4BAASK,WAAU;;sCACpB,KAACL;4BAASK,WAAU;;;;8BAEtB,KAACL;oBAASK,WAAU;;8BACpB,KAACE;oBACCC,eAAY;oBACZH,WAAU;;8BAEZ,KAACL;oBAASK,WAAU;;8BACpB,KAACL;oBAASK,WAAU;;8BACpB,KAACE;oBAAKF,WAAU;;8BAChB,KAACL;oBAASK,WAAU;;8BACpB,KAACL;oBAASK,WAAU;;;;;AAI5B;AAEA,OAAO,SAASI;IACd,qBACE,KAACN;QACCC,cAAW;QACXC,WAAU;kBAEV,cAAA,KAACL;YAASK,WAAU;;;AAG1B;AAEA,OAAO,SAASK;IACd,qBACE,KAACX;QACCY,MAAK;QACLC,OAAM;QACNC,aAAY;;AAGlB;AAEA;;;;CAIC,GACD,OAAO,SAASC,sBAAsB,EAACC,KAAK,EAA+B;IACzE,qBACE,KAACT;QAAID,WAAU;kBACb,cAAA,KAACP;YAAQkB,MAAK;YAAQC,MAAK;sBACzB,cAAA,MAACX;gBAAID,WAAU;;kCACb,KAACJ;wBAAKiB,MAAK;kCAAK;;kCAChB,KAACrB;wBACCoB,MAAK;wBACLC,MAAK;wBACLC,SAAQ;wBACRC,WAAWL,MAAMM,UAAU;wBAC3BC,SAAS;4BACP,KAAKP,MAAMQ,OAAO;wBACpB;kCACD;;;;;;AAOX"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
-
import type { RunAnnotationSummary } from '#core/workflow-run-tabs.js';
|
|
3
2
|
import { type WorkflowJobSearch, type WorkflowRunsSearch, type WorkflowRunTab } from '#routes/inputs.js';
|
|
4
3
|
export interface WorkflowRunViewProps {
|
|
5
4
|
projectId: string;
|
|
@@ -9,7 +8,6 @@ export interface WorkflowRunViewProps {
|
|
|
9
8
|
runAttempt?: number | undefined;
|
|
10
9
|
selection?: WorkflowRunsSearch | undefined;
|
|
11
10
|
tab?: WorkflowRunTab | undefined;
|
|
12
|
-
annotationSummary?: RunAnnotationSummary | undefined;
|
|
13
11
|
activeJobId?: string | undefined;
|
|
14
12
|
jobSearch?: WorkflowJobSearch | undefined;
|
|
15
13
|
jobContent?: ReactNode | undefined;
|
|
@@ -18,5 +16,5 @@ export interface WorkflowRunViewProps {
|
|
|
18
16
|
* The persistent run workspace. Run-level sections and dedicated job routes share the same
|
|
19
17
|
* header and job index, while the all-jobs Summary always opens on the dependency graph.
|
|
20
18
|
*/
|
|
21
|
-
export declare function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, runAttempt, selection, tab,
|
|
19
|
+
export declare function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, runAttempt, selection, tab, activeJobId, jobSearch, jobContent, }: WorkflowRunViewProps): import("react").JSX.Element;
|
|
22
20
|
//# sourceMappingURL=workflow-run-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-view.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow-run-view.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/workflow-run-view.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAC,KAAK,SAAS,EAAqB,MAAM,OAAO,CAAC;AAezD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AAqB3B,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC3C,GAAG,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC1C,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,GAAG,EACH,WAAW,EACX,SAAS,EACT,UAAU,GACX,EAAE,oBAAoB,+BA0CtB"}
|
|
@@ -5,14 +5,18 @@ import { EmptyState } from '@shipfox/react-ui/empty-state';
|
|
|
5
5
|
import { RelativeTimeProvider } from '@shipfox/react-ui/relative-time';
|
|
6
6
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@shipfox/react-ui/select';
|
|
7
7
|
import { toast } from '@shipfox/react-ui/toast';
|
|
8
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
9
|
import { useNavigate } from '@tanstack/react-router';
|
|
9
|
-
import { useEffect } from 'react';
|
|
10
|
+
import { useEffect, useMemo } from 'react';
|
|
11
|
+
import { buildRunAnnotationList } from '#core/run-annotation.js';
|
|
12
|
+
import { isWorkflowRunTerminal } from '#core/workflow-run.js';
|
|
10
13
|
import { withoutWorkflowRunSelectionSearch } from '#core/workflow-run-url-state.js';
|
|
14
|
+
import { useRunAnnotationsQuery } from '#hooks/api/run-annotations.js';
|
|
11
15
|
import { useCancelWorkflowRunMutation, useRerunWorkflowRunMutation, useWorkflowRunAttemptQuery } from '#hooks/api/workflow-runs.js';
|
|
12
16
|
import { workflowJobSearchParams, workflowRunSearchParams } from '#routes/inputs.js';
|
|
13
17
|
import { JobGraph } from '../job-graph/index.js';
|
|
14
18
|
import { WorkflowRunSummary } from '../workflow-run-summary/index.js';
|
|
15
|
-
import {
|
|
19
|
+
import { RunAnnotationList, RunAnnotationSummaryLine } from '../workflow-run-tabs/index.js';
|
|
16
20
|
import { WorkflowSourceContent } from '../workflow-source-panel/index.js';
|
|
17
21
|
import { RunWorkspaceNav } from './run-workspace-nav.js';
|
|
18
22
|
import { resolveWorkflowRunSelection } from './workflow-run-selection.js';
|
|
@@ -20,12 +24,24 @@ import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, W
|
|
|
20
24
|
/**
|
|
21
25
|
* The persistent run workspace. Run-level sections and dedicated job routes share the same
|
|
22
26
|
* header and job index, while the all-jobs Summary always opens on the dependency graph.
|
|
23
|
-
*/ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, runAttempt, selection, tab,
|
|
27
|
+
*/ export function WorkflowRunView({ projectId, workspaceSlug, projectSlug, workflowRunId, runAttempt, selection, tab, activeJobId, jobSearch, jobContent }) {
|
|
24
28
|
const runQuery = useWorkflowRunAttemptQuery({
|
|
25
29
|
workflowRunId,
|
|
26
30
|
runAttempt: selection?.runAttempt ?? runAttempt
|
|
27
31
|
});
|
|
28
32
|
const rerunMutation = useRerunWorkflowRunMutation(projectId);
|
|
33
|
+
// Annotations are read on their own cadence rather than hydrated into the run, so the two
|
|
34
|
+
// modules stay decoupled. The key is shared, so the job page's count chip costs no extra fetch.
|
|
35
|
+
//
|
|
36
|
+
// Polling is scoped to the surface that renders bodies. The read has no counts-only mode, so
|
|
37
|
+
// every poll transfers whole Markdown bodies; doing that every four seconds behind Summary,
|
|
38
|
+
// Source, and the job log spends megabytes to keep one rail number warm. Off that surface the
|
|
39
|
+
// counts still refresh on navigation and on window focus.
|
|
40
|
+
const annotationsQuery = useRunAnnotationsQuery({
|
|
41
|
+
workflowRunId,
|
|
42
|
+
runAttempt: runQuery.data?.runAttempt.attempt,
|
|
43
|
+
live: runWorkspaceSection(tab) === 'annotations' && !activeJobId && Boolean(runQuery.data) && !isWorkflowRunTerminal(runQuery.data?.runAttempt.status ?? 'pending')
|
|
44
|
+
});
|
|
29
45
|
return /*#__PURE__*/ _jsx(RelativeTimeProvider, {
|
|
30
46
|
children: /*#__PURE__*/ _jsx("div", {
|
|
31
47
|
className: "flex min-h-0 min-w-0 flex-1 overflow-hidden",
|
|
@@ -33,11 +49,11 @@ import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, W
|
|
|
33
49
|
workspaceSlug: workspaceSlug,
|
|
34
50
|
projectSlug: projectSlug,
|
|
35
51
|
query: runQuery,
|
|
52
|
+
annotations: annotationsQuery,
|
|
36
53
|
rerunMutation: rerunMutation,
|
|
37
54
|
runAttempt: runAttempt,
|
|
38
55
|
selection: selection,
|
|
39
56
|
tab: tab,
|
|
40
|
-
annotationSummary: annotationSummary,
|
|
41
57
|
activeJobId: activeJobId,
|
|
42
58
|
jobSearch: jobSearch,
|
|
43
59
|
jobContent: jobContent
|
|
@@ -45,9 +61,10 @@ import { WorkflowRunContentSkeleton, WorkflowRunNotFound, WorkflowRunSkeleton, W
|
|
|
45
61
|
})
|
|
46
62
|
});
|
|
47
63
|
}
|
|
48
|
-
function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, runAttempt, selection, tab,
|
|
64
|
+
function RunViewContent({ workspaceSlug, projectSlug, query, annotations, rerunMutation, runAttempt, selection, tab, activeJobId, jobSearch, jobContent }) {
|
|
49
65
|
const navigate = useNavigate();
|
|
50
66
|
const runData = query.data;
|
|
67
|
+
const annotationSummary = annotations.summary;
|
|
51
68
|
const activeSection = runWorkspaceSection(tab);
|
|
52
69
|
const cancelMutation = useCancelWorkflowRunMutation(runData);
|
|
53
70
|
const sourceSnapshot = runData?.sourceSnapshot ?? null;
|
|
@@ -147,6 +164,29 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, runA
|
|
|
147
164
|
search: workflowRunSearchParams(nextSearch, nextSearch)
|
|
148
165
|
});
|
|
149
166
|
}
|
|
167
|
+
function clearAnnotationFilters() {
|
|
168
|
+
if (!runData || !workspaceSlug || !projectSlug) return;
|
|
169
|
+
const nextSearch = {
|
|
170
|
+
...selection,
|
|
171
|
+
tab: 'annotations'
|
|
172
|
+
};
|
|
173
|
+
delete nextSearch.jobId;
|
|
174
|
+
delete nextSearch.jobExecutionId;
|
|
175
|
+
delete nextSearch.stepId;
|
|
176
|
+
delete nextSearch.stepAttemptId;
|
|
177
|
+
delete nextSearch.severity;
|
|
178
|
+
delete nextSearch.annotation;
|
|
179
|
+
void navigate({
|
|
180
|
+
to: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
181
|
+
params: {
|
|
182
|
+
workspaceSlug,
|
|
183
|
+
projectSlug,
|
|
184
|
+
workflowRunId: runData.id
|
|
185
|
+
},
|
|
186
|
+
search: workflowRunSearchParams(nextSearch, nextSearch),
|
|
187
|
+
replace: true
|
|
188
|
+
});
|
|
189
|
+
}
|
|
150
190
|
function cancelRun() {
|
|
151
191
|
cancelMutation.mutate(undefined, {
|
|
152
192
|
onError: (error)=>toast.error(cancelErrorMessage(error))
|
|
@@ -193,6 +233,7 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, runA
|
|
|
193
233
|
children: runData && jobContent ? jobContent : runData ? /*#__PURE__*/ _jsx(RunSectionContent, {
|
|
194
234
|
section: activeSection,
|
|
195
235
|
run: runData,
|
|
236
|
+
annotations: annotations,
|
|
196
237
|
annotationSummary: annotationSummary,
|
|
197
238
|
workspaceSlug: workspaceSlug,
|
|
198
239
|
projectSlug: projectSlug,
|
|
@@ -200,10 +241,11 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, runA
|
|
|
200
241
|
selectedJobId: selection?.jobId,
|
|
201
242
|
onSelectGraphJob: selectGraphJob,
|
|
202
243
|
onSelectAnnotationJob: selectAnnotationJob,
|
|
244
|
+
onClearAnnotationFilters: clearAnnotationFilters,
|
|
203
245
|
sourceSnapshot: sourceSnapshot,
|
|
204
246
|
highlightedLineRange: highlightedLineRange
|
|
205
247
|
}) : /*#__PURE__*/ _jsx("div", {
|
|
206
|
-
className: "min-h-0 flex-1 overflow-auto p-
|
|
248
|
+
className: "min-h-0 flex-1 overflow-auto p-panel",
|
|
207
249
|
children: loadingOrError
|
|
208
250
|
})
|
|
209
251
|
})
|
|
@@ -212,72 +254,188 @@ function RunViewContent({ workspaceSlug, projectSlug, query, rerunMutation, runA
|
|
|
212
254
|
]
|
|
213
255
|
});
|
|
214
256
|
}
|
|
215
|
-
function RunSectionContent({ section, run, annotationSummary, workspaceSlug, projectSlug, selection, selectedJobId, onSelectGraphJob, onSelectAnnotationJob, sourceSnapshot, highlightedLineRange }) {
|
|
257
|
+
function RunSectionContent({ section, run, annotations, annotationSummary, workspaceSlug, projectSlug, selection, selectedJobId, onSelectGraphJob, onSelectAnnotationJob, onClearAnnotationFilters, sourceSnapshot, highlightedLineRange }) {
|
|
216
258
|
if (section === 'summary') {
|
|
217
259
|
return /*#__PURE__*/ _jsx("section", {
|
|
218
260
|
"aria-label": "All jobs summary",
|
|
219
|
-
className: "min-h-0 flex-1 overflow-auto pb-
|
|
220
|
-
children: /*#__PURE__*/ _jsx("div", {
|
|
221
|
-
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-16 px-24",
|
|
222
|
-
children: /*#__PURE__*/ _jsx(JobGraph, {
|
|
223
|
-
run: run,
|
|
224
|
-
selectedJobId: selectedJobId,
|
|
225
|
-
onSelectedJobChange: onSelectGraphJob,
|
|
226
|
-
className: "min-h-160 overflow-hidden"
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
if (section === 'annotations') {
|
|
232
|
-
const selectedJob = run.jobs.find((job)=>job.id === selectedJobId);
|
|
233
|
-
return /*#__PURE__*/ _jsx("section", {
|
|
234
|
-
"aria-label": "Run annotations",
|
|
235
|
-
className: "min-h-0 flex-1 overflow-auto pb-24 pt-16",
|
|
261
|
+
className: "min-h-0 flex-1 overflow-auto pb-panel pt-[16px]",
|
|
236
262
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
237
|
-
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-
|
|
263
|
+
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-group px-frame",
|
|
238
264
|
children: [
|
|
239
|
-
/*#__PURE__*/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
summary: annotationSummary,
|
|
244
|
-
workspaceSlug: workspaceSlug,
|
|
245
|
-
projectSlug: projectSlug,
|
|
246
|
-
workflowRunId: run.id,
|
|
247
|
-
search: selection
|
|
248
|
-
}),
|
|
249
|
-
/*#__PURE__*/ _jsx(AnnotationJobFilter, {
|
|
250
|
-
jobs: run.jobs,
|
|
251
|
-
selectedJobId: selectedJob?.id,
|
|
252
|
-
onSelect: onSelectAnnotationJob
|
|
253
|
-
})
|
|
254
|
-
]
|
|
265
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
266
|
+
as: "h2",
|
|
267
|
+
className: "sr-only",
|
|
268
|
+
children: "All jobs summary"
|
|
255
269
|
}),
|
|
256
|
-
/*#__PURE__*/ _jsx(
|
|
257
|
-
|
|
270
|
+
/*#__PURE__*/ _jsx(JobGraph, {
|
|
271
|
+
run: run,
|
|
272
|
+
selectedJobId: selectedJobId,
|
|
273
|
+
onSelectedJobChange: onSelectGraphJob,
|
|
274
|
+
className: "min-h-160 overflow-hidden"
|
|
258
275
|
})
|
|
259
276
|
]
|
|
260
277
|
})
|
|
261
278
|
});
|
|
262
279
|
}
|
|
280
|
+
if (section === 'annotations') {
|
|
281
|
+
return /*#__PURE__*/ _jsx(RunAnnotationsSection, {
|
|
282
|
+
run: run,
|
|
283
|
+
annotations: annotations,
|
|
284
|
+
annotationSummary: annotationSummary,
|
|
285
|
+
workspaceSlug: workspaceSlug,
|
|
286
|
+
projectSlug: projectSlug,
|
|
287
|
+
selection: selection,
|
|
288
|
+
selectedJobId: selectedJobId,
|
|
289
|
+
onSelectAnnotationJob: onSelectAnnotationJob,
|
|
290
|
+
onClearAnnotationFilters: onClearAnnotationFilters
|
|
291
|
+
});
|
|
292
|
+
}
|
|
263
293
|
return /*#__PURE__*/ _jsx("section", {
|
|
264
294
|
"aria-label": "Workflow source",
|
|
265
|
-
className: "min-h-0 flex-1 overflow-auto pb-
|
|
266
|
-
children: /*#__PURE__*/
|
|
267
|
-
className: "mx-auto flex min-h-full w-full max-w-[1120px] px-
|
|
268
|
-
children:
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
295
|
+
className: "min-h-0 flex-1 overflow-auto pb-panel pt-[16px]",
|
|
296
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
297
|
+
className: "mx-auto flex min-h-full w-full max-w-[1120px] flex-col px-frame",
|
|
298
|
+
children: [
|
|
299
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
300
|
+
as: "h2",
|
|
301
|
+
className: "sr-only",
|
|
302
|
+
children: "Workflow source"
|
|
303
|
+
}),
|
|
304
|
+
sourceSnapshot ? /*#__PURE__*/ _jsx(WorkflowSourceContent, {
|
|
305
|
+
source: sourceSnapshot,
|
|
306
|
+
highlightedLineRange: highlightedLineRange,
|
|
307
|
+
scrollHighlightedIntoView: true
|
|
308
|
+
}) : /*#__PURE__*/ _jsx(EmptyState, {
|
|
309
|
+
className: "min-h-160 w-full",
|
|
310
|
+
icon: "fileDamageLine",
|
|
311
|
+
title: "Source snapshot unavailable",
|
|
312
|
+
description: run.isTemporary ? 'Temporary runs do not capture workflow source.' : 'This run was created before workflow source snapshots were captured.'
|
|
313
|
+
})
|
|
314
|
+
]
|
|
315
|
+
})
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function RunAnnotationsSection({ run, annotations, annotationSummary, workspaceSlug, projectSlug, selection, selectedJobId, onSelectAnnotationJob, onClearAnnotationFilters }) {
|
|
319
|
+
const selectedJob = run.jobs.find((job)=>job.id === selectedJobId);
|
|
320
|
+
const severity = selection?.severity;
|
|
321
|
+
const records = annotations.annotations;
|
|
322
|
+
const entries = useMemo(()=>records ? buildRunAnnotationList({
|
|
323
|
+
annotations: records,
|
|
324
|
+
jobs: run.jobs,
|
|
325
|
+
severity,
|
|
326
|
+
jobId: selectedJob?.id
|
|
327
|
+
}) : undefined, [
|
|
328
|
+
records,
|
|
329
|
+
run.jobs,
|
|
330
|
+
selectedJob?.id,
|
|
331
|
+
severity
|
|
332
|
+
]);
|
|
333
|
+
const derivedAnnotations = useMemo(()=>{
|
|
334
|
+
if (!records) return undefined;
|
|
335
|
+
const jobsWithAnnotations = new Set(records.map((annotation)=>annotation.jobId));
|
|
336
|
+
return run.jobs.filter((job)=>{
|
|
337
|
+
const style = job.status === 'failed' ? 'error' : 'warning';
|
|
338
|
+
return (!selectedJob || selectedJob.id === job.id) && !jobsWithAnnotations.has(job.id) && (job.status === 'failed' || job.status === 'skipped') && job.jobExecutions.length === 0 && matchesDerivedAnnotationFilters(style, selection);
|
|
339
|
+
}).map((job)=>({
|
|
340
|
+
id: `derived-${job.id}`,
|
|
341
|
+
style: job.status === 'failed' ? 'error' : 'warning',
|
|
342
|
+
body: derivedJobAnnotation(job)
|
|
343
|
+
}));
|
|
344
|
+
}, [
|
|
345
|
+
records,
|
|
346
|
+
run.jobs,
|
|
347
|
+
selectedJob,
|
|
348
|
+
selection
|
|
349
|
+
]);
|
|
350
|
+
const hasSynthesizedJobAnnotations = run.jobs.some((job)=>(job.status === 'failed' || job.status === 'skipped') && job.jobExecutions.length === 0);
|
|
351
|
+
return /*#__PURE__*/ _jsx("section", {
|
|
352
|
+
"aria-label": "Run annotations",
|
|
353
|
+
className: "min-h-0 flex-1 overflow-auto pb-panel pt-[16px]",
|
|
354
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
355
|
+
className: "mx-auto flex w-full max-w-[1120px] flex-col gap-group px-frame",
|
|
356
|
+
children: [
|
|
357
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
358
|
+
as: "h2",
|
|
359
|
+
className: "sr-only",
|
|
360
|
+
children: "Annotations"
|
|
361
|
+
}),
|
|
362
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
363
|
+
className: "flex flex-wrap items-center justify-between gap-inline",
|
|
364
|
+
children: [
|
|
365
|
+
/*#__PURE__*/ _jsx(RunAnnotationSummaryLine, {
|
|
366
|
+
summary: annotationSummary,
|
|
367
|
+
workspaceSlug: workspaceSlug,
|
|
368
|
+
projectSlug: projectSlug,
|
|
369
|
+
workflowRunId: run.id,
|
|
370
|
+
search: selection
|
|
371
|
+
}),
|
|
372
|
+
/*#__PURE__*/ _jsx(AnnotationJobFilter, {
|
|
373
|
+
jobs: run.jobs,
|
|
374
|
+
selectedJobId: selectedJob?.id,
|
|
375
|
+
onSelect: onSelectAnnotationJob
|
|
376
|
+
})
|
|
377
|
+
]
|
|
378
|
+
}),
|
|
379
|
+
/*#__PURE__*/ _jsx(RunAnnotationList, {
|
|
380
|
+
query: annotations.query,
|
|
381
|
+
entries: entries,
|
|
382
|
+
derivedAnnotations: derivedAnnotations,
|
|
383
|
+
workspaceSlug: workspaceSlug,
|
|
384
|
+
projectSlug: projectSlug,
|
|
385
|
+
workflowRunId: run.id,
|
|
386
|
+
runAttempt: run.runAttempt.attempt,
|
|
387
|
+
// A run with no annotations at all offers no filter to clear, whatever the URL says.
|
|
388
|
+
filtered: Boolean((severity || selectedJob || selection?.annotation) && ((annotationSummary?.total ?? 0) > 0 || hasSynthesizedJobAnnotations)),
|
|
389
|
+
filteredJobName: selectedJob?.displayName,
|
|
390
|
+
filteredSeverity: severity,
|
|
391
|
+
onClearFilters: onClearAnnotationFilters,
|
|
392
|
+
selectedAnnotationId: selection?.annotation
|
|
393
|
+
}, `${run.id}:${run.runAttempt.attempt}:${severity ?? 'all'}:${selectedJob?.id ?? 'all'}`)
|
|
394
|
+
]
|
|
278
395
|
})
|
|
279
396
|
});
|
|
280
397
|
}
|
|
398
|
+
function matchesDerivedAnnotationFilters(style, selection) {
|
|
399
|
+
if (selection?.severity && style !== selection.severity) return false;
|
|
400
|
+
// A selected annotation is a deep-link to a real annotation record. Synthetic diagnostics
|
|
401
|
+
// have no id or context, so they must not remain visible behind that selection.
|
|
402
|
+
return !selection?.annotation;
|
|
403
|
+
}
|
|
404
|
+
function derivedJobAnnotation(job) {
|
|
405
|
+
const reason = job.statusReason ? `Reason: \`${job.statusReason}\`` : null;
|
|
406
|
+
const traceSummary = formatConditionEvaluation(job.evaluationTrace);
|
|
407
|
+
const details = [
|
|
408
|
+
reason,
|
|
409
|
+
traceSummary
|
|
410
|
+
].filter(Boolean).join('\n');
|
|
411
|
+
if (job.status === 'skipped') {
|
|
412
|
+
return [
|
|
413
|
+
`**${job.displayName}** was skipped before an execution was created.`,
|
|
414
|
+
'',
|
|
415
|
+
'Review its dependencies or condition before re-running.',
|
|
416
|
+
details
|
|
417
|
+
].filter(Boolean).join('\n');
|
|
418
|
+
}
|
|
419
|
+
return [
|
|
420
|
+
`**${job.displayName}** failed before an execution was created.`,
|
|
421
|
+
'',
|
|
422
|
+
'Check runner availability and workflow configuration before re-running.',
|
|
423
|
+
details
|
|
424
|
+
].filter(Boolean).join('\n');
|
|
425
|
+
}
|
|
426
|
+
function formatConditionEvaluation(trace) {
|
|
427
|
+
if (!trace?.length) return null;
|
|
428
|
+
return [
|
|
429
|
+
'Condition evaluation:',
|
|
430
|
+
...trace.map((entry)=>{
|
|
431
|
+
if ('dropped' in entry) {
|
|
432
|
+
return `- ${entry.dropped} additional evaluation${entry.dropped === 1 ? '' : 's'} not recorded`;
|
|
433
|
+
}
|
|
434
|
+
const value = entry.value === undefined || entry.value === '' ? '(empty)' : `\`${entry.value}\``;
|
|
435
|
+
return `- \`${entry.field}\` evaluated \`${entry.expression}\` to ${value}${entry.degraded ? ' (degraded)' : ''}`;
|
|
436
|
+
})
|
|
437
|
+
].join('\n');
|
|
438
|
+
}
|
|
281
439
|
const ALL_ANNOTATION_JOBS = 'all-jobs';
|
|
282
440
|
function AnnotationJobFilter({ jobs, selectedJobId, onSelect }) {
|
|
283
441
|
const sortedJobs = [
|
|
@@ -312,15 +470,15 @@ function AnnotationJobFilter({ jobs, selectedJobId, onSelect }) {
|
|
|
312
470
|
function RunWorkspaceNavSkeleton() {
|
|
313
471
|
return /*#__PURE__*/ _jsx("aside", {
|
|
314
472
|
"aria-label": "Loading run navigation",
|
|
315
|
-
className: "hidden w-240 shrink-0 border-r border-border-neutral-base bg-background-neutral-background p-
|
|
473
|
+
className: "hidden w-240 shrink-0 border-r border-border-neutral-base bg-background-neutral-background p-panel-compact min-[768px]:block",
|
|
316
474
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
317
|
-
className: "flex flex-col gap-
|
|
475
|
+
className: "flex flex-col gap-group",
|
|
318
476
|
children: [
|
|
319
477
|
/*#__PURE__*/ _jsx("div", {
|
|
320
478
|
className: "h-32 rounded-4 bg-background-components-subtle"
|
|
321
479
|
}),
|
|
322
480
|
/*#__PURE__*/ _jsxs("div", {
|
|
323
|
-
className: "flex flex-col gap-
|
|
481
|
+
className: "flex flex-col gap-inline",
|
|
324
482
|
children: [
|
|
325
483
|
/*#__PURE__*/ _jsx("div", {
|
|
326
484
|
className: "h-16 w-64 rounded-4 bg-background-components-subtle"
|