@shipfox/client-workflows 12.0.2 → 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/preview.tsx +3 -3
- package/.storybook/workspace-source.ts +31 -0
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +79 -0
- package/dist/components/job-graph/job-duration-format.d.ts +2 -2
- package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +4 -2
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- 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.d.ts +1 -1
- package/dist/components/job-graph/job-node.d.ts.map +1 -1
- package/dist/components/job-graph/job-node.js +10 -9
- 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/step-list/step-list.d.ts +2 -2
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- 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.d.ts +21 -0
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-list/job-status-strip.js +185 -0
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
- package/dist/components/workflow-run-list/run-display.d.ts +27 -3
- package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
- package/dist/components/workflow-run-list/run-display.js +78 -7
- package/dist/components/workflow-run-list/run-display.js.map +1 -1
- package/dist/components/workflow-run-list/types.d.ts +23 -24
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
- package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-number-label.d.ts +15 -0
- package/dist/components/workflow-run-number-label.d.ts.map +1 -0
- package/dist/components/workflow-run-number-label.js +41 -0
- package/dist/components/workflow-run-number-label.js.map +1 -0
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +10 -9
- 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 +4 -9
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +107 -74
- 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/agent-config-failure-callout.d.ts +2 -2
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts +2 -2
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +51 -32
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
- 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 +10 -5
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +136 -85
- 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/components/workflow-status/status-visuals.d.ts +4 -4
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +7 -0
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +6 -4
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +10 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +11 -0
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +4 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.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/entities/workflow-run.d.ts +52 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +22 -3
- package/dist/core/entities/workflow-run.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/core/workflow-run.d.ts +6 -6
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +3 -3
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +7 -7
- package/dist/feature.js +5 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +31 -3
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +4 -4
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +12 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.js +46 -1
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.d.ts +11 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-detail-page.js +52 -0
- package/dist/pages/workflow-run-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-list-page.d.ts +10 -0
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-list-page.js +43 -0
- package/dist/pages/workflow-run-list-page.js.map +1 -0
- package/dist/routes/inputs.d.ts +94 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +199 -13
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-route.d.ts +6 -0
- package/dist/routes/project-route.d.ts.map +1 -0
- package/dist/routes/project-route.js +25 -0
- package/dist/routes/project-route.js.map +1 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +12 -7
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +11 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +6 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-format.ts +11 -2
- 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.test.tsx +19 -8
- package/src/components/job-graph/job-graph-view.tsx +6 -3
- package/src/components/job-graph/job-graph.stories.tsx +24 -20
- package/src/components/job-graph/job-node.stories.tsx +11 -6
- package/src/components/job-graph/job-node.test.tsx +82 -12
- package/src/components/job-graph/job-node.tsx +22 -15
- package/src/components/job-graph/types.ts +5 -1
- package/src/components/step-list/step-list.stories.tsx +2 -3
- package/src/components/step-list/step-list.tsx +3 -2
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
- package/src/components/workflow-run-list/run-display.test.ts +169 -30
- package/src/components/workflow-run-list/run-display.ts +116 -9
- package/src/components/workflow-run-list/types.ts +21 -17
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
- package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
- package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
- package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
- package/src/components/workflow-run-number-label.test.tsx +45 -0
- package/src/components/workflow-run-number-label.tsx +47 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +15 -14
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +29 -38
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +30 -24
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +102 -77
- 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/agent-config-failure-callout.stories.tsx +2 -3
- package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
- package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
- package/src/components/workflow-run-view/job-card.test.tsx +115 -0
- package/src/components/workflow-run-view/job-card.tsx +74 -35
- package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
- 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 +152 -94
- package/src/components/workflow-run-view/workflow-run-view.tsx +219 -95
- 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/components/workflow-status/status-visuals.test.ts +9 -0
- package/src/components/workflow-status/status-visuals.ts +6 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
- package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
- package/src/core/entities/job-execution.ts +22 -0
- package/src/core/entities/job-status.test.ts +104 -0
- package/src/core/entities/job.ts +18 -3
- package/src/core/entities/step.ts +4 -0
- package/src/core/entities/workflow-run.ts +76 -7
- package/src/core/workflow-run-tabs.ts +8 -0
- package/src/core/workflow-run.test.ts +24 -9
- package/src/core/workflow-run.ts +17 -2
- package/src/feature.ts +5 -5
- package/src/hooks/api/workflow-run-mapper.ts +30 -3
- package/src/hooks/api/workflow-runs.test.tsx +2 -0
- package/src/hooks/api/workflow-runs.ts +12 -7
- package/src/index.ts +7 -2
- package/src/page-harness-budget.test.ts +1 -1
- package/src/pages/project-workflows-page.test.tsx +59 -5
- package/src/pages/project-workflows-page.tsx +34 -1
- package/src/pages/workflow-run-detail-page.tsx +72 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/workflow-run-pages.test.tsx +823 -0
- package/src/routes/inputs.test.ts +240 -12
- package/src/routes/inputs.ts +215 -11
- package/src/routes/project-route.tsx +25 -0
- package/src/routes/run-detail.tsx +15 -8
- package/src/routes/runs.tsx +14 -7
- package/src/routes/workflows.tsx +5 -4
- package/src/storybook-config.test.ts +27 -0
- package/test/fixtures/workflow-run.ts +76 -7
- package/test/pages.tsx +18 -8
- package/test/render.tsx +8 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.test.json +7 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
- package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
- package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
- package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
- package/dist/pages/workflow-run-first-time-use.js +0 -17
- package/dist/pages/workflow-run-first-time-use.js.map +0 -1
- package/dist/pages/workflow-run-page.d.ts +0 -10
- package/dist/pages/workflow-run-page.d.ts.map +0 -1
- package/dist/pages/workflow-run-page.js +0 -90
- package/dist/pages/workflow-run-page.js.map +0 -1
- package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
- package/src/pages/workflow-run-first-time-use.tsx +0 -17
- package/src/pages/workflow-run-page.test.tsx +0 -519
- package/src/pages/workflow-run-page.tsx +0 -94
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;
|
package/.storybook/preview.tsx
CHANGED
|
@@ -42,20 +42,20 @@ const withRelativeTime: Decorator = (Story) => (
|
|
|
42
42
|
</RelativeTimeProvider>
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
// Run rows render `<Link to="/
|
|
45
|
+
// Run rows render `<Link to="/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId">` and read
|
|
46
46
|
// `useParams`, both of which need a router in context. A memory router seeded at a
|
|
47
47
|
// matching path lets the rows render and resolve their hrefs without a real app shell.
|
|
48
48
|
const withRouter: Decorator = (Story) => {
|
|
49
49
|
const rootRoute = createRootRoute();
|
|
50
50
|
const runRoute = createRoute({
|
|
51
51
|
getParentRoute: () => rootRoute,
|
|
52
|
-
path: '/
|
|
52
|
+
path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
53
53
|
component: () => <Story />,
|
|
54
54
|
});
|
|
55
55
|
const router = createRouter({
|
|
56
56
|
routeTree: rootRoute.addChildren([runRoute]),
|
|
57
57
|
history: createMemoryHistory({
|
|
58
|
-
initialEntries: ['/
|
|
58
|
+
initialEntries: ['/w/ws-demo/p/proj-demo/runs/run-1'],
|
|
59
59
|
}),
|
|
60
60
|
});
|
|
61
61
|
return <RouterProvider router={router} />;
|
|
@@ -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,84 @@
|
|
|
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
|
+
|
|
26
|
+
## 13.0.0
|
|
27
|
+
|
|
28
|
+
### Major Changes
|
|
29
|
+
|
|
30
|
+
- e405e92: Move client routes to slug-based `/w/$workspaceSlug` and `/p/$projectSlug` URLs, enforce the new composition contract, and support bounded project-slug resolution.
|
|
31
|
+
- 13fa279: Replaces the exported workflow-run UUID short-id with API-provided workflow names and run numbers in client workflow metadata.
|
|
32
|
+
- 452e0f8: Separates workflow run list and detail into route-owned pages, removes the shared
|
|
33
|
+
`WorkflowRunPage` export, and exposes older run pages for filtering.
|
|
34
|
+
|
|
35
|
+
### Minor Changes
|
|
36
|
+
|
|
37
|
+
- dea1ffd: Expose normalized repository references on listening execution events.
|
|
38
|
+
- 54c820e: Rebuild the run list as a full-width page: 44px rows carrying status, name, trigger, branch, commit, a per-run job status strip, duration, and time, with URL-backed search, status, branch, actor, event, and date filters.
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- b5bb0c5: Derives one activity-aware workflow status across graph, execution, and job detail surfaces.
|
|
43
|
+
- a2d684e: Displays resolved job execution names in workflow run job card titles.
|
|
44
|
+
- 5d2c9cf: Carry checkout steps from workflow normalization through step materialization and surface their setup error category.
|
|
45
|
+
- f78740d: Remove Unicode dash punctuation from package prose and source comments.
|
|
46
|
+
- 9fdd5e4: Persist definition validation warnings from repository syncs and surface them on the workflow page without changing sync success or run creation behavior.
|
|
47
|
+
- edb4a18: Present resolved workflow run and job execution names across workflow run surfaces.
|
|
48
|
+
- Updated dependencies [ee2ce67]
|
|
49
|
+
- Updated dependencies [5d2c9cf]
|
|
50
|
+
- Updated dependencies [e405e92]
|
|
51
|
+
- Updated dependencies [f7939c7]
|
|
52
|
+
- Updated dependencies [89f2c18]
|
|
53
|
+
- Updated dependencies [f78740d]
|
|
54
|
+
- Updated dependencies [9e1d599]
|
|
55
|
+
- Updated dependencies [dea1ffd]
|
|
56
|
+
- Updated dependencies [adf07e7]
|
|
57
|
+
- Updated dependencies [3f781ee]
|
|
58
|
+
- Updated dependencies [9969937]
|
|
59
|
+
- Updated dependencies [9fdd5e4]
|
|
60
|
+
- Updated dependencies [285fff2]
|
|
61
|
+
- Updated dependencies [e44a279]
|
|
62
|
+
- Updated dependencies [3c73365]
|
|
63
|
+
- Updated dependencies [4eb18b8]
|
|
64
|
+
- Updated dependencies [54c820e]
|
|
65
|
+
- Updated dependencies [54c820e]
|
|
66
|
+
- Updated dependencies [35a42bd]
|
|
67
|
+
- Updated dependencies [6adc228]
|
|
68
|
+
- Updated dependencies [d77baaa]
|
|
69
|
+
- Updated dependencies [032d316]
|
|
70
|
+
- Updated dependencies [c2a8e54]
|
|
71
|
+
- Updated dependencies [cb0abfa]
|
|
72
|
+
- Updated dependencies [e1efaee]
|
|
73
|
+
- @shipfox/api-definitions-dto@12.0.0
|
|
74
|
+
- @shipfox/api-workflows-dto@12.0.0
|
|
75
|
+
- @shipfox/client-projects@13.0.0
|
|
76
|
+
- @shipfox/client-shell@13.0.0
|
|
77
|
+
- @shipfox/client-triggers@13.0.0
|
|
78
|
+
- @shipfox/client-logs@13.0.0
|
|
79
|
+
- @shipfox/client-ui@13.0.0
|
|
80
|
+
- @shipfox/react-ui@0.4.0
|
|
81
|
+
|
|
3
82
|
## 12.0.2
|
|
4
83
|
|
|
5
84
|
### Patch Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { JobDisplayDuration, JobExecutionTime } from '#core/workflow-run.js';
|
|
1
|
+
import type { JobDisplayDuration, JobExecutionDisplayStatus, JobExecutionTime } from '#core/workflow-run.js';
|
|
2
2
|
export declare function formatJobExecutionTimeLabel(time: JobExecutionTime): string;
|
|
3
|
-
export declare function formatJobDurationAccessibleLabel(duration: JobDisplayDuration | null): string | undefined;
|
|
3
|
+
export declare function formatJobDurationAccessibleLabel(duration: JobDisplayDuration | null, displayStatus?: JobExecutionDisplayStatus): string | undefined;
|
|
4
4
|
//# sourceMappingURL=job-duration-format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-duration-format.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-duration-format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"job-duration-format.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-duration-format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EACzB,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAE/B,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAW1E;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,kBAAkB,GAAG,IAAI,EACnC,aAAa,CAAC,EAAE,yBAAyB,GACxC,MAAM,GAAG,SAAS,CAkBpB"}
|
|
@@ -12,14 +12,16 @@ export function formatJobExecutionTimeLabel(time) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
export function formatJobDurationAccessibleLabel(duration) {
|
|
15
|
+
export function formatJobDurationAccessibleLabel(duration, displayStatus) {
|
|
16
16
|
if (duration === null) return undefined;
|
|
17
17
|
const label = formatJobExecutionTimeLabel(duration);
|
|
18
18
|
switch(duration.kind){
|
|
19
19
|
case 'queue':
|
|
20
20
|
return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;
|
|
21
21
|
case 'run':
|
|
22
|
-
|
|
22
|
+
if (duration.state !== 'live') return `ran ${label}`;
|
|
23
|
+
if (displayStatus === 'running') return `running ${label}`;
|
|
24
|
+
return displayStatus === 'pending' ? `runner assigned ${label}` : `execution elapsed ${label}`;
|
|
23
25
|
default:
|
|
24
26
|
{
|
|
25
27
|
const exhaustive = duration;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/job-graph/job-duration-format.ts"],"sourcesContent":["import {humanDuration} from '@shipfox/react-ui/utils';\nimport type {JobDisplayDuration
|
|
1
|
+
{"version":3,"sources":["../../../src/components/job-graph/job-duration-format.ts"],"sourcesContent":["import {humanDuration} from '@shipfox/react-ui/utils';\nimport type {\n JobDisplayDuration,\n JobExecutionDisplayStatus,\n JobExecutionTime,\n} from '#core/workflow-run.js';\n\nexport function formatJobExecutionTimeLabel(time: JobExecutionTime): string {\n switch (time.state) {\n case 'live':\n return humanDuration(time.fromIso);\n case 'fixed':\n return formatFixedDurationLabel(time.elapsed);\n default: {\n const exhaustive: never = time;\n return exhaustive;\n }\n }\n}\n\nexport function formatJobDurationAccessibleLabel(\n duration: JobDisplayDuration | null,\n displayStatus?: JobExecutionDisplayStatus,\n): string | undefined {\n if (duration === null) return undefined;\n\n const label = formatJobExecutionTimeLabel(duration);\n switch (duration.kind) {\n case 'queue':\n return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;\n case 'run':\n if (duration.state !== 'live') return `ran ${label}`;\n if (displayStatus === 'running') return `running ${label}`;\n return displayStatus === 'pending'\n ? `runner assigned ${label}`\n : `execution elapsed ${label}`;\n default: {\n const exhaustive: never = duration;\n return exhaustive;\n }\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<JobExecutionTime, {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":["humanDuration","formatJobExecutionTimeLabel","time","state","fromIso","formatFixedDurationLabel","elapsed","exhaustive","formatJobDurationAccessibleLabel","duration","displayStatus","undefined","label","kind","years","months","weeks","days","hours","minutes","seconds","totalDays","totalHours","pad2","value","toString","padStart"],"mappings":"AAAA,SAAQA,aAAa,QAAO,0BAA0B;AAOtD,OAAO,SAASC,4BAA4BC,IAAsB;IAChE,OAAQA,KAAKC,KAAK;QAChB,KAAK;YACH,OAAOH,cAAcE,KAAKE,OAAO;QACnC,KAAK;YACH,OAAOC,yBAAyBH,KAAKI,OAAO;QAC9C;YAAS;gBACP,MAAMC,aAAoBL;gBAC1B,OAAOK;YACT;IACF;AACF;AAEA,OAAO,SAASC,iCACdC,QAAmC,EACnCC,aAAyC;IAEzC,IAAID,aAAa,MAAM,OAAOE;IAE9B,MAAMC,QAAQX,4BAA4BQ;IAC1C,OAAQA,SAASI,IAAI;QACnB,KAAK;YACH,OAAOJ,SAASN,KAAK,KAAK,SAAS,CAAC,SAAS,EAAES,OAAO,GAAG,CAAC,OAAO,EAAEA,OAAO;QAC5E,KAAK;YACH,IAAIH,SAASN,KAAK,KAAK,QAAQ,OAAO,CAAC,IAAI,EAAES,OAAO;YACpD,IAAIF,kBAAkB,WAAW,OAAO,CAAC,QAAQ,EAAEE,OAAO;YAC1D,OAAOF,kBAAkB,YACrB,CAAC,gBAAgB,EAAEE,OAAO,GAC1B,CAAC,kBAAkB,EAAEA,OAAO;QAClC;YAAS;gBACP,MAAML,aAAoBE;gBAC1B,OAAOF;YACT;IACF;AACF;AAEA,SAASF,yBAAyB,EAChCS,QAAQ,CAAC,EACTC,SAAS,CAAC,EACVC,QAAQ,CAAC,EACTC,OAAO,CAAC,EACRC,QAAQ,CAAC,EACTC,UAAU,CAAC,EACXC,UAAU,CAAC,EAC4C;IACvD,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"}
|
|
@@ -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":";AAAA,SAAQA,UAAU,QAAO,gCAAgC;AACzD,SAAQC,kBAAkB,QAAO,gCAAgC;AACjE,SAAQC,EAAE,QAAO,0BAA0B;AAE3C,SAAQC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAGvC,SAAQC,kBAAkB,QAAO,mBAAmB;AACpD,SAAQC,OAAO,EAAEC,WAAW,QAAO,gBAAgB;AAGnD,MAAMC,aAAa;AACnB,MAAMC,cAAc;AACpB,MAAMC,aAAa;AACnB,MAAMC,UAAU;AAChB,MAAMC,gBAAgB;AACtB,MAAMC,UAAU;AAEhB,OAAO,SAASC,gBAAgB,EAC9BC,KAAK,EACLC,OAAO,EACPC,aAAa,EACbC,WAAW,EASZ;IACC,MAAMC,WAAWhB,OAAO,IAAIiB;IAC5B,MAAM,CAACC,cAAcC,gBAAgB,GAAGlB;IAExC,IAAIW,MAAMQ,KAAK,CAACC,MAAM,KAAK,GAAG;QAC5B,qBACE,KAACxB;YACCyB,WAAU;YACVC,MAAK;YACLC,OAAM;YACNC,aAAY;YACZC,SAAQ;;IAGd;IAEA,MAAMC,UAAUC,KAAKC,GAAG,CAAC,MAAMjB,MAAMkB,OAAO,CAACC,GAAG,CAAC,CAACC,SAAWA,OAAOX,MAAM;IAC1E,MAAMY,eACJvB,UAAU,IACVD,gBACAF,aACAK,MAAMkB,OAAO,CAACT,MAAM,GAAGhB,aACvBuB,KAAKC,GAAG,CAAC,GAAGjB,MAAMkB,OAAO,CAACT,MAAM,GAAG,KAAKd;IAC1C,MAAM2B,gBAAgBxB,UAAU,IAAIiB,UAAUrB,cAAcsB,KAAKC,GAAG,CAAC,GAAGF,UAAU,KAAKnB;IAEvF,SAAS2B,WAAWC,KAAa;QAC/B,OAAO,CAACC;YACN,IAAIA,SAAS;gBACXrB,SAASsB,OAAO,CAACC,GAAG,CAACH,OAAOC;YAC9B,OAAO;gBACLrB,SAASsB,OAAO,CAACE,MAAM,CAACJ;YAC1B;QACF;IACF;IAEA,SAASK,cAAcC,KAAuC;QAC5D,MAAMC,MAAMC,cAAcF,MAAMC,GAAG;QACnC,IAAI,CAACA,KAAK;QAEV,MAAME,gBAAgBH,MAAMI,aAAa,CAACC,OAAO,CAACX,KAAK;QACvD,IAAI,CAACS,eAAe;QAEpB,MAAMG,aAAa9C,mBAAmB;YAACU;YAAOiC;YAAeF;QAAG;QAChE,IAAI,CAACK,YAAY;QAEjBN,MAAMO,cAAc;QACpBlC,YAAYiC,YAAY;QACxBhC,SAASsB,OAAO,CAACY,GAAG,CAACF,aAAaG;IACpC;IAEA,qBACE,KAACrD;QAAmBsD,YAAY;QAAMC,yBAAyB;kBAC7D,cAAA,KAACC;YAAIhC,WAAU;sBACb,cAAA,MAACgC;gBAAIhC,WAAU;gBAAWiC,OAAO;oBAACC,OAAOvB;oBAAcwB,WAAWvB;gBAAa;;kCAC7E,KAACwB;wBAAW9C,OAAOA;wBAAOM,cAAcA;;kCACxC,KAACoC;wBACChC,WAAU;wBACViC,OAAO;4BAACI,MAAMjD;4BAASkD,KAAKlD,UAAU,AAACJ,CAAAA,cAAcG,aAAY,IAAK;wBAAC;kCAEvE,cAAA,KAACL;4BAAYS,SAASA;;;oBAEvBD,MAAMkB,OAAO,CAACC,GAAG,CAAC,CAACC,QAAQ6B,4BAC1B,KAACP;4BAEChC,WAAU;4BACViC,OAAO;gCAACI,MAAMG,QAAQD;gCAAcD,KAAKlD;4BAAO;sCAE/CsB,OAAOD,GAAG,CAAC,CAACgC,qBACX,KAAC5D;oCAEC4D,MAAMA;oCACNC,UAAUD,KAAKE,EAAE,KAAKnD;oCACtBoD,KAAK/B,WAAW4B,KAAKE,EAAE;oCACvBE,UAAU,IACRpD,YAAYgD,KAAKE,EAAE,KAAKnD,gBAAgBsD,YAAYL,KAAKE,EAAE,EAAE;oCAE/DI,WAAW5B;oCACX6B,cAAc,IAAMnD,gBAAgB4C,KAAKE,EAAE;oCAC3CM,YAAY,IACVpD,gBAAgB,CAACmB,UAAaA,YAAYyB,KAAKE,EAAE,GAAGG,YAAY9B;mCAV7DyB,KAAKE,EAAE;2BANXjC,OAAOD,GAAG,CAAC,CAACgC,OAASA,KAAKE,EAAE,EAAEO,IAAI,CAAC;;;;;AA0BtD;AAEA,SAAS5B,cAAcD,GAAW;IAChC,IACEA,QAAQ,gBACRA,QAAQ,eACRA,QAAQ,eACRA,QAAQ,aACRA,QAAQ,OACRA,QAAQ,KACR;QACA,OAAOA;IACT;IACA,OAAOyB;AACT;AAEA,SAASV,WAAW,EAClB9C,KAAK,EACLM,YAAY,EAIb;IACC,MAAMuD,OAAO,IAAIxD,IAAIL,MAAMQ,KAAK,CAACW,GAAG,CAAC,CAACgC,OAAS;YAACA,KAAKE,EAAE;YAAEF;SAAK;IAC9D,MAAMW,YAAY9D,MAAM+D,KAAK,CAC1B5C,GAAG,CAAC,CAAC6C;QACJ,MAAMC,WAAWJ,KAAKvB,GAAG,CAAC0B,KAAKE,IAAI;QACnC,MAAMC,SAASN,KAAKvB,GAAG,CAAC0B,KAAKI,EAAE;QAC/B,MAAMF,OAAOF,KAAKE,IAAI,KAAK,YAAYG,iBAAiBC,cAAcL;QACtE,MAAMG,KAAKG,aAAaJ;QACxB,IAAI,CAACD,QAAQ,CAACE,IAAI,OAAOZ;QAEzB,OAAO;YACLH,IAAIW,KAAKX,EAAE;YACXmB,aAAaR,KAAKE,IAAI,KAAK5D,gBAAgB0D,KAAKI,EAAE,KAAK9D;YACvDmE,MAAMC,SAAS;gBAACR;gBAAMD;gBAAUG;gBAAID;YAAM;QAC5C;IACF,GACCQ,MAAM,CAAC,CAACC,WAAuDA,aAAapB,WAC5EqB,IAAI,CAAC,CAAC9B,MAAM+B,QAAUC,OAAOhC,KAAKyB,WAAW,IAAIO,OAAOD,MAAMN,WAAW;IAE5E,qBACE,KAACQ;QAAItE,WAAU;QAAiDuE,eAAY;kBACzEnB,UAAU3C,GAAG,CAAC,CAAC6C;YACd,qBACE,KAACkB;gBAECxE,WAAWvB,GACT,mDACA6E,KAAKQ,WAAW,IAAI;0BAGtB,cAAA,KAACC;oBACCU,gBAAcnB,KAAKX,EAAE;oBACrB+B,GAAGpB,KAAKS,IAAI;oBACZY,MAAK;oBACLC,QAAO;oBACPC,aAAavB,KAAKQ,WAAW,GAAG,MAAM;;eAXnCR,KAAKX,EAAE;QAelB;;AAGN;AAEA,SAASqB,SAAS,EAChBR,IAAI,EACJD,QAAQ,EACRG,EAAE,EACFD,MAAM,EAMP;IACC,IAAIF,YAAYE,UAAUA,OAAO/C,MAAM,GAAG6C,SAAS7C,MAAM,GAAG,KAAK8C,KAAKsB,CAAC,KAAKpB,GAAGoB,CAAC,EAAE;QAChF,MAAMC,cAAcrB,GAAGsB,CAAC,GAAG/F,aAAa;QACxC,OAAO,CAAC,EAAE,EAAEuE,KAAKwB,CAAC,CAAC,CAAC,EAAExB,KAAKsB,CAAC,CAAC,GAAG,EAAEC,YAAY,GAAG,EAAErB,GAAGoB,CAAC,CAAC,GAAG,EAAEpB,GAAGsB,CAAC,EAAE;IACrE;IAEA,MAAMC,OAAOzB,KAAKwB,CAAC,GAAG,AAACtB,CAAAA,GAAGsB,CAAC,GAAGxB,KAAKwB,CAAC,AAADA,IAAK;IACxC,OAAO,CAAC,EAAE,EAAExB,KAAKwB,CAAC,CAAC,CAAC,EAAExB,KAAKsB,CAAC,CAAC,GAAG,EAAEG,KAAK,GAAG,EAAEvB,GAAGoB,CAAC,CAAC,GAAG,EAAEpB,GAAGsB,CAAC,EAAE;AAC9D;AAEA,SAASrB;IACP,OAAO;QAACqB,GAAG5F,UAAUD;QAAe2F,GAAG1F,UAAUJ,cAAc;IAAC;AAClE;AAEA,SAASwD,QAAQ9B,MAAc;IAC7B,yFAAyF;IACzF,OAAOtB,UAAUD,gBAAgBF,aAAayB,SAAU3B,CAAAA,aAAaE,UAAS;AAChF;AAEA,SAASiG,OAAOC,GAAW;IACzB,OAAO/F,UAAU+F,MAAOnG,CAAAA,cAAcE,OAAM;AAC9C;AAEA,SAAS2E,aAAapB,IAA+C;IACnE,IAAI,CAACA,MAAM,OAAOK;IAClB,OAAO;QAACkC,GAAGxC,QAAQC,KAAK/B,MAAM;QAAGoE,GAAGI,OAAOzC,KAAK0C,GAAG,IAAInG,cAAc;IAAC;AACxE;AAEA,SAAS4E,cAAcnB,IAA+C;IACpE,IAAI,CAACA,MAAM,OAAOK;IAClB,OAAO;QAACkC,GAAGxC,QAAQC,KAAK/B,MAAM,IAAI3B;QAAY+F,GAAGI,OAAOzC,KAAK0C,GAAG,IAAInG,cAAc;IAAC;AACrF"}
|
|
@@ -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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KeyboardEventHandler, Ref } from 'react';
|
|
2
|
-
import type
|
|
2
|
+
import { type WorkflowRunDetail } from '#core/workflow-run.js';
|
|
3
3
|
import type { JobGraphNode } from './graph-model.js';
|
|
4
4
|
export declare function TriggerNode({ trigger, }: {
|
|
5
5
|
trigger: Pick<WorkflowRunDetail, 'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-node.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-node.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAE,GAAG,EAAC,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"job-node.d.ts","sourceRoot":"","sources":["../../../src/components/job-graph/job-node.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,oBAAoB,EAAE,GAAG,EAAC,MAAM,OAAO,CAAC;AAGrD,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAMnD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,GACR,EAAE;IACD,OAAO,EAAE,IAAI,CACX,iBAAiB,EACjB,qBAAqB,GAAG,cAAc,GAAG,iBAAiB,GAAG,eAAe,CAC7E,CAAC;CACH,+BA0BA;AAED,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,EACV,GAAG,GACJ,EAAE;IACD,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IACnD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;CAC9B,+BAoDA"}
|
|
@@ -9,6 +9,7 @@ import { Code } from '@shipfox/react-ui/typography';
|
|
|
9
9
|
import { cn } from '@shipfox/react-ui/utils';
|
|
10
10
|
import { getWorkflowStatusVisual } from '#components/workflow-status/status-visuals.js';
|
|
11
11
|
import { WorkflowStatusIcon } from '#components/workflow-status/workflow-status-icon.js';
|
|
12
|
+
import { defaultJobExecution, deriveJobDisplayStatus, deriveJobExecutionDisplayStatus } from '#core/workflow-run.js';
|
|
12
13
|
import { formatJobDurationAccessibleLabel } from './job-duration-format.js';
|
|
13
14
|
import { JobDurationLabel } from './job-duration-label.js';
|
|
14
15
|
const TRIGGER_SIZE = 36;
|
|
@@ -22,7 +23,7 @@ export function TriggerNode({ trigger }) {
|
|
|
22
23
|
children: /*#__PURE__*/ _jsx("button", {
|
|
23
24
|
type: "button",
|
|
24
25
|
"aria-label": label,
|
|
25
|
-
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",
|
|
26
27
|
style: {
|
|
27
28
|
width: TRIGGER_SIZE,
|
|
28
29
|
height: TRIGGER_SIZE
|
|
@@ -31,7 +32,7 @@ export function TriggerNode({ trigger }) {
|
|
|
31
32
|
provider: trigger.triggerProvider,
|
|
32
33
|
source: trigger.triggerSource,
|
|
33
34
|
"aria-hidden": true,
|
|
34
|
-
className: "size-14 shrink-0 text-foreground-neutral-
|
|
35
|
+
className: "size-14 shrink-0 text-foreground-neutral-subtle"
|
|
35
36
|
})
|
|
36
37
|
})
|
|
37
38
|
}),
|
|
@@ -45,12 +46,13 @@ export function TriggerNode({ trigger }) {
|
|
|
45
46
|
}
|
|
46
47
|
export function JobNode({ node, selected, onSelect, onKeyDown, onHoverStart, onHoverEnd, ref }) {
|
|
47
48
|
useTimeTick();
|
|
48
|
-
const status = node
|
|
49
|
+
const status = deriveJobDisplayStatus(node);
|
|
50
|
+
const execution = defaultJobExecution(node);
|
|
49
51
|
const visual = getWorkflowStatusVisual(status);
|
|
50
52
|
const accessibleLabel = [
|
|
51
53
|
node.displayName,
|
|
52
54
|
visual.label,
|
|
53
|
-
formatJobDurationAccessibleLabel(node.displayDuration),
|
|
55
|
+
formatJobDurationAccessibleLabel(node.displayDuration, execution ? deriveJobExecutionDisplayStatus(execution) : undefined),
|
|
54
56
|
node.executionCountVisible ? executionCountAccessibleLabel(node.jobExecutions.length) : undefined,
|
|
55
57
|
node.carriedOver ? 'reused' : undefined
|
|
56
58
|
].filter((part)=>Boolean(part)).join(', ');
|
|
@@ -64,18 +66,17 @@ export function JobNode({ node, selected, onSelect, onKeyDown, onHoverStart, onH
|
|
|
64
66
|
onKeyDown: onKeyDown,
|
|
65
67
|
onPointerEnter: onHoverStart,
|
|
66
68
|
onPointerLeave: onHoverEnd,
|
|
67
|
-
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]'),
|
|
68
70
|
children: [
|
|
69
71
|
selected ? /*#__PURE__*/ _jsx("span", {
|
|
70
72
|
"aria-hidden": "true",
|
|
71
|
-
className: "absolute inset-y-6 left-0 w-
|
|
73
|
+
className: "absolute inset-y-6 left-0 w-px rounded-full bg-border-highlights-interactive"
|
|
72
74
|
}) : null,
|
|
73
75
|
/*#__PURE__*/ _jsxs("div", {
|
|
74
76
|
className: "flex min-w-0 flex-1 items-center gap-8",
|
|
75
77
|
children: [
|
|
76
78
|
/*#__PURE__*/ _jsx(WorkflowStatusIcon, {
|
|
77
79
|
status: status,
|
|
78
|
-
jobMode: node.mode,
|
|
79
80
|
size: 14
|
|
80
81
|
}),
|
|
81
82
|
/*#__PURE__*/ _jsx(JobLabel, {
|
|
@@ -101,7 +102,7 @@ function ExecutionCountText({ executions }) {
|
|
|
101
102
|
asChild: true,
|
|
102
103
|
children: /*#__PURE__*/ _jsxs("span", {
|
|
103
104
|
"aria-hidden": "true",
|
|
104
|
-
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",
|
|
105
106
|
children: [
|
|
106
107
|
/*#__PURE__*/ _jsx(Icon, {
|
|
107
108
|
name: "loopRightLine",
|
|
@@ -183,7 +184,7 @@ const EXECUTION_STATUSES = [
|
|
|
183
184
|
];
|
|
184
185
|
function executionStatusCounts(executions) {
|
|
185
186
|
return executions.reduce((counts, execution)=>{
|
|
186
|
-
counts[execution
|
|
187
|
+
counts[deriveJobExecutionDisplayStatus(execution)] += 1;
|
|
187
188
|
return counts;
|
|
188
189
|
}, {
|
|
189
190
|
pending: 0,
|
|
@@ -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 type {JobExecution, JobExecutionStatus, WorkflowRunDetail} 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 =\n node.status === 'pending' &&\n node.jobExecutions.some((jobExecution) => jobExecution.status === 'running')\n ? 'running'\n : node.status;\n const visual = getWorkflowStatusVisual(status);\n const accessibleLabel = [\n node.displayName,\n visual.label,\n formatJobDurationAccessibleLabel(node.displayDuration),\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} jobMode={node.mode} 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[execution.status] += 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","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","jobExecutions","some","jobExecution","visual","accessibleLabel","displayName","displayDuration","executionCountVisible","executionCountAccessibleLabel","length","undefined","carriedOver","filter","part","Boolean","join","aria-pressed","data-job-id","id","onClick","onPointerEnter","onPointerLeave","div","jobMode","mode","size","JobLabel","duration","ExecutionCountText","executions","CarriedOverBadge","count","name","as","variant","executionCountTooltip","labelRef","isTruncated","bold","counts","executionStatusCounts","parts","EXECUTION_STATUSES","map","reduce","execution","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"}
|