@shipfox/client-workflows 13.0.0 → 14.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/main.ts +16 -1
- package/.storybook/workspace-source.ts +31 -0
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +23 -0
- package/dist/components/job-graph/job-duration-label.js +1 -1
- package/dist/components/job-graph/job-duration-label.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.d.ts +2 -1
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +3 -3
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-graph-view.d.ts +2 -1
- package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-view.js +2 -2
- package/dist/components/job-graph/job-graph-view.js.map +1 -1
- package/dist/components/job-graph/job-node.js +5 -5
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/job-graph/types.d.ts +2 -1
- package/dist/components/job-graph/types.d.ts.map +1 -1
- package/dist/components/job-graph/types.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-number-label.js +1 -1
- package/dist/components/workflow-run-number-label.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +2 -2
- 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 +2 -7
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +92 -68
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +6 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/index.js +7 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
- package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +8 -4
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +125 -78
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/index.d.ts +1 -0
- package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/index.js +1 -0
- package/dist/components/workflow-source-panel/index.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
- package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
- package/dist/core/entities/step.d.ts +1 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +2 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/workflow-run-tabs.d.ts +9 -0
- package/dist/core/workflow-run-tabs.d.ts.map +1 -0
- package/dist/core/workflow-run-tabs.js +3 -0
- package/dist/core/workflow-run-tabs.js.map +1 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-detail-page.js +28 -6
- package/dist/pages/workflow-run-detail-page.js.map +1 -1
- package/dist/routes/inputs.d.ts +33 -0
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +49 -0
- package/dist/routes/inputs.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-label.tsx +5 -1
- package/src/components/job-graph/job-graph-content.tsx +7 -4
- package/src/components/job-graph/job-graph-view.tsx +6 -3
- package/src/components/job-graph/job-graph.stories.tsx +1 -1
- package/src/components/job-graph/job-node.stories.tsx +1 -1
- package/src/components/job-graph/job-node.test.tsx +27 -2
- package/src/components/job-graph/job-node.tsx +6 -6
- package/src/components/job-graph/types.ts +5 -1
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-number-label.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +2 -2
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +23 -30
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +3 -22
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +84 -69
- package/src/components/workflow-run-tabs/index.ts +5 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
- package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
- package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
- package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
- package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
- package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
- package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
- package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
- package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
- package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +64 -64
- package/src/components/workflow-run-view/workflow-run-view.tsx +199 -85
- package/src/components/workflow-source-panel/index.ts +1 -0
- package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
- package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
- package/src/core/entities/step.ts +4 -0
- package/src/core/workflow-run-tabs.ts +8 -0
- package/src/pages/workflow-run-detail-page.tsx +31 -4
- package/src/pages/workflow-run-pages.test.tsx +185 -45
- package/src/routes/inputs.test.ts +47 -0
- package/src/routes/inputs.ts +45 -0
- package/src/storybook-config.test.ts +27 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.test.json +7 -1
package/.storybook/main.ts
CHANGED
|
@@ -1 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import type {StorybookConfig} from '@storybook/react-vite';
|
|
2
|
+
import sharedConfig from '../../.storybook/main.ts';
|
|
3
|
+
import {withWorkspaceSource} from './workspace-source.ts';
|
|
4
|
+
|
|
5
|
+
const config: StorybookConfig = {
|
|
6
|
+
...sharedConfig,
|
|
7
|
+
viteFinal: async (viteConfig, options) => {
|
|
8
|
+
const sharedViteConfig = sharedConfig.viteFinal
|
|
9
|
+
? await sharedConfig.viteFinal(viteConfig, options)
|
|
10
|
+
: viteConfig;
|
|
11
|
+
|
|
12
|
+
return withWorkspaceSource(sharedViteConfig);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default config;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {workspaceSourceResolver} from '@shipfox/vite/workspace-source';
|
|
2
|
+
import type {InlineConfig} from 'vite';
|
|
3
|
+
import {defaultClientConditions, defaultServerConditions} from 'vite';
|
|
4
|
+
|
|
5
|
+
function withCondition(conditions: readonly string[] | undefined, defaults: readonly string[]) {
|
|
6
|
+
const resolvedConditions = [...(conditions ?? defaults)];
|
|
7
|
+
|
|
8
|
+
if (!resolvedConditions.includes('workspace-source')) {
|
|
9
|
+
resolvedConditions.push('workspace-source');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return resolvedConditions;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function withWorkspaceSource(config: InlineConfig): InlineConfig {
|
|
16
|
+
return {
|
|
17
|
+
...config,
|
|
18
|
+
plugins: [...(config.plugins ?? []), workspaceSourceResolver()],
|
|
19
|
+
resolve: {
|
|
20
|
+
...config.resolve,
|
|
21
|
+
conditions: withCondition(config.resolve?.conditions, defaultClientConditions),
|
|
22
|
+
},
|
|
23
|
+
ssr: {
|
|
24
|
+
...config.ssr,
|
|
25
|
+
resolve: {
|
|
26
|
+
...config.ssr?.resolve,
|
|
27
|
+
conditions: withCondition(config.ssr?.resolve?.conditions, defaultServerConditions),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 72 files with swc (492.28ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @shipfox/client-workflows
|
|
2
2
|
|
|
3
|
+
## 14.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 312a137: Add typed step errors for invalid checkout paths and occupied checkout destinations.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- f8a98cb: Publish the shared UI component artifacts and release `@shipfox/client-ui` with the updated shared UI dependency so fresh consumers and Storybook resolve component subpaths without configuration overrides.
|
|
12
|
+
- b2d4550: Add URL-backed run detail tabs for Summary, Jobs, Annotations, and Source.
|
|
13
|
+
- Updated dependencies [baa7594]
|
|
14
|
+
- Updated dependencies [f8a98cb]
|
|
15
|
+
- Updated dependencies [1267eb3]
|
|
16
|
+
- Updated dependencies [b2d4550]
|
|
17
|
+
- Updated dependencies [312a137]
|
|
18
|
+
- @shipfox/react-ui@0.5.0
|
|
19
|
+
- @shipfox/client-ui@14.0.0
|
|
20
|
+
- @shipfox/api-workflows-dto@12.1.0
|
|
21
|
+
- @shipfox/client-logs@14.0.0
|
|
22
|
+
- @shipfox/client-projects@14.0.0
|
|
23
|
+
- @shipfox/client-shell@14.0.0
|
|
24
|
+
- @shipfox/client-triggers@14.0.0
|
|
25
|
+
|
|
3
26
|
## 13.0.0
|
|
4
27
|
|
|
5
28
|
### Major Changes
|
|
@@ -25,7 +25,7 @@ function DurationText({ children }) {
|
|
|
25
25
|
return /*#__PURE__*/ _jsx(Code, {
|
|
26
26
|
as: "span",
|
|
27
27
|
variant: "label",
|
|
28
|
-
className: "shrink-0 tabular-nums text-foreground-neutral-
|
|
28
|
+
className: "shrink-0 tabular-nums text-foreground-neutral-subtle",
|
|
29
29
|
children: children
|
|
30
30
|
});
|
|
31
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-duration-label.tsx"],"sourcesContent":["import {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {humanDuration} from '@shipfox/react-ui/utils';\nimport type {JobDisplayDuration} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from './job-duration-format.js';\n\nexport function JobDurationLabel({duration}: {duration: JobDisplayDuration | null}) {\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n // The status icon names the state; the node aria-label carries the verb.\n return (\n <Code
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-duration-label.tsx"],"sourcesContent":["import {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {humanDuration} from '@shipfox/react-ui/utils';\nimport type {JobDisplayDuration} from '#core/workflow-run.js';\nimport {formatJobExecutionTimeLabel} from './job-duration-format.js';\n\nexport function JobDurationLabel({duration}: {duration: JobDisplayDuration | null}) {\n if (duration === null) return null;\n\n if (duration.state === 'live') {\n return <LiveDurationText fromIso={duration.fromIso} />;\n }\n\n return <DurationText>{formatJobExecutionTimeLabel(duration)}</DurationText>;\n}\n\nfunction LiveDurationText({fromIso}: {fromIso: string}) {\n useTimeTick();\n return <DurationText>{humanDuration(fromIso)}</DurationText>;\n}\n\nfunction DurationText({children}: {children: string}) {\n // The status icon names the state; the node aria-label carries the verb.\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"shrink-0 tabular-nums text-foreground-neutral-subtle\"\n >\n {children}\n </Code>\n );\n}\n"],"names":["useTimeTick","Code","humanDuration","formatJobExecutionTimeLabel","JobDurationLabel","duration","state","LiveDurationText","fromIso","DurationText","children","as","variant","className"],"mappings":";AAAA,SAAQA,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,aAAa,QAAO,0BAA0B;AAEtD,SAAQC,2BAA2B,QAAO,2BAA2B;AAErE,OAAO,SAASC,iBAAiB,EAACC,QAAQ,EAAwC;IAChF,IAAIA,aAAa,MAAM,OAAO;IAE9B,IAAIA,SAASC,KAAK,KAAK,QAAQ;QAC7B,qBAAO,KAACC;YAAiBC,SAASH,SAASG,OAAO;;IACpD;IAEA,qBAAO,KAACC;kBAAcN,4BAA4BE;;AACpD;AAEA,SAASE,iBAAiB,EAACC,OAAO,EAAoB;IACpDR;IACA,qBAAO,KAACS;kBAAcP,cAAcM;;AACtC;AAEA,SAASC,aAAa,EAACC,QAAQ,EAAqB;IAClD,yEAAyE;IACzE,qBACE,KAACT;QACCU,IAAG;QACHC,SAAQ;QACRC,WAAU;kBAETH;;AAGP"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { WorkflowRunDetail } from '#core/workflow-run.js';
|
|
2
2
|
import type { JobGraphModel } from './graph-model.js';
|
|
3
|
+
import type { JobGraphSelectionSource } from './types.js';
|
|
3
4
|
export declare function JobGraphContent({ model, trigger, selectedJobId, onSelectJob, }: {
|
|
4
5
|
model: JobGraphModel;
|
|
5
6
|
trigger: Pick<WorkflowRunDetail, 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'>;
|
|
6
7
|
selectedJobId?: string | undefined;
|
|
7
|
-
onSelectJob: (jobId: string | undefined) => void;
|
|
8
|
+
onSelectJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;
|
|
8
9
|
}): import("react").JSX.Element;
|
|
9
10
|
//# sourceMappingURL=job-graph-content.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-graph-content.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-graph-content.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAC,aAAa,EAAwB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"job-graph-content.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-graph-content.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAC,aAAa,EAAwB,MAAM,kBAAkB,CAAC;AAG3E,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,YAAY,CAAC;AASxD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,OAAO,EACP,aAAa,EACb,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,IAAI,CACX,iBAAiB,EACjB,qBAAqB,GAAG,cAAc,GAAG,iBAAiB,GAAG,eAAe,CAC7E,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;CACpF,+BAyFA"}
|
|
@@ -47,14 +47,14 @@ export function JobGraphContent({ model, trigger, selectedJobId, onSelectJob })
|
|
|
47
47
|
});
|
|
48
48
|
if (!nextNodeId) return;
|
|
49
49
|
event.preventDefault();
|
|
50
|
-
onSelectJob(nextNodeId);
|
|
50
|
+
onSelectJob(nextNodeId, 'keyboard');
|
|
51
51
|
nodeRefs.current.get(nextNodeId)?.focus();
|
|
52
52
|
}
|
|
53
53
|
return /*#__PURE__*/ _jsx(TimeTickerProvider, {
|
|
54
54
|
intervalMs: 1000,
|
|
55
55
|
reducedMotionIntervalMs: 10_000,
|
|
56
56
|
children: /*#__PURE__*/ _jsx("div", {
|
|
57
|
-
className: "min-h-0 overflow-auto bg-background-neutral-
|
|
57
|
+
className: "min-h-0 overflow-auto bg-background-neutral-background",
|
|
58
58
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
59
59
|
className: "relative",
|
|
60
60
|
style: {
|
|
@@ -86,7 +86,7 @@ export function JobGraphContent({ model, trigger, selectedJobId, onSelectJob })
|
|
|
86
86
|
node: node,
|
|
87
87
|
selected: node.id === selectedJobId,
|
|
88
88
|
ref: setNodeRef(node.id),
|
|
89
|
-
onSelect: ()=>onSelectJob(node.id === selectedJobId ? undefined : node.id),
|
|
89
|
+
onSelect: ()=>onSelectJob(node.id === selectedJobId ? undefined : node.id, 'pointer'),
|
|
90
90
|
onKeyDown: handleKeyDown,
|
|
91
91
|
onHoverStart: ()=>setHoveredJobId(node.id),
|
|
92
92
|
onHoverEnd: ()=>setHoveredJobId((current)=>current === node.id ? undefined : current)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-graph-content.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEvent} from 'react';\nimport {useRef, useState} from 'react';\nimport type {WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphModel, JobGraphNavigationKey} from './graph-model.js';\nimport {nextJobGraphNodeId} from './graph-model.js';\nimport {JobNode, TriggerNode} from './job-node.js';\n\nconst NODE_WIDTH = 208;\nconst NODE_HEIGHT = 48;\nconst COLUMN_GAP = 64;\nconst ROW_GAP = 20;\nconst TRIGGER_WIDTH = 36;\nconst PADDING = 16;\n\nexport function JobGraphContent({\n model,\n trigger,\n selectedJobId,\n onSelectJob,\n}: {\n model: JobGraphModel;\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n selectedJobId?: string | undefined;\n onSelectJob: (jobId: string | undefined) => void;\n}) {\n const nodeRefs = useRef(new Map<string, HTMLButtonElement>());\n const [hoveredJobId, setHoveredJobId] = useState<string | undefined>();\n\n if (model.nodes.length === 0) {\n return (\n <EmptyState\n className=\"min-h-160 px-16 py-24\"\n icon=\"componentLine\"\n title=\"No jobs yet\"\n description=\"This run has not materialized jobs.\"\n variant=\"compact\"\n />\n );\n }\n\n const maxRows = Math.max(1, ...model.columns.map((column) => column.length));\n const contentWidth =\n PADDING * 2 +\n TRIGGER_WIDTH +\n COLUMN_GAP +\n model.columns.length * NODE_WIDTH +\n Math.max(0, model.columns.length - 1) * COLUMN_GAP;\n const contentHeight = PADDING * 2 + maxRows * NODE_HEIGHT + Math.max(0, maxRows - 1) * ROW_GAP;\n\n function setNodeRef(jobId: string) {\n return (element: HTMLButtonElement | null) => {\n if (element) {\n nodeRefs.current.set(jobId, element);\n } else {\n nodeRefs.current.delete(jobId);\n }\n };\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n const key = navigationKey(event.key);\n if (!key) return;\n\n const currentNodeId = event.currentTarget.dataset.jobId;\n if (!currentNodeId) return;\n\n const nextNodeId = nextJobGraphNodeId({model, currentNodeId, key});\n if (!nextNodeId) return;\n\n event.preventDefault();\n onSelectJob(nextNodeId);\n nodeRefs.current.get(nextNodeId)?.focus();\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div className=\"min-h-0 overflow-auto bg-background-neutral-base\">\n <div className=\"relative\" style={{width: contentWidth, minHeight: contentHeight}}>\n <GraphEdges model={model} hoveredJobId={hoveredJobId} />\n <div\n className=\"absolute\"\n style={{left: PADDING, top: PADDING + (NODE_HEIGHT - TRIGGER_WIDTH) / 2}}\n >\n <TriggerNode trigger={trigger} />\n </div>\n {model.columns.map((column, columnIndex) => (\n <div\n key={column.map((node) => node.id).join(':')}\n className=\"absolute flex flex-col gap-20\"\n style={{left: jobLeft(columnIndex), top: PADDING}}\n >\n {column.map((node) => (\n <JobNode\n key={node.id}\n node={node}\n selected={node.id === selectedJobId}\n ref={setNodeRef(node.id)}\n onSelect={() => onSelectJob(node.id === selectedJobId ? undefined : node.id)}\n onKeyDown={handleKeyDown}\n onHoverStart={() => setHoveredJobId(node.id)}\n onHoverEnd={() =>\n setHoveredJobId((current) => (current === node.id ? undefined : current))\n }\n />\n ))}\n </div>\n ))}\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction navigationKey(key: string): JobGraphNavigationKey | undefined {\n if (\n key === 'ArrowRight' ||\n key === 'ArrowLeft' ||\n key === 'ArrowDown' ||\n key === 'ArrowUp' ||\n key === 'j' ||\n key === 'k'\n ) {\n return key;\n }\n return undefined;\n}\n\nfunction GraphEdges({\n model,\n hoveredJobId,\n}: {\n model: JobGraphModel;\n hoveredJobId?: string | undefined;\n}) {\n const byId = new Map(model.nodes.map((node) => [node.id, node]));\n const edgeViews = model.edges\n .map((edge) => {\n const fromNode = byId.get(edge.from);\n const toNode = byId.get(edge.to);\n const from = edge.from === 'trigger' ? triggerPoint() : jobRightPoint(fromNode);\n const to = jobLeftPoint(toNode);\n if (!from || !to) return undefined;\n\n return {\n id: edge.id,\n highlighted: edge.from === hoveredJobId || edge.to === hoveredJobId,\n path: edgePath({from, fromNode, to, toNode}),\n };\n })\n .filter((edgeView): edgeView is NonNullable<typeof edgeView> => edgeView !== undefined)\n .sort((left, right) => Number(left.highlighted) - Number(right.highlighted));\n\n return (\n <svg className=\"pointer-events-none absolute inset-0 size-full\" aria-hidden=\"true\">\n {edgeViews.map((edge) => {\n return (\n <g\n key={edge.id}\n className={cn(\n 'text-foreground-neutral-muted transition-colors',\n edge.highlighted && 'text-foreground-neutral-base',\n )}\n >\n <path\n data-edge-id={edge.id}\n d={edge.path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={edge.highlighted ? 1.5 : 1}\n />\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction edgePath({\n from,\n fromNode,\n to,\n toNode,\n}: {\n from: {x: number; y: number};\n fromNode: {column: number} | undefined;\n to: {x: number; y: number};\n toNode: {column: number} | undefined;\n}) {\n if (fromNode && toNode && toNode.column - fromNode.column > 1 && from.y !== to.y) {\n const targetLaneX = to.x - COLUMN_GAP / 2;\n return `M ${from.x} ${from.y} H ${targetLaneX} V ${to.y} H ${to.x}`;\n }\n\n const midX = from.x + (to.x - from.x) / 2;\n return `M ${from.x} ${from.y} H ${midX} V ${to.y} H ${to.x}`;\n}\n\nfunction triggerPoint() {\n return {x: PADDING + TRIGGER_WIDTH, y: PADDING + NODE_HEIGHT / 2};\n}\n\nfunction jobLeft(column: number): number {\n // Dependency depths are contiguous, so a node's depth matches its rendered column index.\n return PADDING + TRIGGER_WIDTH + COLUMN_GAP + column * (NODE_WIDTH + COLUMN_GAP);\n}\n\nfunction jobTop(row: number): number {\n return PADDING + row * (NODE_HEIGHT + ROW_GAP);\n}\n\nfunction jobLeftPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column), y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n\nfunction jobRightPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column) + NODE_WIDTH, y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n"],"names":["EmptyState","TimeTickerProvider","cn","useRef","useState","nextJobGraphNodeId","JobNode","TriggerNode","NODE_WIDTH","NODE_HEIGHT","COLUMN_GAP","ROW_GAP","TRIGGER_WIDTH","PADDING","JobGraphContent","model","trigger","selectedJobId","onSelectJob","nodeRefs","Map","hoveredJobId","setHoveredJobId","nodes","length","className","icon","title","description","variant","maxRows","Math","max","columns","map","column","contentWidth","contentHeight","setNodeRef","jobId","element","current","set","delete","handleKeyDown","event","key","navigationKey","currentNodeId","currentTarget","dataset","nextNodeId","preventDefault","get","focus","intervalMs","reducedMotionIntervalMs","div","style","width","minHeight","GraphEdges","left","top","columnIndex","jobLeft","node","selected","id","ref","onSelect","undefined","onKeyDown","onHoverStart","onHoverEnd","join","byId","edgeViews","edges","edge","fromNode","from","toNode","to","triggerPoint","jobRightPoint","jobLeftPoint","highlighted","path","edgePath","filter","edgeView","sort","right","Number","svg","aria-hidden","g","data-edge-id","d","fill","stroke","strokeWidth","y","targetLaneX","x","midX","jobTop","row"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-graph-content.tsx"],"sourcesContent":["import {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEvent} from 'react';\nimport {useRef, useState} from 'react';\nimport type {WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphModel, JobGraphNavigationKey} from './graph-model.js';\nimport {nextJobGraphNodeId} from './graph-model.js';\nimport {JobNode, TriggerNode} from './job-node.js';\nimport type {JobGraphSelectionSource} from './types.js';\n\nconst NODE_WIDTH = 208;\nconst NODE_HEIGHT = 48;\nconst COLUMN_GAP = 64;\nconst ROW_GAP = 20;\nconst TRIGGER_WIDTH = 36;\nconst PADDING = 16;\n\nexport function JobGraphContent({\n model,\n trigger,\n selectedJobId,\n onSelectJob,\n}: {\n model: JobGraphModel;\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n selectedJobId?: string | undefined;\n onSelectJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;\n}) {\n const nodeRefs = useRef(new Map<string, HTMLButtonElement>());\n const [hoveredJobId, setHoveredJobId] = useState<string | undefined>();\n\n if (model.nodes.length === 0) {\n return (\n <EmptyState\n className=\"min-h-160 px-16 py-24\"\n icon=\"componentLine\"\n title=\"No jobs yet\"\n description=\"This run has not materialized jobs.\"\n variant=\"compact\"\n />\n );\n }\n\n const maxRows = Math.max(1, ...model.columns.map((column) => column.length));\n const contentWidth =\n PADDING * 2 +\n TRIGGER_WIDTH +\n COLUMN_GAP +\n model.columns.length * NODE_WIDTH +\n Math.max(0, model.columns.length - 1) * COLUMN_GAP;\n const contentHeight = PADDING * 2 + maxRows * NODE_HEIGHT + Math.max(0, maxRows - 1) * ROW_GAP;\n\n function setNodeRef(jobId: string) {\n return (element: HTMLButtonElement | null) => {\n if (element) {\n nodeRefs.current.set(jobId, element);\n } else {\n nodeRefs.current.delete(jobId);\n }\n };\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n const key = navigationKey(event.key);\n if (!key) return;\n\n const currentNodeId = event.currentTarget.dataset.jobId;\n if (!currentNodeId) return;\n\n const nextNodeId = nextJobGraphNodeId({model, currentNodeId, key});\n if (!nextNodeId) return;\n\n event.preventDefault();\n onSelectJob(nextNodeId, 'keyboard');\n nodeRefs.current.get(nextNodeId)?.focus();\n }\n\n return (\n <TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>\n <div className=\"min-h-0 overflow-auto bg-background-neutral-background\">\n <div className=\"relative\" style={{width: contentWidth, minHeight: contentHeight}}>\n <GraphEdges model={model} hoveredJobId={hoveredJobId} />\n <div\n className=\"absolute\"\n style={{left: PADDING, top: PADDING + (NODE_HEIGHT - TRIGGER_WIDTH) / 2}}\n >\n <TriggerNode trigger={trigger} />\n </div>\n {model.columns.map((column, columnIndex) => (\n <div\n key={column.map((node) => node.id).join(':')}\n className=\"absolute flex flex-col gap-20\"\n style={{left: jobLeft(columnIndex), top: PADDING}}\n >\n {column.map((node) => (\n <JobNode\n key={node.id}\n node={node}\n selected={node.id === selectedJobId}\n ref={setNodeRef(node.id)}\n onSelect={() =>\n onSelectJob(node.id === selectedJobId ? undefined : node.id, 'pointer')\n }\n onKeyDown={handleKeyDown}\n onHoverStart={() => setHoveredJobId(node.id)}\n onHoverEnd={() =>\n setHoveredJobId((current) => (current === node.id ? undefined : current))\n }\n />\n ))}\n </div>\n ))}\n </div>\n </div>\n </TimeTickerProvider>\n );\n}\n\nfunction navigationKey(key: string): JobGraphNavigationKey | undefined {\n if (\n key === 'ArrowRight' ||\n key === 'ArrowLeft' ||\n key === 'ArrowDown' ||\n key === 'ArrowUp' ||\n key === 'j' ||\n key === 'k'\n ) {\n return key;\n }\n return undefined;\n}\n\nfunction GraphEdges({\n model,\n hoveredJobId,\n}: {\n model: JobGraphModel;\n hoveredJobId?: string | undefined;\n}) {\n const byId = new Map(model.nodes.map((node) => [node.id, node]));\n const edgeViews = model.edges\n .map((edge) => {\n const fromNode = byId.get(edge.from);\n const toNode = byId.get(edge.to);\n const from = edge.from === 'trigger' ? triggerPoint() : jobRightPoint(fromNode);\n const to = jobLeftPoint(toNode);\n if (!from || !to) return undefined;\n\n return {\n id: edge.id,\n highlighted: edge.from === hoveredJobId || edge.to === hoveredJobId,\n path: edgePath({from, fromNode, to, toNode}),\n };\n })\n .filter((edgeView): edgeView is NonNullable<typeof edgeView> => edgeView !== undefined)\n .sort((left, right) => Number(left.highlighted) - Number(right.highlighted));\n\n return (\n <svg className=\"pointer-events-none absolute inset-0 size-full\" aria-hidden=\"true\">\n {edgeViews.map((edge) => {\n return (\n <g\n key={edge.id}\n className={cn(\n 'text-foreground-neutral-muted transition-colors',\n edge.highlighted && 'text-foreground-neutral-base',\n )}\n >\n <path\n data-edge-id={edge.id}\n d={edge.path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={edge.highlighted ? 1.5 : 1}\n />\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction edgePath({\n from,\n fromNode,\n to,\n toNode,\n}: {\n from: {x: number; y: number};\n fromNode: {column: number} | undefined;\n to: {x: number; y: number};\n toNode: {column: number} | undefined;\n}) {\n if (fromNode && toNode && toNode.column - fromNode.column > 1 && from.y !== to.y) {\n const targetLaneX = to.x - COLUMN_GAP / 2;\n return `M ${from.x} ${from.y} H ${targetLaneX} V ${to.y} H ${to.x}`;\n }\n\n const midX = from.x + (to.x - from.x) / 2;\n return `M ${from.x} ${from.y} H ${midX} V ${to.y} H ${to.x}`;\n}\n\nfunction triggerPoint() {\n return {x: PADDING + TRIGGER_WIDTH, y: PADDING + NODE_HEIGHT / 2};\n}\n\nfunction jobLeft(column: number): number {\n // Dependency depths are contiguous, so a node's depth matches its rendered column index.\n return PADDING + TRIGGER_WIDTH + COLUMN_GAP + column * (NODE_WIDTH + COLUMN_GAP);\n}\n\nfunction jobTop(row: number): number {\n return PADDING + row * (NODE_HEIGHT + ROW_GAP);\n}\n\nfunction jobLeftPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column), y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n\nfunction jobRightPoint(node: {column: number; row: number} | undefined) {\n if (!node) return undefined;\n return {x: jobLeft(node.column) + NODE_WIDTH, y: jobTop(node.row) + NODE_HEIGHT / 2};\n}\n"],"names":["EmptyState","TimeTickerProvider","cn","useRef","useState","nextJobGraphNodeId","JobNode","TriggerNode","NODE_WIDTH","NODE_HEIGHT","COLUMN_GAP","ROW_GAP","TRIGGER_WIDTH","PADDING","JobGraphContent","model","trigger","selectedJobId","onSelectJob","nodeRefs","Map","hoveredJobId","setHoveredJobId","nodes","length","className","icon","title","description","variant","maxRows","Math","max","columns","map","column","contentWidth","contentHeight","setNodeRef","jobId","element","current","set","delete","handleKeyDown","event","key","navigationKey","currentNodeId","currentTarget","dataset","nextNodeId","preventDefault","get","focus","intervalMs","reducedMotionIntervalMs","div","style","width","minHeight","GraphEdges","left","top","columnIndex","jobLeft","node","selected","id","ref","onSelect","undefined","onKeyDown","onHoverStart","onHoverEnd","join","byId","edgeViews","edges","edge","fromNode","from","toNode","to","triggerPoint","jobRightPoint","jobLeftPoint","highlighted","path","edgePath","filter","edgeView","sort","right","Number","svg","aria-hidden","g","data-edge-id","d","fill","stroke","strokeWidth","y","targetLaneX","x","midX","jobTop","row"],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { WorkflowRunDetail } from '#core/workflow-run.js';
|
|
2
2
|
import type { JobGraphModel } from './graph-model.js';
|
|
3
|
+
import type { JobGraphSelectionSource } from './types.js';
|
|
3
4
|
export declare function JobGraphView({ model, trigger, selectedJobId, defaultSelectedJobId, onSelectedJobChange, className, }: {
|
|
4
5
|
model: JobGraphModel;
|
|
5
6
|
trigger: Pick<WorkflowRunDetail, 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'>;
|
|
6
7
|
selectedJobId?: string | undefined;
|
|
7
8
|
defaultSelectedJobId?: string | undefined;
|
|
8
|
-
onSelectedJobChange?: ((jobId: string | undefined) => void) | undefined;
|
|
9
|
+
onSelectedJobChange?: ((jobId: string | undefined, source?: JobGraphSelectionSource) => void) | undefined;
|
|
9
10
|
className?: string | undefined;
|
|
10
11
|
}): import("react").JSX.Element;
|
|
11
12
|
//# sourceMappingURL=job-graph-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-graph-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-graph-view.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"job-graph-view.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-graph-view.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,YAAY,CAAC;AAExD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,SAAS,GACV,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,IAAI,CACX,iBAAiB,EACjB,qBAAqB,GAAG,cAAc,GAAG,iBAAiB,GAAG,eAAe,CAC7E,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,mBAAmB,CAAC,EAChB,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC,GACvE,SAAS,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,+BAqBA"}
|
|
@@ -5,9 +5,9 @@ import { JobGraphContent } from './job-graph-content.js';
|
|
|
5
5
|
export function JobGraphView({ model, trigger, selectedJobId, defaultSelectedJobId, onSelectedJobChange, className }) {
|
|
6
6
|
const [localSelectedJobId, setLocalSelectedJobId] = useState(defaultSelectedJobId);
|
|
7
7
|
const selected = selectedJobId ?? localSelectedJobId;
|
|
8
|
-
function selectJob(jobId) {
|
|
8
|
+
function selectJob(jobId, source) {
|
|
9
9
|
setLocalSelectedJobId(jobId);
|
|
10
|
-
onSelectedJobChange?.(jobId);
|
|
10
|
+
onSelectedJobChange?.(jobId, source);
|
|
11
11
|
}
|
|
12
12
|
return /*#__PURE__*/ _jsx("section", {
|
|
13
13
|
"aria-label": "Workflow jobs",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-graph-view.tsx"],"sourcesContent":["import {cn} from '@shipfox/react-ui/utils';\nimport {useState} from 'react';\nimport type {WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphModel} from './graph-model.js';\nimport {JobGraphContent} from './job-graph-content.js';\n\nexport function JobGraphView({\n model,\n trigger,\n selectedJobId,\n defaultSelectedJobId,\n onSelectedJobChange,\n className,\n}: {\n model: JobGraphModel;\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n selectedJobId?: string | undefined;\n defaultSelectedJobId?: string | undefined;\n onSelectedJobChange
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-graph-view.tsx"],"sourcesContent":["import {cn} from '@shipfox/react-ui/utils';\nimport {useState} from 'react';\nimport type {WorkflowRunDetail} from '#core/workflow-run.js';\nimport type {JobGraphModel} from './graph-model.js';\nimport {JobGraphContent} from './job-graph-content.js';\nimport type {JobGraphSelectionSource} from './types.js';\n\nexport function JobGraphView({\n model,\n trigger,\n selectedJobId,\n defaultSelectedJobId,\n onSelectedJobChange,\n className,\n}: {\n model: JobGraphModel;\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n selectedJobId?: string | undefined;\n defaultSelectedJobId?: string | undefined;\n onSelectedJobChange?:\n | ((jobId: string | undefined, source?: JobGraphSelectionSource) => void)\n | undefined;\n className?: string | undefined;\n}) {\n const [localSelectedJobId, setLocalSelectedJobId] = useState<string | undefined>(\n defaultSelectedJobId,\n );\n const selected = selectedJobId ?? localSelectedJobId;\n\n function selectJob(jobId: string | undefined, source?: JobGraphSelectionSource) {\n setLocalSelectedJobId(jobId);\n onSelectedJobChange?.(jobId, source);\n }\n\n return (\n <section aria-label=\"Workflow jobs\" className={cn('min-h-0', className)}>\n <JobGraphContent\n model={model}\n trigger={trigger}\n selectedJobId={selected}\n onSelectJob={selectJob}\n />\n </section>\n );\n}\n"],"names":["cn","useState","JobGraphContent","JobGraphView","model","trigger","selectedJobId","defaultSelectedJobId","onSelectedJobChange","className","localSelectedJobId","setLocalSelectedJobId","selected","selectJob","jobId","source","section","aria-label","onSelectJob"],"mappings":";AAAA,SAAQA,EAAE,QAAO,0BAA0B;AAC3C,SAAQC,QAAQ,QAAO,QAAQ;AAG/B,SAAQC,eAAe,QAAO,yBAAyB;AAGvD,OAAO,SAASC,aAAa,EAC3BC,KAAK,EACLC,OAAO,EACPC,aAAa,EACbC,oBAAoB,EACpBC,mBAAmB,EACnBC,SAAS,EAaV;IACC,MAAM,CAACC,oBAAoBC,sBAAsB,GAAGV,SAClDM;IAEF,MAAMK,WAAWN,iBAAiBI;IAElC,SAASG,UAAUC,KAAyB,EAAEC,MAAgC;QAC5EJ,sBAAsBG;QACtBN,sBAAsBM,OAAOC;IAC/B;IAEA,qBACE,KAACC;QAAQC,cAAW;QAAgBR,WAAWT,GAAG,WAAWS;kBAC3D,cAAA,KAACP;YACCE,OAAOA;YACPC,SAASA;YACTC,eAAeM;YACfM,aAAaL;;;AAIrB"}
|
|
@@ -23,7 +23,7 @@ export function TriggerNode({ trigger }) {
|
|
|
23
23
|
children: /*#__PURE__*/ _jsx("button", {
|
|
24
24
|
type: "button",
|
|
25
25
|
"aria-label": label,
|
|
26
|
-
className: "flex items-center justify-center rounded-full
|
|
26
|
+
className: "flex cursor-help items-center justify-center rounded-full bg-background-neutral-base shadow-button-neutral transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none",
|
|
27
27
|
style: {
|
|
28
28
|
width: TRIGGER_SIZE,
|
|
29
29
|
height: TRIGGER_SIZE
|
|
@@ -32,7 +32,7 @@ export function TriggerNode({ trigger }) {
|
|
|
32
32
|
provider: trigger.triggerProvider,
|
|
33
33
|
source: trigger.triggerSource,
|
|
34
34
|
"aria-hidden": true,
|
|
35
|
-
className: "size-14 shrink-0 text-foreground-neutral-
|
|
35
|
+
className: "size-14 shrink-0 text-foreground-neutral-subtle"
|
|
36
36
|
})
|
|
37
37
|
})
|
|
38
38
|
}),
|
|
@@ -66,11 +66,11 @@ export function JobNode({ node, selected, onSelect, onKeyDown, onHoverStart, onH
|
|
|
66
66
|
onKeyDown: onKeyDown,
|
|
67
67
|
onPointerEnter: onHoverStart,
|
|
68
68
|
onPointerLeave: onHoverEnd,
|
|
69
|
-
className: cn('group relative flex h-48 w-208 items-center gap-8 rounded-8
|
|
69
|
+
className: cn('group relative flex h-48 w-208 cursor-pointer items-center gap-8 rounded-8 bg-background-neutral-base px-10 text-left shadow-button-neutral transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none', selected && 'bg-background-components-hover hover:bg-background-components-pressed', node.carriedOver && 'opacity-[0.55]'),
|
|
70
70
|
children: [
|
|
71
71
|
selected ? /*#__PURE__*/ _jsx("span", {
|
|
72
72
|
"aria-hidden": "true",
|
|
73
|
-
className: "absolute inset-y-6 left-0 w-
|
|
73
|
+
className: "absolute inset-y-6 left-0 w-px rounded-full bg-border-highlights-interactive"
|
|
74
74
|
}) : null,
|
|
75
75
|
/*#__PURE__*/ _jsxs("div", {
|
|
76
76
|
className: "flex min-w-0 flex-1 items-center gap-8",
|
|
@@ -102,7 +102,7 @@ function ExecutionCountText({ executions }) {
|
|
|
102
102
|
asChild: true,
|
|
103
103
|
children: /*#__PURE__*/ _jsxs("span", {
|
|
104
104
|
"aria-hidden": "true",
|
|
105
|
-
className: "inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-
|
|
105
|
+
className: "inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-subtle",
|
|
106
106
|
children: [
|
|
107
107
|
/*#__PURE__*/ _jsx(Icon, {
|
|
108
108
|
name: "loopRightLine",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-node.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEventHandler, Ref} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionStatus,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport type {JobGraphNode} from './graph-model.js';\nimport {formatJobDurationAccessibleLabel} from './job-duration-format.js';\nimport {JobDurationLabel} from './job-duration-label.js';\n\nconst TRIGGER_SIZE = 36;\n\nexport function TriggerNode({\n trigger,\n}: {\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n}) {\n const label = trigger.triggerDisplayLabel || 'trigger';\n const tooltip = trigger.triggerLabel || label;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={label}\n className=\"flex items-center justify-center rounded-full border border-border-neutral-base bg-background-components-base transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none\"\n style={{width: TRIGGER_SIZE, height: TRIGGER_SIZE}}\n >\n <TriggerSourceIcon\n provider={trigger.triggerProvider}\n source={trigger.triggerSource}\n aria-hidden\n className=\"size-14 shrink-0 text-foreground-neutral-muted\"\n />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <span>{tooltip}</span>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nexport function JobNode({\n node,\n selected,\n onSelect,\n onKeyDown,\n onHoverStart,\n onHoverEnd,\n ref,\n}: {\n node: JobGraphNode;\n selected: boolean;\n onSelect: () => void;\n onKeyDown: KeyboardEventHandler<HTMLButtonElement>;\n onHoverStart: () => void;\n onHoverEnd: () => void;\n ref?: Ref<HTMLButtonElement>;\n}) {\n useTimeTick();\n const status = deriveJobDisplayStatus(node);\n const execution = defaultJobExecution(node);\n const visual = getWorkflowStatusVisual(status);\n const accessibleLabel = [\n node.displayName,\n visual.label,\n formatJobDurationAccessibleLabel(\n node.displayDuration,\n execution ? deriveJobExecutionDisplayStatus(execution) : undefined,\n ),\n node.executionCountVisible\n ? executionCountAccessibleLabel(node.jobExecutions.length)\n : undefined,\n node.carriedOver ? 'reused' : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n aria-label={accessibleLabel}\n data-job-id={node.id}\n onClick={onSelect}\n onKeyDown={onKeyDown}\n onPointerEnter={onHoverStart}\n onPointerLeave={onHoverEnd}\n className={cn(\n 'group relative flex h-48 w-208 items-center gap-8 rounded-8 border border-border-neutral-base bg-background-components-base px-10 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none',\n selected && 'bg-background-components-hover',\n node.carriedOver && 'opacity-[0.55]',\n )}\n >\n {selected ? (\n <span\n aria-hidden=\"true\"\n className=\"absolute inset-y-6 left-0 w-3 rounded-full bg-border-highlights-interactive\"\n />\n ) : null}\n <div className=\"flex min-w-0 flex-1 items-center gap-8\">\n <WorkflowStatusIcon status={status} size={14} />\n <JobLabel label={node.displayName} />\n </div>\n <JobDurationLabel duration={node.displayDuration} />\n {node.executionCountVisible ? <ExecutionCountText executions={node.jobExecutions} /> : null}\n {node.carriedOver ? <CarriedOverBadge /> : null}\n </button>\n );\n}\n\nfunction ExecutionCountText({executions}: {executions: JobExecution[]}) {\n const count = executions.length;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n aria-hidden=\"true\"\n className=\"inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-muted\"\n >\n <Icon name=\"loopRightLine\" className=\"size-12\" />\n <Code as=\"span\" variant=\"label\" className=\"text-current\">\n {count}\n </Code>\n </span>\n </TooltipTrigger>\n <TooltipContent>{executionCountTooltip(executions)}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobLabel({label}: {label: string}) {\n const {ref: labelRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(label);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span ref={labelRef} className=\"block min-w-0 truncate\">\n <Code as=\"span\" variant=\"label\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Code>\n </span>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{label}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction executionCountAccessibleLabel(count: number): string {\n return `${count} ${count === 1 ? 'execution' : 'executions'}`;\n}\n\nfunction executionCountTooltip(executions: JobExecution[]): string {\n const counts = executionStatusCounts(executions);\n const parts = EXECUTION_STATUSES.map((status) =>\n counts[status] > 0 ? `${counts[status]} ${status}` : undefined,\n ).filter((part): part is string => part !== undefined);\n return parts.length > 0 ? parts.join(', ') : 'No executions';\n}\n\nconst EXECUTION_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n] as const satisfies readonly JobExecutionStatus[];\n\nfunction executionStatusCounts(executions: JobExecution[]) {\n return executions.reduce(\n (counts, execution) => {\n counts[deriveJobExecutionDisplayStatus(execution)] += 1;\n return counts;\n },\n {pending: 0, running: 0, succeeded: 0, failed: 0, cancelled: 0},\n );\n}\n"],"names":["TriggerSourceIcon","Badge","useIsTextTruncated","Icon","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","cn","getWorkflowStatusVisual","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","formatJobDurationAccessibleLabel","JobDurationLabel","TRIGGER_SIZE","TriggerNode","trigger","label","triggerDisplayLabel","tooltip","triggerLabel","asChild","button","type","aria-label","className","style","width","height","provider","triggerProvider","source","triggerSource","aria-hidden","span","JobNode","node","selected","onSelect","onKeyDown","onHoverStart","onHoverEnd","ref","status","execution","visual","accessibleLabel","displayName","displayDuration","undefined","executionCountVisible","executionCountAccessibleLabel","jobExecutions","length","carriedOver","filter","part","Boolean","join","aria-pressed","data-job-id","id","onClick","onPointerEnter","onPointerLeave","div","size","JobLabel","duration","ExecutionCountText","executions","CarriedOverBadge","count","name","as","variant","executionCountTooltip","labelRef","isTruncated","bold","counts","executionStatusCounts","parts","EXECUTION_STATUSES","map","reduce","pending","running","succeeded","failed","cancelled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-node.tsx"],"sourcesContent":["import {TriggerSourceIcon} from '@shipfox/client-triggers';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {useIsTextTruncated} from '@shipfox/react-ui/hooks';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport type {KeyboardEventHandler, Ref} from 'react';\nimport {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';\nimport {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';\nimport {\n defaultJobExecution,\n deriveJobDisplayStatus,\n deriveJobExecutionDisplayStatus,\n type JobExecution,\n type JobExecutionStatus,\n type WorkflowRunDetail,\n} from '#core/workflow-run.js';\nimport type {JobGraphNode} from './graph-model.js';\nimport {formatJobDurationAccessibleLabel} from './job-duration-format.js';\nimport {JobDurationLabel} from './job-duration-label.js';\n\nconst TRIGGER_SIZE = 36;\n\nexport function TriggerNode({\n trigger,\n}: {\n trigger: Pick<\n WorkflowRunDetail,\n 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'\n >;\n}) {\n const label = trigger.triggerDisplayLabel || 'trigger';\n const tooltip = trigger.triggerLabel || label;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label={label}\n className=\"flex cursor-help items-center justify-center rounded-full bg-background-neutral-base shadow-button-neutral transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none\"\n style={{width: TRIGGER_SIZE, height: TRIGGER_SIZE}}\n >\n <TriggerSourceIcon\n provider={trigger.triggerProvider}\n source={trigger.triggerSource}\n aria-hidden\n className=\"size-14 shrink-0 text-foreground-neutral-subtle\"\n />\n </button>\n </TooltipTrigger>\n <TooltipContent>\n <span>{tooltip}</span>\n </TooltipContent>\n </Tooltip>\n );\n}\n\nexport function JobNode({\n node,\n selected,\n onSelect,\n onKeyDown,\n onHoverStart,\n onHoverEnd,\n ref,\n}: {\n node: JobGraphNode;\n selected: boolean;\n onSelect: () => void;\n onKeyDown: KeyboardEventHandler<HTMLButtonElement>;\n onHoverStart: () => void;\n onHoverEnd: () => void;\n ref?: Ref<HTMLButtonElement>;\n}) {\n useTimeTick();\n const status = deriveJobDisplayStatus(node);\n const execution = defaultJobExecution(node);\n const visual = getWorkflowStatusVisual(status);\n const accessibleLabel = [\n node.displayName,\n visual.label,\n formatJobDurationAccessibleLabel(\n node.displayDuration,\n execution ? deriveJobExecutionDisplayStatus(execution) : undefined,\n ),\n node.executionCountVisible\n ? executionCountAccessibleLabel(node.jobExecutions.length)\n : undefined,\n node.carriedOver ? 'reused' : undefined,\n ]\n .filter((part): part is string => Boolean(part))\n .join(', ');\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-pressed={selected}\n aria-label={accessibleLabel}\n data-job-id={node.id}\n onClick={onSelect}\n onKeyDown={onKeyDown}\n onPointerEnter={onHoverStart}\n onPointerLeave={onHoverEnd}\n className={cn(\n 'group relative flex h-48 w-208 cursor-pointer items-center gap-8 rounded-8 bg-background-neutral-base px-10 text-left shadow-button-neutral transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none',\n selected && 'bg-background-components-hover hover:bg-background-components-pressed',\n node.carriedOver && 'opacity-[0.55]',\n )}\n >\n {selected ? (\n <span\n aria-hidden=\"true\"\n className=\"absolute inset-y-6 left-0 w-px rounded-full bg-border-highlights-interactive\"\n />\n ) : null}\n <div className=\"flex min-w-0 flex-1 items-center gap-8\">\n <WorkflowStatusIcon status={status} size={14} />\n <JobLabel label={node.displayName} />\n </div>\n <JobDurationLabel duration={node.displayDuration} />\n {node.executionCountVisible ? <ExecutionCountText executions={node.jobExecutions} /> : null}\n {node.carriedOver ? <CarriedOverBadge /> : null}\n </button>\n );\n}\n\nfunction ExecutionCountText({executions}: {executions: JobExecution[]}) {\n const count = executions.length;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n aria-hidden=\"true\"\n className=\"inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-subtle\"\n >\n <Icon name=\"loopRightLine\" className=\"size-12\" />\n <Code as=\"span\" variant=\"label\" className=\"text-current\">\n {count}\n </Code>\n </span>\n </TooltipTrigger>\n <TooltipContent>{executionCountTooltip(executions)}</TooltipContent>\n </Tooltip>\n );\n}\n\nfunction CarriedOverBadge() {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"shrink-0\">\n <Badge variant=\"neutral\" size=\"2xs\">\n reused\n </Badge>\n </span>\n </TooltipTrigger>\n <TooltipContent>\n Carried over from a previous attempt; did not run in this attempt\n </TooltipContent>\n </Tooltip>\n );\n}\n\nfunction JobLabel({label}: {label: string}) {\n const {ref: labelRef, isTruncated} = useIsTextTruncated<HTMLSpanElement>(label);\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <span ref={labelRef} className=\"block min-w-0 truncate\">\n <Code as=\"span\" variant=\"label\" bold className=\"text-foreground-neutral-base\">\n {label}\n </Code>\n </span>\n </TooltipTrigger>\n {isTruncated ? <TooltipContent>{label}</TooltipContent> : null}\n </Tooltip>\n );\n}\n\nfunction executionCountAccessibleLabel(count: number): string {\n return `${count} ${count === 1 ? 'execution' : 'executions'}`;\n}\n\nfunction executionCountTooltip(executions: JobExecution[]): string {\n const counts = executionStatusCounts(executions);\n const parts = EXECUTION_STATUSES.map((status) =>\n counts[status] > 0 ? `${counts[status]} ${status}` : undefined,\n ).filter((part): part is string => part !== undefined);\n return parts.length > 0 ? parts.join(', ') : 'No executions';\n}\n\nconst EXECUTION_STATUSES = [\n 'pending',\n 'running',\n 'succeeded',\n 'failed',\n 'cancelled',\n] as const satisfies readonly JobExecutionStatus[];\n\nfunction executionStatusCounts(executions: JobExecution[]) {\n return executions.reduce(\n (counts, execution) => {\n counts[deriveJobExecutionDisplayStatus(execution)] += 1;\n return counts;\n },\n {pending: 0, running: 0, succeeded: 0, failed: 0, cancelled: 0},\n );\n}\n"],"names":["TriggerSourceIcon","Badge","useIsTextTruncated","Icon","useTimeTick","Tooltip","TooltipContent","TooltipTrigger","Code","cn","getWorkflowStatusVisual","WorkflowStatusIcon","defaultJobExecution","deriveJobDisplayStatus","deriveJobExecutionDisplayStatus","formatJobDurationAccessibleLabel","JobDurationLabel","TRIGGER_SIZE","TriggerNode","trigger","label","triggerDisplayLabel","tooltip","triggerLabel","asChild","button","type","aria-label","className","style","width","height","provider","triggerProvider","source","triggerSource","aria-hidden","span","JobNode","node","selected","onSelect","onKeyDown","onHoverStart","onHoverEnd","ref","status","execution","visual","accessibleLabel","displayName","displayDuration","undefined","executionCountVisible","executionCountAccessibleLabel","jobExecutions","length","carriedOver","filter","part","Boolean","join","aria-pressed","data-job-id","id","onClick","onPointerEnter","onPointerLeave","div","size","JobLabel","duration","ExecutionCountText","executions","CarriedOverBadge","count","name","as","variant","executionCountTooltip","labelRef","isTruncated","bold","counts","executionStatusCounts","parts","EXECUTION_STATUSES","map","reduce","pending","running","succeeded","failed","cancelled"],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { WorkflowRunDetail } from '#core/workflow-run.js';
|
|
2
|
+
export type JobGraphSelectionSource = 'pointer' | 'keyboard';
|
|
2
3
|
export interface JobGraphProps {
|
|
3
4
|
run: WorkflowRunDetail;
|
|
4
5
|
selectedJobId?: string | undefined;
|
|
5
6
|
defaultSelectedJobId?: string | undefined;
|
|
6
|
-
onSelectedJobChange?: ((jobId: string | undefined) => void) | undefined;
|
|
7
|
+
onSelectedJobChange?: ((jobId: string | undefined, source?: JobGraphSelectionSource) => void) | undefined;
|
|
7
8
|
className?: string | undefined;
|
|
8
9
|
}
|
|
9
10
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,iBAAiB,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,UAAU,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,iBAAiB,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,mBAAmB,CAAC,EAChB,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC,GACvE,SAAS,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/types.ts"],"sourcesContent":["import type {WorkflowRunDetail} from '#core/workflow-run.js';\n\nexport interface JobGraphProps {\n run: WorkflowRunDetail;\n selectedJobId?: string | undefined;\n defaultSelectedJobId?: string | undefined;\n onSelectedJobChange
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/types.ts"],"sourcesContent":["import type {WorkflowRunDetail} from '#core/workflow-run.js';\n\nexport type JobGraphSelectionSource = 'pointer' | 'keyboard';\n\nexport interface JobGraphProps {\n run: WorkflowRunDetail;\n selectedJobId?: string | undefined;\n defaultSelectedJobId?: string | undefined;\n onSelectedJobChange?:\n | ((jobId: string | undefined, source?: JobGraphSelectionSource) => void)\n | undefined;\n className?: string | undefined;\n}\n"],"names":[],"mappings":"AAIA,WAQC"}
|
|
@@ -59,7 +59,7 @@ function DurationText({ children, className, ariaLabel }) {
|
|
|
59
59
|
as: "span",
|
|
60
60
|
variant: "label",
|
|
61
61
|
"aria-label": ariaLabel,
|
|
62
|
-
className: cn('inline-flex shrink-0 items-center gap-4 tabular-nums text-foreground-neutral-
|
|
62
|
+
className: cn('inline-flex shrink-0 items-center gap-4 tabular-nums text-foreground-neutral-subtle', className),
|
|
63
63
|
children: [
|
|
64
64
|
/*#__PURE__*/ _jsx(Icon, {
|
|
65
65
|
name: "timerLine",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {WorkflowRunAttemptDisplayDuration} from '#core/workflow-run.js';\n\nexport function WorkflowRunDurationLabel({\n duration,\n className,\n}: {\n duration: WorkflowRunAttemptDisplayDuration | null;\n className?: string | undefined;\n}) {\n if (duration === null) return null;\n\n switch (duration.state) {\n case 'fixed': {\n const display = formatFixedDurationLabel(duration.elapsed);\n return (\n <DurationText className={className} ariaLabel={`ran ${display}`}>\n {display}\n </DurationText>\n );\n }\n case 'live':\n return <LiveDurationText duration={duration} className={className} />;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nexport function useWorkflowRunDurationAccessibleLabel(\n duration: WorkflowRunAttemptDisplayDuration | null,\n): string | undefined {\n useTimeTick();\n return workflowRunDurationAccessibleLabel(duration);\n}\n\nexport function workflowRunDurationAccessibleLabel(\n duration: WorkflowRunAttemptDisplayDuration | null,\n): string | undefined {\n if (duration === null) return undefined;\n\n switch (duration.state) {\n case 'live':\n return `running ${humanDuration(duration.fromIso)}`;\n case 'fixed':\n return `ran ${formatFixedDurationLabel(duration.elapsed)}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction LiveDurationText({\n duration,\n className,\n}: {\n duration: Extract<WorkflowRunAttemptDisplayDuration, {state: 'live'}>;\n className?: string | undefined;\n}) {\n useTimeTick();\n const display = humanDuration(duration.fromIso);\n return (\n <DurationText className={className} ariaLabel={`running ${display}`}>\n {display}\n </DurationText>\n );\n}\n\nfunction DurationText({\n children,\n className,\n ariaLabel,\n}: {\n children: string;\n className?: string | undefined;\n ariaLabel?: string | undefined;\n}) {\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n aria-label={ariaLabel}\n className={cn(\n 'inline-flex shrink-0 items-center gap-4 tabular-nums text-foreground-neutral-
|
|
1
|
+
{"version":3,"sources":["../../src/components/workflow-run-duration-label.tsx"],"sourcesContent":["import {Icon} from '@shipfox/react-ui/icon';\nimport {useTimeTick} from '@shipfox/react-ui/time-ticker';\nimport {Code} from '@shipfox/react-ui/typography';\nimport {cn, humanDuration} from '@shipfox/react-ui/utils';\nimport type {WorkflowRunAttemptDisplayDuration} from '#core/workflow-run.js';\n\nexport function WorkflowRunDurationLabel({\n duration,\n className,\n}: {\n duration: WorkflowRunAttemptDisplayDuration | null;\n className?: string | undefined;\n}) {\n if (duration === null) return null;\n\n switch (duration.state) {\n case 'fixed': {\n const display = formatFixedDurationLabel(duration.elapsed);\n return (\n <DurationText className={className} ariaLabel={`ran ${display}`}>\n {display}\n </DurationText>\n );\n }\n case 'live':\n return <LiveDurationText duration={duration} className={className} />;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nexport function useWorkflowRunDurationAccessibleLabel(\n duration: WorkflowRunAttemptDisplayDuration | null,\n): string | undefined {\n useTimeTick();\n return workflowRunDurationAccessibleLabel(duration);\n}\n\nexport function workflowRunDurationAccessibleLabel(\n duration: WorkflowRunAttemptDisplayDuration | null,\n): string | undefined {\n if (duration === null) return undefined;\n\n switch (duration.state) {\n case 'live':\n return `running ${humanDuration(duration.fromIso)}`;\n case 'fixed':\n return `ran ${formatFixedDurationLabel(duration.elapsed)}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\n }\n}\n\nfunction LiveDurationText({\n duration,\n className,\n}: {\n duration: Extract<WorkflowRunAttemptDisplayDuration, {state: 'live'}>;\n className?: string | undefined;\n}) {\n useTimeTick();\n const display = humanDuration(duration.fromIso);\n return (\n <DurationText className={className} ariaLabel={`running ${display}`}>\n {display}\n </DurationText>\n );\n}\n\nfunction DurationText({\n children,\n className,\n ariaLabel,\n}: {\n children: string;\n className?: string | undefined;\n ariaLabel?: string | undefined;\n}) {\n return (\n <Code\n as=\"span\"\n variant=\"label\"\n aria-label={ariaLabel}\n className={cn(\n 'inline-flex shrink-0 items-center gap-4 tabular-nums text-foreground-neutral-subtle',\n className,\n )}\n >\n <Icon name=\"timerLine\" className=\"size-12 shrink-0\" aria-hidden=\"true\" />\n {children}\n </Code>\n );\n}\n\nfunction formatFixedDurationLabel({\n years = 0,\n months = 0,\n weeks = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n}: Extract<WorkflowRunAttemptDisplayDuration, {state: 'fixed'}>['elapsed']): string {\n const totalDays = years * 365 + months * 30 + weeks * 7 + days;\n const totalHours = totalDays * 24 + hours;\n\n if (totalHours > 0) return `${totalHours}h ${pad2(minutes)}m`;\n if (minutes > 0) return `${minutes}m ${pad2(seconds)}s`;\n return `${seconds}s`;\n}\n\nfunction pad2(value: number): string {\n return value.toString().padStart(2, '0');\n}\n"],"names":["Icon","useTimeTick","Code","cn","humanDuration","WorkflowRunDurationLabel","duration","className","state","display","formatFixedDurationLabel","elapsed","DurationText","ariaLabel","LiveDurationText","exhaustive","useWorkflowRunDurationAccessibleLabel","workflowRunDurationAccessibleLabel","undefined","fromIso","children","as","variant","aria-label","name","aria-hidden","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":";AAAA,SAAQA,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,gCAAgC;AAC1D,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,EAAE,EAAEC,aAAa,QAAO,0BAA0B;AAG1D,OAAO,SAASC,yBAAyB,EACvCC,QAAQ,EACRC,SAAS,EAIV;IACC,IAAID,aAAa,MAAM,OAAO;IAE9B,OAAQA,SAASE,KAAK;QACpB,KAAK;YAAS;gBACZ,MAAMC,UAAUC,yBAAyBJ,SAASK,OAAO;gBACzD,qBACE,KAACC;oBAAaL,WAAWA;oBAAWM,WAAW,CAAC,IAAI,EAAEJ,SAAS;8BAC5DA;;YAGP;QACA,KAAK;YACH,qBAAO,KAACK;gBAAiBR,UAAUA;gBAAUC,WAAWA;;QAC1D;YAAS;gBACP,MAAMQ,aAAoBT;gBAC1B,OAAOS;YACT;IACF;AACF;AAEA,OAAO,SAASC,sCACdV,QAAkD;IAElDL;IACA,OAAOgB,mCAAmCX;AAC5C;AAEA,OAAO,SAASW,mCACdX,QAAkD;IAElD,IAAIA,aAAa,MAAM,OAAOY;IAE9B,OAAQZ,SAASE,KAAK;QACpB,KAAK;YACH,OAAO,CAAC,QAAQ,EAAEJ,cAAcE,SAASa,OAAO,GAAG;QACrD,KAAK;YACH,OAAO,CAAC,IAAI,EAAET,yBAAyBJ,SAASK,OAAO,GAAG;QAC5D;YAAS;gBACP,MAAMI,aAAoBT;gBAC1B,OAAOS;YACT;IACF;AACF;AAEA,SAASD,iBAAiB,EACxBR,QAAQ,EACRC,SAAS,EAIV;IACCN;IACA,MAAMQ,UAAUL,cAAcE,SAASa,OAAO;IAC9C,qBACE,KAACP;QAAaL,WAAWA;QAAWM,WAAW,CAAC,QAAQ,EAAEJ,SAAS;kBAChEA;;AAGP;AAEA,SAASG,aAAa,EACpBQ,QAAQ,EACRb,SAAS,EACTM,SAAS,EAKV;IACC,qBACE,MAACX;QACCmB,IAAG;QACHC,SAAQ;QACRC,cAAYV;QACZN,WAAWJ,GACT,uFACAI;;0BAGF,KAACP;gBAAKwB,MAAK;gBAAYjB,WAAU;gBAAmBkB,eAAY;;YAC/DL;;;AAGP;AAEA,SAASV,yBAAyB,EAChCgB,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EAC6D;IACxE,MAAMC,YAAYP,QAAQ,MAAMC,SAAS,KAAKC,QAAQ,IAAIC;IAC1D,MAAMK,aAAaD,YAAY,KAAKH;IAEpC,IAAII,aAAa,GAAG,OAAO,GAAGA,WAAW,EAAE,EAAEC,KAAKJ,SAAS,CAAC,CAAC;IAC7D,IAAIA,UAAU,GAAG,OAAO,GAAGA,QAAQ,EAAE,EAAEI,KAAKH,SAAS,CAAC,CAAC;IACvD,OAAO,GAAGA,QAAQ,CAAC,CAAC;AACtB;AAEA,SAASG,KAAKC,KAAa;IACzB,OAAOA,MAAMC,QAAQ,GAAGC,QAAQ,CAAC,GAAG;AACtC"}
|
|
@@ -22,7 +22,7 @@ export function formatWorkflowRunNumberLabel({ number, workflowName }) {
|
|
|
22
22
|
children: /*#__PURE__*/ _jsx(Code, {
|
|
23
23
|
as: "span",
|
|
24
24
|
variant: "label",
|
|
25
|
-
className: "min-w-0 max-w-[240px] truncate text-foreground-neutral-
|
|
25
|
+
className: "min-w-0 max-w-[240px] truncate text-foreground-neutral-subtle",
|
|
26
26
|
children: visibleLabel
|
|
27
27
|
})
|
|
28
28
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workflow-run-number-label.tsx"],"sourcesContent":["import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code} from '@shipfox/react-ui/typography';\nimport type {WorkflowRun} from '#core/workflow-run.js';\n\ntype WorkflowRunNumberLabelRun = Pick<WorkflowRun, 'number' | 'workflowName'> &\n Partial<Pick<WorkflowRun, 'name'>>;\n\nexport function formatWorkflowRunNumberLabel({\n number,\n workflowName,\n}: WorkflowRunNumberLabelRun): string | undefined {\n return number === null ? undefined : `${workflowName} #${number}`;\n}\n\n/**\n * The run's identity beside its name.\n *\n * A run whose name was never overridden carries its workflow name twice on one line, so the\n * visible label drops to the bare number in that case. The full label stays in the tooltip\n * and in `formatWorkflowRunNumberLabel`, which feeds accessible names.\n */\nexport function WorkflowRunNumberLabel({run}: {run: WorkflowRunNumberLabelRun}) {\n const label = formatWorkflowRunNumberLabel(run);\n if (label === undefined) return null;\n\n const namesTheWorkflowTwice = run.name === run.workflowName;\n const visibleLabel = namesTheWorkflowTwice ? `#${run.number}` : label;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"min-w-0 max-w-[240px] truncate text-foreground-neutral-
|
|
1
|
+
{"version":3,"sources":["../../src/components/workflow-run-number-label.tsx"],"sourcesContent":["import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Code} from '@shipfox/react-ui/typography';\nimport type {WorkflowRun} from '#core/workflow-run.js';\n\ntype WorkflowRunNumberLabelRun = Pick<WorkflowRun, 'number' | 'workflowName'> &\n Partial<Pick<WorkflowRun, 'name'>>;\n\nexport function formatWorkflowRunNumberLabel({\n number,\n workflowName,\n}: WorkflowRunNumberLabelRun): string | undefined {\n return number === null ? undefined : `${workflowName} #${number}`;\n}\n\n/**\n * The run's identity beside its name.\n *\n * A run whose name was never overridden carries its workflow name twice on one line, so the\n * visible label drops to the bare number in that case. The full label stays in the tooltip\n * and in `formatWorkflowRunNumberLabel`, which feeds accessible names.\n */\nexport function WorkflowRunNumberLabel({run}: {run: WorkflowRunNumberLabelRun}) {\n const label = formatWorkflowRunNumberLabel(run);\n if (label === undefined) return null;\n\n const namesTheWorkflowTwice = run.name === run.workflowName;\n const visibleLabel = namesTheWorkflowTwice ? `#${run.number}` : label;\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <Code\n as=\"span\"\n variant=\"label\"\n className=\"min-w-0 max-w-[240px] truncate text-foreground-neutral-subtle\"\n >\n {visibleLabel}\n </Code>\n </TooltipTrigger>\n <TooltipContent>\n <Code as=\"span\" variant=\"label\" className=\"block max-w-[360px] break-words\">\n {label}\n </Code>\n </TooltipContent>\n </Tooltip>\n );\n}\n"],"names":["Tooltip","TooltipContent","TooltipTrigger","Code","formatWorkflowRunNumberLabel","number","workflowName","undefined","WorkflowRunNumberLabel","run","label","namesTheWorkflowTwice","name","visibleLabel","asChild","as","variant","className"],"mappings":";AAAA,SAAQA,OAAO,EAAEC,cAAc,EAAEC,cAAc,QAAO,4BAA4B;AAClF,SAAQC,IAAI,QAAO,+BAA+B;AAMlD,OAAO,SAASC,6BAA6B,EAC3CC,MAAM,EACNC,YAAY,EACc;IAC1B,OAAOD,WAAW,OAAOE,YAAY,GAAGD,aAAa,EAAE,EAAED,QAAQ;AACnE;AAEA;;;;;;CAMC,GACD,OAAO,SAASG,uBAAuB,EAACC,GAAG,EAAmC;IAC5E,MAAMC,QAAQN,6BAA6BK;IAC3C,IAAIC,UAAUH,WAAW,OAAO;IAEhC,MAAMI,wBAAwBF,IAAIG,IAAI,KAAKH,IAAIH,YAAY;IAC3D,MAAMO,eAAeF,wBAAwB,CAAC,CAAC,EAAEF,IAAIJ,MAAM,EAAE,GAAGK;IAEhE,qBACE,MAACV;;0BACC,KAACE;gBAAeY,OAAO;0BACrB,cAAA,KAACX;oBACCY,IAAG;oBACHC,SAAQ;oBACRC,WAAU;8BAETJ;;;0BAGL,KAACZ;0BACC,cAAA,KAACE;oBAAKY,IAAG;oBAAOC,SAAQ;oBAAQC,WAAU;8BACvCP;;;;;AAKX"}
|
|
@@ -33,7 +33,7 @@ export function WorkflowRunAttemptSwitcher({ workspaceSlug, projectSlug, run, la
|
|
|
33
33
|
size: "2xs",
|
|
34
34
|
iconRight: "arrowDownSLine",
|
|
35
35
|
"aria-label": `Switch attempt, currently ${run.runAttempt.attempt} of ${maxAttempt}`,
|
|
36
|
-
className: "h-20 px-4 text-foreground-neutral-
|
|
36
|
+
className: "h-20 px-4 text-foreground-neutral-subtle hover:text-foreground-neutral-base",
|
|
37
37
|
children: /*#__PURE__*/ _jsxs(Text, {
|
|
38
38
|
as: "span",
|
|
39
39
|
size: "xs",
|
|
@@ -129,7 +129,7 @@ function AttemptItem({ attempt, current, workflowRunId, workspaceSlug, projectSl
|
|
|
129
129
|
}),
|
|
130
130
|
/*#__PURE__*/ _jsx(RelativeTime, {
|
|
131
131
|
value: attempt.createdAt,
|
|
132
|
-
className: "shrink-0 whitespace-nowrap font-code text-xs leading-20 text-foreground-neutral-
|
|
132
|
+
className: "shrink-0 whitespace-nowrap font-code text-xs leading-20 text-foreground-neutral-subtle"
|
|
133
133
|
})
|
|
134
134
|
]
|
|
135
135
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {useState} from 'react';\nimport type {WorkflowRunAttempt, WorkflowRunDetail} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {useWorkflowRunAttemptsQuery} from '#hooks/api/workflow-runs.js';\nimport {WorkflowStatusIcon} from '../workflow-status/workflow-status-icon.js';\n\nexport interface WorkflowRunAttemptSwitcherProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n latestAttempt: number;\n}\n\nexport function WorkflowRunAttemptSwitcher({\n workspaceSlug,\n projectSlug,\n run,\n latestAttempt,\n}: WorkflowRunAttemptSwitcherProps) {\n const [open, setOpen] = useState(false);\n const attemptsQuery = useWorkflowRunAttemptsQuery({\n workflowRunId: run.id,\n enabled: open,\n });\n\n if (latestAttempt <= 1) return null;\n if (!workspaceSlug || !projectSlug) return null;\n\n const attempts = attemptsQuery.data ?? [];\n const latestLoadedAttempt = Math.max(0, ...attempts.map((attempt) => attempt.attempt));\n const maxAttempt = Math.max(latestAttempt, run.runAttempt.attempt, latestLoadedAttempt);\n const isLoadingMissingAttempt =\n attempts.length > 0 && attemptsQuery.isFetching && latestLoadedAttempt < maxAttempt;\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"transparentMuted\"\n size=\"2xs\"\n iconRight=\"arrowDownSLine\"\n aria-label={`Switch attempt, currently ${run.runAttempt.attempt} of ${maxAttempt}`}\n className=\"h-20 px-4 text-foreground-neutral-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx"],"sourcesContent":["import {Button} from '@shipfox/react-ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {RelativeTime} from '@shipfox/react-ui/relative-time';\nimport {Code, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {Link} from '@tanstack/react-router';\nimport {useState} from 'react';\nimport type {WorkflowRunAttempt, WorkflowRunDetail} from '#core/workflow-run.js';\nimport {withoutWorkflowRunSelectionSearch} from '#core/workflow-run-url-state.js';\nimport {useWorkflowRunAttemptsQuery} from '#hooks/api/workflow-runs.js';\nimport {WorkflowStatusIcon} from '../workflow-status/workflow-status-icon.js';\n\nexport interface WorkflowRunAttemptSwitcherProps {\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n run: WorkflowRunDetail;\n latestAttempt: number;\n}\n\nexport function WorkflowRunAttemptSwitcher({\n workspaceSlug,\n projectSlug,\n run,\n latestAttempt,\n}: WorkflowRunAttemptSwitcherProps) {\n const [open, setOpen] = useState(false);\n const attemptsQuery = useWorkflowRunAttemptsQuery({\n workflowRunId: run.id,\n enabled: open,\n });\n\n if (latestAttempt <= 1) return null;\n if (!workspaceSlug || !projectSlug) return null;\n\n const attempts = attemptsQuery.data ?? [];\n const latestLoadedAttempt = Math.max(0, ...attempts.map((attempt) => attempt.attempt));\n const maxAttempt = Math.max(latestAttempt, run.runAttempt.attempt, latestLoadedAttempt);\n const isLoadingMissingAttempt =\n attempts.length > 0 && attemptsQuery.isFetching && latestLoadedAttempt < maxAttempt;\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"transparentMuted\"\n size=\"2xs\"\n iconRight=\"arrowDownSLine\"\n aria-label={`Switch attempt, currently ${run.runAttempt.attempt} of ${maxAttempt}`}\n className=\"h-20 px-4 text-foreground-neutral-subtle hover:text-foreground-neutral-base\"\n >\n <Text as=\"span\" size=\"xs\" className=\"text-inherit\">\n Attempt {run.runAttempt.attempt} of {maxAttempt}\n </Text>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" size=\"lg\">\n {attemptsQuery.isPending && attempts.length === 0 ? <LoadingRow /> : null}\n {isLoadingMissingAttempt ? <LoadingRow /> : null}\n {attemptsQuery.isError && attempts.length === 0 ? (\n <ErrorRow onRetry={() => void attemptsQuery.refetch()} />\n ) : null}\n {attempts.length > 0\n ? [...attempts]\n .sort((left, right) => right.attempt - left.attempt)\n .map((attempt) => (\n <AttemptItem\n key={attempt.id}\n attempt={attempt}\n current={attempt.id === run.runAttempt.id}\n workflowRunId={run.id}\n workspaceSlug={workspaceSlug}\n projectSlug={projectSlug}\n />\n ))\n : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction LoadingRow() {\n return (\n <DropdownMenuItem disabled>\n <Text as=\"span\" size=\"sm\" className=\"text-foreground-neutral-muted\">\n Loading attempts...\n </Text>\n </DropdownMenuItem>\n );\n}\n\nfunction ErrorRow({onRetry}: {onRetry: () => void}) {\n return (\n <DropdownMenuItem closeOnSelect={false} onSelect={onRetry}>\n <Text as=\"span\" size=\"sm\" className=\"text-foreground-highlight-error\">\n Could not load attempts. Retry\n </Text>\n </DropdownMenuItem>\n );\n}\n\nfunction AttemptItem({\n attempt,\n current,\n workflowRunId,\n workspaceSlug,\n projectSlug,\n}: {\n attempt: WorkflowRunAttempt;\n current: boolean;\n workflowRunId: string;\n workspaceSlug?: string | undefined;\n projectSlug?: string | undefined;\n}) {\n return (\n <DropdownMenuItem asChild className={cn(current && 'text-foreground-neutral-base')}>\n <Link\n to=\"/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId\"\n params={{workspaceSlug, projectSlug, workflowRunId}}\n search={\n ((previous: Record<string, unknown>) => {\n if (current) return previous;\n return {\n ...withoutWorkflowRunSelectionSearch(previous),\n runAttempt: attempt.attempt,\n };\n }) as never\n }\n aria-current={current ? 'page' : undefined}\n >\n <WorkflowStatusIcon status={attempt.status} size={14} tooltip={false} />\n <Code as=\"span\" variant=\"label\" className=\"min-w-0 flex-1 truncate text-inherit\">\n Attempt {attempt.attempt}\n </Code>\n <RelativeTime\n value={attempt.createdAt}\n className=\"shrink-0 whitespace-nowrap font-code text-xs leading-20 text-foreground-neutral-subtle\"\n />\n </Link>\n </DropdownMenuItem>\n );\n}\n"],"names":["Button","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","RelativeTime","Code","Text","cn","Link","useState","withoutWorkflowRunSelectionSearch","useWorkflowRunAttemptsQuery","WorkflowStatusIcon","WorkflowRunAttemptSwitcher","workspaceSlug","projectSlug","run","latestAttempt","open","setOpen","attemptsQuery","workflowRunId","id","enabled","attempts","data","latestLoadedAttempt","Math","max","map","attempt","maxAttempt","runAttempt","isLoadingMissingAttempt","length","isFetching","onOpenChange","asChild","type","variant","size","iconRight","aria-label","className","as","align","isPending","LoadingRow","isError","ErrorRow","onRetry","refetch","sort","left","right","AttemptItem","current","disabled","closeOnSelect","onSelect","to","params","search","previous","aria-current","undefined","status","tooltip","value","createdAt"],"mappings":"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"}
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import type { Ref } from 'react';
|
|
2
1
|
import { type WorkflowRunDetail, type WorkflowRunRerunMode } from '#core/workflow-run.js';
|
|
3
2
|
export interface WorkflowRunSummaryProps {
|
|
4
3
|
workspaceSlug?: string | undefined;
|
|
5
4
|
projectSlug?: string | undefined;
|
|
6
5
|
run: WorkflowRunDetail;
|
|
7
|
-
sourceAvailable?: boolean | undefined;
|
|
8
|
-
sourceOpen?: boolean | undefined;
|
|
9
|
-
sourcePanelId?: string | undefined;
|
|
10
|
-
sourceButtonRef?: Ref<HTMLButtonElement> | undefined;
|
|
11
|
-
onSourceToggle?: (() => void) | undefined;
|
|
12
6
|
cancelling?: boolean | undefined;
|
|
13
7
|
onCancel?: (() => void) | undefined;
|
|
14
8
|
rerunPending?: boolean | undefined;
|
|
15
9
|
onRerun?: ((mode: WorkflowRunRerunMode) => void) | undefined;
|
|
16
10
|
latestAttempt?: number | undefined;
|
|
17
11
|
}
|
|
18
|
-
export declare function WorkflowRunSummary({ workspaceSlug, projectSlug, run,
|
|
12
|
+
export declare function WorkflowRunSummary({ workspaceSlug, projectSlug, run, cancelling, onCancel, rerunPending, onRerun, latestAttempt, }: WorkflowRunSummaryProps): import("react").JSX.Element;
|
|
13
|
+
export declare function MetadataSeparator(): import("react").JSX.Element;
|
|
19
14
|
//# sourceMappingURL=workflow-run-summary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-run-summary.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow-run-summary.d.ts","sourceRoot":"","sources":["../../../src/components/workflow-run-summary/workflow-run-summary.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAY/B,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,GAAG,EAAE,iBAAiB,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED,wBAAgB,kBAAkB,CAAC,EACjC,aAAa,EACb,WAAW,EACX,GAAG,EACH,UAAkB,EAClB,QAAQ,EACR,YAAoB,EACpB,OAAO,EACP,aAAa,GACd,EAAE,uBAAuB,+BA4JzB;AA0GD,wBAAgB,iBAAiB,gCAEhC"}
|