@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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AnnotationCard, QueryLoadError } from '@shipfox/client-ui';
|
|
3
|
+
import { Button } from '@shipfox/react-ui/button';
|
|
4
|
+
import { Callout } from '@shipfox/react-ui/callout';
|
|
5
|
+
import { EmptyState } from '@shipfox/react-ui/empty-state';
|
|
6
|
+
import { Skeleton } from '@shipfox/react-ui/skeleton';
|
|
7
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
|
+
import { useState } from 'react';
|
|
9
|
+
import { RunAnnotationItem } from './run-annotation-item.js';
|
|
10
|
+
import { RunAnnotationsEmpty } from './run-annotations-empty.js';
|
|
11
|
+
/**
|
|
12
|
+
* How many annotations render at once.
|
|
13
|
+
*
|
|
14
|
+
* A page holds up to 500 rows and each one mounts a Markdown pipeline, so the fetch budget is
|
|
15
|
+
* not a render budget. The window grows on demand and the server page only loads once the
|
|
16
|
+
* reader has actually seen everything already fetched.
|
|
17
|
+
*/ const RENDER_WINDOW = 25;
|
|
18
|
+
/**
|
|
19
|
+
* The annotations list.
|
|
20
|
+
*
|
|
21
|
+
* Loading, empty, error, and stale are four distinct renderings on purpose: coalescing a failed
|
|
22
|
+
* fetch into an empty result tells a reader their run is clean when the truth is unknown.
|
|
23
|
+
*/ export function RunAnnotationList({ query, entries, derivedAnnotations = [], workflowRunId, workspaceSlug, projectSlug, runAttempt, filteredJobName, filteredSeverity, filtered, onClearFilters, selectedAnnotationId }) {
|
|
24
|
+
const [visibleCount, setVisibleCount] = useState(RENDER_WINDOW);
|
|
25
|
+
// A deep-linked annotation past the window would otherwise be unreachable: it is in the data,
|
|
26
|
+
// absent from the DOM, and nothing on screen says so.
|
|
27
|
+
const selectedIndex = selectedAnnotationId ? entries?.findIndex((entry)=>entry.annotation.id === selectedAnnotationId) ?? -1 : -1;
|
|
28
|
+
const renderCount = Math.max(visibleCount, selectedIndex + 1);
|
|
29
|
+
if (query.isPending) return /*#__PURE__*/ _jsx(RunAnnotationListSkeleton, {});
|
|
30
|
+
if (query.isError && entries === undefined) {
|
|
31
|
+
return /*#__PURE__*/ _jsx(QueryLoadError, {
|
|
32
|
+
query: query,
|
|
33
|
+
subject: "annotations",
|
|
34
|
+
icon: "fileDamageLine"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (entries === undefined) return /*#__PURE__*/ _jsx(RunAnnotationListSkeleton, {});
|
|
38
|
+
const visible = entries.slice(0, renderCount);
|
|
39
|
+
const hiddenCount = entries.length - visible.length;
|
|
40
|
+
const hasContent = entries.length > 0 || derivedAnnotations.length > 0;
|
|
41
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
42
|
+
className: "flex min-w-0 flex-col gap-cluster",
|
|
43
|
+
children: [
|
|
44
|
+
query.isError ? /*#__PURE__*/ _jsx(RunAnnotationStaleError, {
|
|
45
|
+
query: query
|
|
46
|
+
}) : null,
|
|
47
|
+
!hasContent ? filtered ? /*#__PURE__*/ _jsx(RunAnnotationsFilteredEmpty, {
|
|
48
|
+
jobName: filteredJobName,
|
|
49
|
+
severity: filteredSeverity,
|
|
50
|
+
incomplete: query.hasNextPage,
|
|
51
|
+
onClearFilters: onClearFilters
|
|
52
|
+
}) : /*#__PURE__*/ _jsx(RunAnnotationsEmpty, {}) : /*#__PURE__*/ _jsxs("ol", {
|
|
53
|
+
className: "flex min-w-0 flex-col gap-cluster",
|
|
54
|
+
children: [
|
|
55
|
+
visible.map((entry)=>/*#__PURE__*/ _jsx(RunAnnotationItem, {
|
|
56
|
+
entry: entry,
|
|
57
|
+
workspaceSlug: workspaceSlug,
|
|
58
|
+
projectSlug: projectSlug,
|
|
59
|
+
workflowRunId: workflowRunId,
|
|
60
|
+
runAttempt: runAttempt,
|
|
61
|
+
selected: entry.annotation.id === selectedAnnotationId
|
|
62
|
+
}, entry.annotation.id)),
|
|
63
|
+
derivedAnnotations.map((annotation)=>/*#__PURE__*/ _jsx("li", {
|
|
64
|
+
children: /*#__PURE__*/ _jsx(AnnotationCard, {
|
|
65
|
+
style: annotation.style,
|
|
66
|
+
body: annotation.body
|
|
67
|
+
})
|
|
68
|
+
}, annotation.id))
|
|
69
|
+
]
|
|
70
|
+
}),
|
|
71
|
+
hiddenCount > 0 ? /*#__PURE__*/ _jsxs(Button, {
|
|
72
|
+
type: "button",
|
|
73
|
+
size: "sm",
|
|
74
|
+
variant: "secondary",
|
|
75
|
+
className: "self-start [@media(pointer:coarse)]:min-h-44",
|
|
76
|
+
onClick: ()=>setVisibleCount((current)=>current + RENDER_WINDOW),
|
|
77
|
+
children: [
|
|
78
|
+
"Show ",
|
|
79
|
+
Math.min(hiddenCount, RENDER_WINDOW),
|
|
80
|
+
" more of ",
|
|
81
|
+
entries.length
|
|
82
|
+
]
|
|
83
|
+
}) : query.hasNextPage ? /*#__PURE__*/ _jsx(Button, {
|
|
84
|
+
type: "button",
|
|
85
|
+
size: "sm",
|
|
86
|
+
variant: "secondary",
|
|
87
|
+
className: "self-start [@media(pointer:coarse)]:min-h-44",
|
|
88
|
+
isLoading: query.isFetchingNextPage,
|
|
89
|
+
onClick: ()=>{
|
|
90
|
+
void query.fetchNextPage();
|
|
91
|
+
},
|
|
92
|
+
children: "Load more annotations"
|
|
93
|
+
}) : null
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Names the filters that are actually active.
|
|
99
|
+
*
|
|
100
|
+
* Blaming severity for a job-only filter sends the reader to change the wrong control, which
|
|
101
|
+
* is worse than saying nothing.
|
|
102
|
+
*/ function filteredEmptyDescription({ jobName, severity, incomplete }) {
|
|
103
|
+
if (incomplete) {
|
|
104
|
+
if (jobName && severity) {
|
|
105
|
+
return `None of the loaded annotations are from ${jobName} at ${severity} severity. Load more annotations to continue searching.`;
|
|
106
|
+
}
|
|
107
|
+
if (jobName) {
|
|
108
|
+
return `None of the loaded annotations are from ${jobName}. Load more annotations to continue searching.`;
|
|
109
|
+
}
|
|
110
|
+
if (severity) {
|
|
111
|
+
return `None of the loaded annotations are at ${severity} severity. Load more annotations to continue searching.`;
|
|
112
|
+
}
|
|
113
|
+
return 'None of the loaded annotations match the current filters. Load more annotations to continue searching.';
|
|
114
|
+
}
|
|
115
|
+
if (jobName && severity) {
|
|
116
|
+
return `This run has annotations, but none from ${jobName} at ${severity} severity.`;
|
|
117
|
+
}
|
|
118
|
+
if (jobName) return `This run has annotations, but none from ${jobName}.`;
|
|
119
|
+
if (severity) return `This run has annotations, but none at ${severity} severity.`;
|
|
120
|
+
return 'This run has annotations, but none match the current filters.';
|
|
121
|
+
}
|
|
122
|
+
function RunAnnotationsFilteredEmpty({ jobName, severity, incomplete, onClearFilters }) {
|
|
123
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
124
|
+
className: "flex flex-col items-center gap-cluster",
|
|
125
|
+
children: [
|
|
126
|
+
/*#__PURE__*/ _jsx(EmptyState, {
|
|
127
|
+
icon: "fileDamageLine",
|
|
128
|
+
title: incomplete ? 'No matches in loaded annotations' : 'No matching annotations',
|
|
129
|
+
description: filteredEmptyDescription({
|
|
130
|
+
jobName,
|
|
131
|
+
severity,
|
|
132
|
+
incomplete
|
|
133
|
+
})
|
|
134
|
+
}),
|
|
135
|
+
onClearFilters ? /*#__PURE__*/ _jsx(Button, {
|
|
136
|
+
type: "button",
|
|
137
|
+
size: "sm",
|
|
138
|
+
variant: "secondary",
|
|
139
|
+
className: "[@media(pointer:coarse)]:min-h-44",
|
|
140
|
+
onClick: onClearFilters,
|
|
141
|
+
children: "Clear filters"
|
|
142
|
+
}) : null
|
|
143
|
+
]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A failed poll keeps the annotations already on screen and marks them stale rather than
|
|
148
|
+
* clearing. Polite rather than assertive: the poll runs every 4s and a flapping API would
|
|
149
|
+
* otherwise interrupt a screen reader on every cycle.
|
|
150
|
+
*/ function RunAnnotationStaleError({ query }) {
|
|
151
|
+
return /*#__PURE__*/ _jsx(Callout, {
|
|
152
|
+
role: "status",
|
|
153
|
+
"aria-live": "polite",
|
|
154
|
+
type: "error",
|
|
155
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
156
|
+
className: "flex w-full items-center justify-between gap-inline",
|
|
157
|
+
children: [
|
|
158
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
159
|
+
size: "xs",
|
|
160
|
+
children: "Could not refresh annotations."
|
|
161
|
+
}),
|
|
162
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
163
|
+
type: "button",
|
|
164
|
+
size: "2xs",
|
|
165
|
+
variant: "secondary",
|
|
166
|
+
className: "[@media(pointer:coarse)]:min-h-44",
|
|
167
|
+
isLoading: query.isFetching,
|
|
168
|
+
onClick: ()=>{
|
|
169
|
+
void query.refetch();
|
|
170
|
+
},
|
|
171
|
+
children: "Retry"
|
|
172
|
+
})
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const ANNOTATION_SKELETON_ROWS = [
|
|
178
|
+
'first',
|
|
179
|
+
'second',
|
|
180
|
+
'third'
|
|
181
|
+
];
|
|
182
|
+
function RunAnnotationListSkeleton() {
|
|
183
|
+
return /*#__PURE__*/ _jsx("section", {
|
|
184
|
+
"aria-label": "Loading annotations",
|
|
185
|
+
className: "flex flex-col gap-cluster",
|
|
186
|
+
children: ANNOTATION_SKELETON_ROWS.map((row)=>/*#__PURE__*/ _jsxs("div", {
|
|
187
|
+
className: "flex gap-cluster rounded-8 border border-border-neutral-base bg-background-components-base px-[12px] py-[8px]",
|
|
188
|
+
children: [
|
|
189
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
190
|
+
className: "h-40 w-4 rounded-full"
|
|
191
|
+
}),
|
|
192
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
193
|
+
className: "flex min-w-0 flex-1 flex-col gap-inline",
|
|
194
|
+
children: [
|
|
195
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
196
|
+
className: "h-20 w-160 rounded-4"
|
|
197
|
+
}),
|
|
198
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
199
|
+
className: "h-16 w-240 rounded-4"
|
|
200
|
+
}),
|
|
201
|
+
/*#__PURE__*/ _jsx(Skeleton, {
|
|
202
|
+
className: "h-40 w-full rounded-4"
|
|
203
|
+
})
|
|
204
|
+
]
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
}, row))
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=run-annotation-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-list.tsx"],"sourcesContent":["import {AnnotationCard, QueryLoadError, 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';\nimport {useState} from 'react';\nimport type {RunAnnotationEntry, RunAnnotationStyle} from '#core/run-annotation.js';\nimport {RunAnnotationItem} from './run-annotation-item.js';\nimport {RunAnnotationsEmpty} from './run-annotations-empty.js';\n\nexport interface RunAnnotationListQuery extends QueryLoadErrorQuery {\n isPending: boolean;\n hasNextPage: boolean;\n isFetchingNextPage: boolean;\n fetchNextPage: () => unknown;\n}\n\nexport interface RunAnnotationListProps {\n query: RunAnnotationListQuery;\n entries: RunAnnotationEntry[] | undefined;\n /** Synthetic terminal-job diagnostics for jobs that never created an execution record. */\n derivedAnnotations?: readonly DerivedRunAnnotation[] | undefined;\n workflowRunId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n runAttempt?: number | undefined;\n /** Name of the job the list is filtered to, used only for empty-state copy. */\n filteredJobName?: string | undefined;\n /** Active severity filter, named in the empty state so the reader knows what to clear. */\n filteredSeverity?: string | undefined;\n filtered: boolean;\n onClearFilters?: (() => void) | undefined;\n /** Annotation id from `?annotation=`, which the matching card scrolls to and focuses. */\n selectedAnnotationId?: string | undefined;\n}\n\nexport interface DerivedRunAnnotation {\n id: string;\n style: RunAnnotationStyle;\n body: string;\n}\n\n/**\n * How many annotations render at once.\n *\n * A page holds up to 500 rows and each one mounts a Markdown pipeline, so the fetch budget is\n * not a render budget. The window grows on demand and the server page only loads once the\n * reader has actually seen everything already fetched.\n */\nconst RENDER_WINDOW = 25;\n\n/**\n * The annotations list.\n *\n * Loading, empty, error, and stale are four distinct renderings on purpose: coalescing a failed\n * fetch into an empty result tells a reader their run is clean when the truth is unknown.\n */\nexport function RunAnnotationList({\n query,\n entries,\n derivedAnnotations = [],\n workflowRunId,\n workspaceSlug,\n projectSlug,\n runAttempt,\n filteredJobName,\n filteredSeverity,\n filtered,\n onClearFilters,\n selectedAnnotationId,\n}: RunAnnotationListProps) {\n const [visibleCount, setVisibleCount] = useState(RENDER_WINDOW);\n\n // A deep-linked annotation past the window would otherwise be unreachable: it is in the data,\n // absent from the DOM, and nothing on screen says so.\n const selectedIndex = selectedAnnotationId\n ? (entries?.findIndex((entry) => entry.annotation.id === selectedAnnotationId) ?? -1)\n : -1;\n const renderCount = Math.max(visibleCount, selectedIndex + 1);\n\n if (query.isPending) return <RunAnnotationListSkeleton />;\n\n if (query.isError && entries === undefined) {\n return <QueryLoadError query={query} subject=\"annotations\" icon=\"fileDamageLine\" />;\n }\n\n if (entries === undefined) return <RunAnnotationListSkeleton />;\n\n const visible = entries.slice(0, renderCount);\n const hiddenCount = entries.length - visible.length;\n const hasContent = entries.length > 0 || derivedAnnotations.length > 0;\n\n return (\n <div className=\"flex min-w-0 flex-col gap-cluster\">\n {query.isError ? <RunAnnotationStaleError query={query} /> : null}\n\n {!hasContent ? (\n filtered ? (\n <RunAnnotationsFilteredEmpty\n jobName={filteredJobName}\n severity={filteredSeverity}\n incomplete={query.hasNextPage}\n onClearFilters={onClearFilters}\n />\n ) : (\n <RunAnnotationsEmpty />\n )\n ) : (\n <ol className=\"flex min-w-0 flex-col gap-cluster\">\n {visible.map((entry) => (\n <RunAnnotationItem\n key={entry.annotation.id}\n entry={entry}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n runAttempt={runAttempt}\n selected={entry.annotation.id === selectedAnnotationId}\n />\n ))}\n {derivedAnnotations.map((annotation) => (\n <li key={annotation.id}>\n <AnnotationCard style={annotation.style} body={annotation.body} />\n </li>\n ))}\n </ol>\n )}\n\n {hiddenCount > 0 ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"self-start [@media(pointer:coarse)]:min-h-44\"\n onClick={() => setVisibleCount((current) => current + RENDER_WINDOW)}\n >\n Show {Math.min(hiddenCount, RENDER_WINDOW)} more of {entries.length}\n </Button>\n ) : query.hasNextPage ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"self-start [@media(pointer:coarse)]:min-h-44\"\n isLoading={query.isFetchingNextPage}\n onClick={() => {\n void query.fetchNextPage();\n }}\n >\n Load more annotations\n </Button>\n ) : null}\n </div>\n );\n}\n\n/**\n * Names the filters that are actually active.\n *\n * Blaming severity for a job-only filter sends the reader to change the wrong control, which\n * is worse than saying nothing.\n */\nfunction filteredEmptyDescription({\n jobName,\n severity,\n incomplete,\n}: {\n jobName: string | undefined;\n severity: string | undefined;\n incomplete: boolean;\n}): string {\n if (incomplete) {\n if (jobName && severity) {\n return `None of the loaded annotations are from ${jobName} at ${severity} severity. Load more annotations to continue searching.`;\n }\n if (jobName) {\n return `None of the loaded annotations are from ${jobName}. Load more annotations to continue searching.`;\n }\n if (severity) {\n return `None of the loaded annotations are at ${severity} severity. Load more annotations to continue searching.`;\n }\n return 'None of the loaded annotations match the current filters. Load more annotations to continue searching.';\n }\n if (jobName && severity) {\n return `This run has annotations, but none from ${jobName} at ${severity} severity.`;\n }\n if (jobName) return `This run has annotations, but none from ${jobName}.`;\n if (severity) return `This run has annotations, but none at ${severity} severity.`;\n return 'This run has annotations, but none match the current filters.';\n}\n\nfunction RunAnnotationsFilteredEmpty({\n jobName,\n severity,\n incomplete,\n onClearFilters,\n}: {\n jobName: string | undefined;\n severity: string | undefined;\n incomplete: boolean;\n onClearFilters: (() => void) | undefined;\n}) {\n return (\n <div className=\"flex flex-col items-center gap-cluster\">\n <EmptyState\n icon=\"fileDamageLine\"\n title={incomplete ? 'No matches in loaded annotations' : 'No matching annotations'}\n description={filteredEmptyDescription({jobName, severity, incomplete})}\n />\n {onClearFilters ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n className=\"[@media(pointer:coarse)]:min-h-44\"\n onClick={onClearFilters}\n >\n Clear filters\n </Button>\n ) : null}\n </div>\n );\n}\n\n/**\n * A failed poll keeps the annotations already on screen and marks them stale rather than\n * clearing. Polite rather than assertive: the poll runs every 4s and a flapping API would\n * otherwise interrupt a screen reader on every cycle.\n */\nfunction RunAnnotationStaleError({query}: {query: QueryLoadErrorQuery}) {\n return (\n <Callout role=\"status\" aria-live=\"polite\" type=\"error\">\n <div className=\"flex w-full items-center justify-between gap-inline\">\n <Text size=\"xs\">Could not refresh annotations.</Text>\n <Button\n type=\"button\"\n size=\"2xs\"\n variant=\"secondary\"\n className=\"[@media(pointer:coarse)]:min-h-44\"\n isLoading={query.isFetching}\n onClick={() => {\n void query.refetch();\n }}\n >\n Retry\n </Button>\n </div>\n </Callout>\n );\n}\n\nconst ANNOTATION_SKELETON_ROWS = ['first', 'second', 'third'];\n\nfunction RunAnnotationListSkeleton() {\n return (\n <section aria-label=\"Loading annotations\" className=\"flex flex-col gap-cluster\">\n {ANNOTATION_SKELETON_ROWS.map((row) => (\n <div\n key={row}\n className=\"flex gap-cluster rounded-8 border border-border-neutral-base bg-background-components-base px-[12px] py-[8px]\"\n >\n <Skeleton className=\"h-40 w-4 rounded-full\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-inline\">\n <Skeleton className=\"h-20 w-160 rounded-4\" />\n <Skeleton className=\"h-16 w-240 rounded-4\" />\n <Skeleton className=\"h-40 w-full rounded-4\" />\n </div>\n </div>\n ))}\n </section>\n );\n}\n"],"names":["AnnotationCard","QueryLoadError","Button","Callout","EmptyState","Skeleton","Text","useState","RunAnnotationItem","RunAnnotationsEmpty","RENDER_WINDOW","RunAnnotationList","query","entries","derivedAnnotations","workflowRunId","workspaceSlug","projectSlug","runAttempt","filteredJobName","filteredSeverity","filtered","onClearFilters","selectedAnnotationId","visibleCount","setVisibleCount","selectedIndex","findIndex","entry","annotation","id","renderCount","Math","max","isPending","RunAnnotationListSkeleton","isError","undefined","subject","icon","visible","slice","hiddenCount","length","hasContent","div","className","RunAnnotationStaleError","RunAnnotationsFilteredEmpty","jobName","severity","incomplete","hasNextPage","ol","map","selected","li","style","body","type","size","variant","onClick","current","min","isLoading","isFetchingNextPage","fetchNextPage","filteredEmptyDescription","title","description","role","aria-live","isFetching","refetch","ANNOTATION_SKELETON_ROWS","section","aria-label","row"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RunAnnotationSummary } from '#core/
|
|
1
|
+
import type { RunAnnotationSummary } from '#core/run-annotation.js';
|
|
2
2
|
import { type WorkflowRunsSearch } from '#routes/inputs.js';
|
|
3
3
|
export interface RunAnnotationSummaryLineProps {
|
|
4
4
|
summary?: RunAnnotationSummary | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-annotation-summary-line.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-annotation-summary-line.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAgB3B,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,aAAa,EACb,WAAW,EACX,aAAa,EACb,MAAW,GACZ,EAAE,6BAA6B,sCAsD/B"}
|
|
@@ -1,30 +1,66 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { MetadataSeparator } from '@shipfox/client-ui';
|
|
3
|
+
import { Icon } from '@shipfox/react-ui/icon';
|
|
2
4
|
import { Text } from '@shipfox/react-ui/typography';
|
|
5
|
+
import { cn } from '@shipfox/react-ui/utils';
|
|
3
6
|
import { Link } from '@tanstack/react-router';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
7
|
+
import { workflowRunSearchParams } from '#routes/inputs.js';
|
|
8
|
+
import { SEVERITY_ICON, SEVERITY_ICON_TONE } from './severity-visuals.js';
|
|
9
|
+
/**
|
|
10
|
+
* The severities this line names.
|
|
11
|
+
*
|
|
12
|
+
* The line exists so a failing run cannot look clean while the explanation sits behind an
|
|
13
|
+
* inactive section, which makes its subject "what needs attention", not "what happened".
|
|
14
|
+
* `info` and `success` are already inside the total, and repeating them gives "nothing is
|
|
15
|
+
* wrong" the same weight and the same accent as "something failed".
|
|
16
|
+
*/ const ATTENTION_SEVERITIES = [
|
|
17
|
+
'error',
|
|
18
|
+
'warning'
|
|
19
|
+
];
|
|
6
20
|
export function RunAnnotationSummaryLine({ summary, workspaceSlug, projectSlug, workflowRunId, search = {} }) {
|
|
7
|
-
|
|
8
|
-
|
|
21
|
+
// A run with no annotations says so in its empty state; a "0 annotations" line beside it is
|
|
22
|
+
// the same fact twice.
|
|
23
|
+
if (!summary || summary.total === 0) return null;
|
|
24
|
+
const visibleSeverities = ATTENTION_SEVERITIES.filter((severity)=>summary[severity] > 0);
|
|
25
|
+
const totalLabel = countLabel(summary.total, 'annotation', summary.truncated);
|
|
9
26
|
return /*#__PURE__*/ _jsxs("div", {
|
|
10
|
-
className: "flex flex-wrap items-center gap-x-
|
|
27
|
+
className: "flex flex-wrap items-center gap-x-inline gap-y-tight text-foreground-neutral-subtle",
|
|
11
28
|
children: [
|
|
12
|
-
|
|
29
|
+
search.severity && workspaceSlug && projectSlug && workflowRunId ? // The severity filter has no control of its own, so while one is active the total is
|
|
30
|
+
// the way back out. Unfiltered it stays plain text: a link to where you already are
|
|
31
|
+
// is noise.
|
|
32
|
+
/*#__PURE__*/ _jsx(Link, {
|
|
33
|
+
to: "/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId",
|
|
34
|
+
params: {
|
|
35
|
+
workspaceSlug,
|
|
36
|
+
projectSlug,
|
|
37
|
+
workflowRunId
|
|
38
|
+
},
|
|
39
|
+
search: workflowRunSearchParams({
|
|
40
|
+
...withoutSeverity(search),
|
|
41
|
+
tab: 'annotations'
|
|
42
|
+
}, search),
|
|
43
|
+
"aria-label": `Show all ${totalLabel}`,
|
|
44
|
+
className: "shrink-0 rounded-4 outline-none focus-visible:shadow-border-interactive-with-active",
|
|
45
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
46
|
+
as: "span",
|
|
47
|
+
size: "xs",
|
|
48
|
+
className: "text-foreground-neutral-subtle underline",
|
|
49
|
+
children: totalLabel
|
|
50
|
+
})
|
|
51
|
+
}) : /*#__PURE__*/ _jsx(Text, {
|
|
13
52
|
as: "span",
|
|
14
53
|
size: "xs",
|
|
15
54
|
className: "shrink-0 text-foreground-neutral-subtle",
|
|
16
|
-
children:
|
|
17
|
-
summary.total,
|
|
18
|
-
" ",
|
|
19
|
-
pluralize('annotation', summary.total)
|
|
20
|
-
]
|
|
55
|
+
children: totalLabel
|
|
21
56
|
}),
|
|
22
57
|
visibleSeverities.map((severity)=>/*#__PURE__*/ _jsxs("span", {
|
|
23
|
-
className: "inline-flex shrink-0 items-center gap-
|
|
58
|
+
className: "inline-flex shrink-0 items-center gap-inline",
|
|
24
59
|
children: [
|
|
25
60
|
/*#__PURE__*/ _jsx(MetadataSeparator, {}),
|
|
26
61
|
/*#__PURE__*/ _jsx(SeverityLink, {
|
|
27
62
|
count: summary[severity],
|
|
63
|
+
truncated: summary.truncated,
|
|
28
64
|
severity: severity,
|
|
29
65
|
workspaceSlug: workspaceSlug,
|
|
30
66
|
projectSlug: projectSlug,
|
|
@@ -36,18 +72,31 @@ export function RunAnnotationSummaryLine({ summary, workspaceSlug, projectSlug,
|
|
|
36
72
|
]
|
|
37
73
|
});
|
|
38
74
|
}
|
|
39
|
-
function SeverityLink({ count, severity, workspaceSlug, projectSlug, workflowRunId, search }) {
|
|
40
|
-
const label =
|
|
75
|
+
function SeverityLink({ count, truncated, severity, workspaceSlug, projectSlug, workflowRunId, search }) {
|
|
76
|
+
const label = countLabel(count, severity, truncated);
|
|
77
|
+
const content = /*#__PURE__*/ _jsxs(_Fragment, {
|
|
78
|
+
children: [
|
|
79
|
+
/*#__PURE__*/ _jsx(Icon, {
|
|
80
|
+
name: SEVERITY_ICON[severity],
|
|
81
|
+
size: 12,
|
|
82
|
+
"aria-hidden": "true",
|
|
83
|
+
className: cn('shrink-0', SEVERITY_ICON_TONE[severity])
|
|
84
|
+
}),
|
|
85
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
86
|
+
as: "span",
|
|
87
|
+
size: "xs",
|
|
88
|
+
className: "text-foreground-neutral-subtle",
|
|
89
|
+
children: label
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
});
|
|
41
93
|
if (!workspaceSlug || !projectSlug || !workflowRunId) {
|
|
42
94
|
return /*#__PURE__*/ _jsx("span", {
|
|
43
|
-
className: "
|
|
44
|
-
children:
|
|
95
|
+
className: "inline-flex items-center gap-tight",
|
|
96
|
+
children: content
|
|
45
97
|
});
|
|
46
98
|
}
|
|
47
|
-
const searchWithoutAnnotation =
|
|
48
|
-
...search
|
|
49
|
-
};
|
|
50
|
-
delete searchWithoutAnnotation.annotation;
|
|
99
|
+
const searchWithoutAnnotation = withoutAnnotation(search);
|
|
51
100
|
return /*#__PURE__*/ _jsx(Link, {
|
|
52
101
|
to: "/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId",
|
|
53
102
|
params: {
|
|
@@ -60,12 +109,28 @@ function SeverityLink({ count, severity, workspaceSlug, projectSlug, workflowRun
|
|
|
60
109
|
tab: 'annotations',
|
|
61
110
|
severity
|
|
62
111
|
}, search),
|
|
63
|
-
className: "
|
|
64
|
-
children:
|
|
112
|
+
className: "inline-flex items-center gap-tight rounded-4 outline-none hover:underline focus-visible:shadow-border-interactive-with-active",
|
|
113
|
+
children: content
|
|
65
114
|
});
|
|
66
115
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
116
|
+
/** A selected annotation is stale the moment the list it was chosen from is refiltered. */ function withoutAnnotation(search) {
|
|
117
|
+
const next = {
|
|
118
|
+
...search
|
|
119
|
+
};
|
|
120
|
+
delete next.annotation;
|
|
121
|
+
return next;
|
|
122
|
+
}
|
|
123
|
+
function withoutSeverity(search) {
|
|
124
|
+
const next = withoutAnnotation(search);
|
|
125
|
+
delete next.severity;
|
|
126
|
+
return next;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* A truncated read renders `500+ annotations`: the counts are a lower bound once the page budget
|
|
130
|
+
* is hit, and presenting them as exact would understate a run the reader is trying to trust.
|
|
131
|
+
*/ function countLabel(count, word, truncated) {
|
|
132
|
+
const plural = count === 1 && !truncated ? word : `${word}s`;
|
|
133
|
+
return `${count}${truncated ? '+' : ''} ${plural}`;
|
|
69
134
|
}
|
|
70
135
|
|
|
71
136
|
//# sourceMappingURL=run-annotation-summary-line.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"sourcesContent":["import {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport type {RunAnnotationSummary} from '#core/
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-tabs/run-annotation-summary-line.tsx"],"sourcesContent":["import {MetadataSeparator} from '@shipfox/client-ui';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport type {RunAnnotationSummary} from '#core/run-annotation.js';\nimport {\n type WorkflowRunAnnotationSeverity,\n type WorkflowRunsSearch,\n workflowRunSearchParams,\n} from '#routes/inputs.js';\nimport {SEVERITY_ICON, SEVERITY_ICON_TONE} from './severity-visuals.js';\n\n/**\n * The severities this line names.\n *\n * The line exists so a failing run cannot look clean while the explanation sits behind an\n * inactive section, which makes its subject \"what needs attention\", not \"what happened\".\n * `info` and `success` are already inside the total, and repeating them gives \"nothing is\n * wrong\" the same weight and the same accent as \"something failed\".\n */\nconst ATTENTION_SEVERITIES = [\n 'error',\n 'warning',\n] as const satisfies readonly WorkflowRunAnnotationSeverity[];\n\nexport interface RunAnnotationSummaryLineProps {\n summary?: RunAnnotationSummary | undefined;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search?: WorkflowRunsSearch | undefined;\n}\n\nexport function RunAnnotationSummaryLine({\n summary,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search = {},\n}: RunAnnotationSummaryLineProps) {\n // A run with no annotations says so in its empty state; a \"0 annotations\" line beside it is\n // the same fact twice.\n if (!summary || summary.total === 0) return null;\n\n const visibleSeverities = ATTENTION_SEVERITIES.filter((severity) => summary[severity] > 0);\n\n const totalLabel = countLabel(summary.total, 'annotation', summary.truncated);\n\n return (\n <div className=\"flex flex-wrap items-center gap-x-inline gap-y-tight text-foreground-neutral-subtle\">\n {search.severity && workspaceSlug && projectSlug && workflowRunId ? (\n // The severity filter has no control of its own, so while one is active the total is\n // the way back out. Unfiltered it stays plain text: a link to where you already are\n // is noise.\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...withoutSeverity(search), tab: 'annotations'},\n search,\n ) as never\n }\n aria-label={`Show all ${totalLabel}`}\n className=\"shrink-0 rounded-4 outline-none focus-visible:shadow-border-interactive-with-active\"\n >\n {/* Underlined rather than accented: the escape hatch has to read as a link without\n becoming the loudest thing on a line whose job is to be scannable. */}\n <Text as=\"span\" size=\"xs\" className=\"text-foreground-neutral-subtle underline\">\n {totalLabel}\n </Text>\n </Link>\n ) : (\n <Text as=\"span\" size=\"xs\" className=\"shrink-0 text-foreground-neutral-subtle\">\n {totalLabel}\n </Text>\n )}\n {visibleSeverities.map((severity) => (\n <span key={severity} className=\"inline-flex shrink-0 items-center gap-inline\">\n <MetadataSeparator />\n <SeverityLink\n count={summary[severity]}\n truncated={summary.truncated}\n severity={severity}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n workflowRunId={workflowRunId}\n search={search}\n />\n </span>\n ))}\n </div>\n );\n}\n\nfunction SeverityLink({\n count,\n truncated,\n severity,\n workspaceSlug,\n projectSlug,\n workflowRunId,\n search,\n}: {\n count: number;\n truncated: boolean;\n severity: WorkflowRunAnnotationSeverity;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n workflowRunId?: string | undefined;\n search: WorkflowRunsSearch;\n}) {\n const label = countLabel(count, severity, truncated);\n const content = (\n <>\n <Icon\n name={SEVERITY_ICON[severity]}\n size={12}\n aria-hidden=\"true\"\n className={cn('shrink-0', SEVERITY_ICON_TONE[severity])}\n />\n <Text as=\"span\" size=\"xs\" className=\"text-foreground-neutral-subtle\">\n {label}\n </Text>\n </>\n );\n\n if (!workspaceSlug || !projectSlug || !workflowRunId) {\n return <span className=\"inline-flex items-center gap-tight\">{content}</span>;\n }\n\n const searchWithoutAnnotation = withoutAnnotation(search);\n\n return (\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n workflowRunSearchParams(\n {...searchWithoutAnnotation, tab: 'annotations', severity},\n search,\n ) as never\n }\n className=\"inline-flex items-center gap-tight rounded-4 outline-none hover:underline focus-visible:shadow-border-interactive-with-active\"\n >\n {content}\n </Link>\n );\n}\n\n/** A selected annotation is stale the moment the list it was chosen from is refiltered. */\nfunction withoutAnnotation(search: WorkflowRunsSearch): WorkflowRunsSearch {\n const next = {...search};\n delete next.annotation;\n return next;\n}\n\nfunction withoutSeverity(search: WorkflowRunsSearch): WorkflowRunsSearch {\n const next = withoutAnnotation(search);\n delete next.severity;\n return next;\n}\n\n/**\n * A truncated read renders `500+ annotations`: the counts are a lower bound once the page budget\n * is hit, and presenting them as exact would understate a run the reader is trying to trust.\n */\nfunction countLabel(count: number, word: string, truncated: boolean): string {\n const plural = count === 1 && !truncated ? word : `${word}s`;\n return `${count}${truncated ? '+' : ''} ${plural}`;\n}\n"],"names":["MetadataSeparator","Icon","Text","cn","Link","workflowRunSearchParams","SEVERITY_ICON","SEVERITY_ICON_TONE","ATTENTION_SEVERITIES","RunAnnotationSummaryLine","summary","workspaceSlug","projectSlug","workflowRunId","search","total","visibleSeverities","filter","severity","totalLabel","countLabel","truncated","div","className","to","params","withoutSeverity","tab","aria-label","as","size","map","span","SeverityLink","count","label","content","name","aria-hidden","searchWithoutAnnotation","withoutAnnotation","next","annotation","word","plural"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IconName } from '@shipfox/react-ui/icon';
|
|
2
|
+
import type { RunAnnotationSeverity } from '#core/run-annotation.js';
|
|
3
|
+
/**
|
|
4
|
+
* The glyph vocabulary for annotation severity, shared by every count surface.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately the same marks the `Callout` uses on the cards themselves, so the glyph beside
|
|
7
|
+
* `1 error` in the summary is the glyph on the error card below it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SEVERITY_ICON: Record<RunAnnotationSeverity, IconName>;
|
|
10
|
+
/**
|
|
11
|
+
* Severity tone lives in the glyph, never in the label.
|
|
12
|
+
*
|
|
13
|
+
* Coloring the text would make severity compete with the brand accent, which this system
|
|
14
|
+
* reserves for links, focus, and "you are here". Shape plus tone in a 12px mark distinguishes
|
|
15
|
+
* an error from a warning without either of them shouting.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SEVERITY_ICON_TONE: Record<RunAnnotationSeverity, string>;
|
|
18
|
+
/** Hairline tone for a bordered count chip, matching the glyph it sits beside. */
|
|
19
|
+
export declare const SEVERITY_CHIP_TONE: Record<RunAnnotationSeverity, string>;
|
|
20
|
+
//# sourceMappingURL=severity-visuals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"severity-visuals.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-tabs/severity-visuals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,qBAAqB,EAAE,QAAQ,CAKjE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAKpE,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAKpE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The glyph vocabulary for annotation severity, shared by every count surface.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately the same marks the `Callout` uses on the cards themselves, so the glyph beside
|
|
5
|
+
* `1 error` in the summary is the glyph on the error card below it.
|
|
6
|
+
*/ export const SEVERITY_ICON = {
|
|
7
|
+
error: 'closeCircleFill',
|
|
8
|
+
warning: 'errorWarningFill',
|
|
9
|
+
info: 'info',
|
|
10
|
+
success: 'checkboxCircleFill'
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Severity tone lives in the glyph, never in the label.
|
|
14
|
+
*
|
|
15
|
+
* Coloring the text would make severity compete with the brand accent, which this system
|
|
16
|
+
* reserves for links, focus, and "you are here". Shape plus tone in a 12px mark distinguishes
|
|
17
|
+
* an error from a warning without either of them shouting.
|
|
18
|
+
*/ export const SEVERITY_ICON_TONE = {
|
|
19
|
+
error: 'text-tag-error-icon',
|
|
20
|
+
warning: 'text-tag-warning-icon',
|
|
21
|
+
info: 'text-tag-blue-icon',
|
|
22
|
+
success: 'text-tag-success-icon'
|
|
23
|
+
};
|
|
24
|
+
/** Hairline tone for a bordered count chip, matching the glyph it sits beside. */ export const SEVERITY_CHIP_TONE = {
|
|
25
|
+
error: 'border-tag-error-border text-tag-error-icon',
|
|
26
|
+
warning: 'border-tag-warning-border text-tag-warning-icon',
|
|
27
|
+
info: 'border-tag-blue-border text-tag-blue-icon',
|
|
28
|
+
success: 'border-tag-success-border text-tag-success-icon'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=severity-visuals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-tabs/severity-visuals.ts"],"sourcesContent":["import type {IconName} from '@shipfox/react-ui/icon';\nimport type {RunAnnotationSeverity} from '#core/run-annotation.js';\n\n/**\n * The glyph vocabulary for annotation severity, shared by every count surface.\n *\n * Deliberately the same marks the `Callout` uses on the cards themselves, so the glyph beside\n * `1 error` in the summary is the glyph on the error card below it.\n */\nexport const SEVERITY_ICON: Record<RunAnnotationSeverity, IconName> = {\n error: 'closeCircleFill',\n warning: 'errorWarningFill',\n info: 'info',\n success: 'checkboxCircleFill',\n};\n\n/**\n * Severity tone lives in the glyph, never in the label.\n *\n * Coloring the text would make severity compete with the brand accent, which this system\n * reserves for links, focus, and \"you are here\". Shape plus tone in a 12px mark distinguishes\n * an error from a warning without either of them shouting.\n */\nexport const SEVERITY_ICON_TONE: Record<RunAnnotationSeverity, string> = {\n error: 'text-tag-error-icon',\n warning: 'text-tag-warning-icon',\n info: 'text-tag-blue-icon',\n success: 'text-tag-success-icon',\n};\n\n/** Hairline tone for a bordered count chip, matching the glyph it sits beside. */\nexport const SEVERITY_CHIP_TONE: Record<RunAnnotationSeverity, string> = {\n error: 'border-tag-error-border text-tag-error-icon',\n warning: 'border-tag-warning-border text-tag-warning-icon',\n info: 'border-tag-blue-border text-tag-blue-icon',\n success: 'border-tag-success-border text-tag-success-icon',\n};\n"],"names":["SEVERITY_ICON","error","warning","info","success","SEVERITY_ICON_TONE","SEVERITY_CHIP_TONE"],"mappings":"AAGA;;;;;CAKC,GACD,OAAO,MAAMA,gBAAyD;IACpEC,OAAO;IACPC,SAAS;IACTC,MAAM;IACNC,SAAS;AACX,EAAE;AAEF;;;;;;CAMC,GACD,OAAO,MAAMC,qBAA4D;IACvEJ,OAAO;IACPC,SAAS;IACTC,MAAM;IACNC,SAAS;AACX,EAAE;AAEF,gFAAgF,GAChF,OAAO,MAAME,qBAA4D;IACvEL,OAAO;IACPC,SAAS;IACTC,MAAM;IACNC,SAAS;AACX,EAAE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { RunAnnotationSummary } from '#core/run-annotation.js';
|
|
1
2
|
import { type WorkflowRunDetail } from '#core/workflow-run.js';
|
|
2
|
-
import type { RunAnnotationSummary } from '#core/workflow-run-tabs.js';
|
|
3
3
|
import { type WorkflowJobSearch, type WorkflowRunTab } from '#routes/inputs.js';
|
|
4
4
|
type RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;
|
|
5
5
|
export interface RunWorkspaceNavProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-workspace-nav.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"names":[],"mappings":"AAQA,OAAO,
|
|
1
|
+
{"version":3,"file":"run-workspace-nav.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-view/run-workspace-nav.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAGpB,MAAM,mBAAmB,CAAC;AAG3B,KAAK,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,iBAAiB,CAAC;IACvB,aAAa,EAAE,mBAAmB,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC1C,iBAAiB,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CACtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,aAAa,EACb,WAAW,EACX,GAAG,EACH,aAAa,EACb,YAAY,EACZ,SAAc,EACd,iBAAiB,GAClB,EAAE,oBAAoB,+BA0DtB"}
|
|
@@ -35,7 +35,7 @@ import { JobExecutionTimeText } from '../job-detail/job-execution-time-text.js';
|
|
|
35
35
|
asChild: true,
|
|
36
36
|
children: /*#__PURE__*/ _jsxs("button", {
|
|
37
37
|
type: "button",
|
|
38
|
-
className: "flex min-h-44 w-full items-center justify-between gap-
|
|
38
|
+
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",
|
|
39
39
|
"aria-label": "Toggle run navigation",
|
|
40
40
|
children: [
|
|
41
41
|
/*#__PURE__*/ _jsxs("span", {
|
|
@@ -63,7 +63,7 @@ import { JobExecutionTimeText } from '../job-detail/job-execution-time-text.js';
|
|
|
63
63
|
})
|
|
64
64
|
}),
|
|
65
65
|
/*#__PURE__*/ _jsx("div", {
|
|
66
|
-
className: cn('max-h-[50vh] overflow-y-auto p-
|
|
66
|
+
className: cn('max-h-[50vh] overflow-y-auto p-tight min-[768px]:block min-[768px]:max-h-none min-[768px]:p-[12px]', !mobileOpen && 'hidden'),
|
|
67
67
|
children: /*#__PURE__*/ _jsx(RunWorkspaceNavContent, {
|
|
68
68
|
workspaceSlug: workspaceSlug,
|
|
69
69
|
projectSlug: projectSlug,
|
|
@@ -105,7 +105,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
|
|
|
105
105
|
]);
|
|
106
106
|
return /*#__PURE__*/ _jsxs("nav", {
|
|
107
107
|
"aria-label": "Run workspace",
|
|
108
|
-
className: "flex min-w-0 flex-col gap-
|
|
108
|
+
className: "flex min-w-0 flex-col gap-group",
|
|
109
109
|
children: [
|
|
110
110
|
/*#__PURE__*/ _jsx("ul", {
|
|
111
111
|
children: /*#__PURE__*/ _jsx("li", {
|
|
@@ -125,7 +125,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
|
|
|
125
125
|
className: "min-w-0",
|
|
126
126
|
children: [
|
|
127
127
|
/*#__PURE__*/ _jsxs("div", {
|
|
128
|
-
className: "flex items-center justify-between gap-
|
|
128
|
+
className: "flex items-center justify-between gap-inline px-tight pb-[6px]",
|
|
129
129
|
children: [
|
|
130
130
|
/*#__PURE__*/ _jsx(Text, {
|
|
131
131
|
as: "h2",
|
|
@@ -164,7 +164,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
|
|
|
164
164
|
}),
|
|
165
165
|
onClick: onNavigate,
|
|
166
166
|
"aria-current": current ? 'page' : undefined,
|
|
167
|
-
className: cn('flex min-h-32 min-w-0 items-center gap-
|
|
167
|
+
className: cn('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', current && 'bg-background-neutral-hover'),
|
|
168
168
|
children: [
|
|
169
169
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
170
170
|
status: deriveJobDisplayStatus(job),
|
|
@@ -196,7 +196,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
|
|
|
196
196
|
id: "run-workspace-details-heading",
|
|
197
197
|
size: "xs",
|
|
198
198
|
bold: true,
|
|
199
|
-
className: "px-
|
|
199
|
+
className: "px-tight pb-[6px] text-foreground-neutral-muted",
|
|
200
200
|
children: "Run details"
|
|
201
201
|
}),
|
|
202
202
|
/*#__PURE__*/ _jsxs("ul", {
|
|
@@ -210,6 +210,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
|
|
|
210
210
|
section: "annotations",
|
|
211
211
|
current: !currentJobId && activeSection === 'annotations',
|
|
212
212
|
count: annotationSummary?.total,
|
|
213
|
+
countTruncated: annotationSummary?.truncated,
|
|
213
214
|
onNavigate: onNavigate
|
|
214
215
|
})
|
|
215
216
|
}),
|
|
@@ -231,7 +232,7 @@ function RunWorkspaceNavContent({ workspaceSlug, projectSlug, run, jobs, activeS
|
|
|
231
232
|
]
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
|
-
function RunSectionLink({ workspaceSlug, projectSlug, runId, runAttempt, section, current, count, onNavigate }) {
|
|
235
|
+
function RunSectionLink({ workspaceSlug, projectSlug, runId, runAttempt, section, current, count, countTruncated = false, onNavigate }) {
|
|
235
236
|
return /*#__PURE__*/ _jsxs(Link, {
|
|
236
237
|
to: "/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId",
|
|
237
238
|
params: {
|
|
@@ -249,12 +250,17 @@ function RunSectionLink({ workspaceSlug, projectSlug, runId, runAttempt, section
|
|
|
249
250
|
}),
|
|
250
251
|
onClick: onNavigate,
|
|
251
252
|
"aria-current": current ? 'page' : undefined,
|
|
252
|
-
|
|
253
|
+
"aria-label": count ? `${sectionLabel(section)}, ${countTruncated ? `${count} or more` : count}` : undefined,
|
|
254
|
+
className: cn('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', current && 'bg-background-neutral-hover'),
|
|
253
255
|
children: [
|
|
254
256
|
sectionLabel(section),
|
|
255
|
-
count ? /*#__PURE__*/
|
|
257
|
+
count ? /*#__PURE__*/ _jsxs("span", {
|
|
258
|
+
"aria-hidden": "true",
|
|
256
259
|
className: "ml-auto font-code text-xs text-foreground-neutral-muted tabular-nums",
|
|
257
|
-
children:
|
|
260
|
+
children: [
|
|
261
|
+
count,
|
|
262
|
+
countTruncated ? '+' : ''
|
|
263
|
+
]
|
|
258
264
|
}) : null
|
|
259
265
|
]
|
|
260
266
|
});
|