@nvent-addon/app 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/module.d.mts +6 -0
  2. package/dist/module.json +9 -0
  3. package/dist/module.mjs +42 -0
  4. package/dist/runtime/app/assets/vueflow.css +1 -0
  5. package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +33 -0
  6. package/dist/runtime/app/components/ConfirmDialog.vue +121 -0
  7. package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +33 -0
  8. package/dist/runtime/app/components/FlowDiagram.d.vue.ts +65 -0
  9. package/dist/runtime/app/components/FlowDiagram.vue +341 -0
  10. package/dist/runtime/app/components/FlowDiagram.vue.d.ts +65 -0
  11. package/dist/runtime/app/components/FlowNodeCard.d.vue.ts +29 -0
  12. package/dist/runtime/app/components/FlowNodeCard.vue +158 -0
  13. package/dist/runtime/app/components/FlowNodeCard.vue.d.ts +29 -0
  14. package/dist/runtime/app/components/FlowRunOverview.d.vue.ts +17 -0
  15. package/dist/runtime/app/components/FlowRunOverview.vue +188 -0
  16. package/dist/runtime/app/components/FlowRunOverview.vue.d.ts +17 -0
  17. package/dist/runtime/app/components/FlowRunStatusBadge.d.vue.ts +18 -0
  18. package/dist/runtime/app/components/FlowRunStatusBadge.vue +74 -0
  19. package/dist/runtime/app/components/FlowRunStatusBadge.vue.d.ts +18 -0
  20. package/dist/runtime/app/components/FlowRunTimeline.d.vue.ts +12 -0
  21. package/dist/runtime/app/components/FlowRunTimeline.vue +127 -0
  22. package/dist/runtime/app/components/FlowRunTimeline.vue.d.ts +12 -0
  23. package/dist/runtime/app/components/FlowScheduleDialog.d.vue.ts +16 -0
  24. package/dist/runtime/app/components/FlowScheduleDialog.vue +226 -0
  25. package/dist/runtime/app/components/FlowScheduleDialog.vue.d.ts +16 -0
  26. package/dist/runtime/app/components/FlowSchedulesList.d.vue.ts +12 -0
  27. package/dist/runtime/app/components/FlowSchedulesList.vue +99 -0
  28. package/dist/runtime/app/components/FlowSchedulesList.vue.d.ts +12 -0
  29. package/dist/runtime/app/components/FlowStepSelector.d.vue.ts +22 -0
  30. package/dist/runtime/app/components/FlowStepSelector.vue +238 -0
  31. package/dist/runtime/app/components/FlowStepSelector.vue.d.ts +22 -0
  32. package/dist/runtime/app/components/JobScheduling.d.vue.ts +6 -0
  33. package/dist/runtime/app/components/JobScheduling.vue +203 -0
  34. package/dist/runtime/app/components/JobScheduling.vue.d.ts +6 -0
  35. package/dist/runtime/app/components/ListItem.d.vue.ts +23 -0
  36. package/dist/runtime/app/components/ListItem.vue +70 -0
  37. package/dist/runtime/app/components/ListItem.vue.d.ts +23 -0
  38. package/dist/runtime/app/components/QueueConfigDetails.d.vue.ts +45 -0
  39. package/dist/runtime/app/components/QueueConfigDetails.vue +412 -0
  40. package/dist/runtime/app/components/QueueConfigDetails.vue.d.ts +45 -0
  41. package/dist/runtime/app/components/StatCounter.d.vue.ts +9 -0
  42. package/dist/runtime/app/components/StatCounter.vue +25 -0
  43. package/dist/runtime/app/components/StatCounter.vue.d.ts +9 -0
  44. package/dist/runtime/app/components/TimelineList.d.vue.ts +7 -0
  45. package/dist/runtime/app/components/TimelineList.vue +211 -0
  46. package/dist/runtime/app/components/TimelineList.vue.d.ts +7 -0
  47. package/dist/runtime/app/components/nhealth/component-router.d.vue.ts +46 -0
  48. package/dist/runtime/app/components/nhealth/component-router.vue +26 -0
  49. package/dist/runtime/app/components/nhealth/component-router.vue.d.ts +46 -0
  50. package/dist/runtime/app/components/nhealth/component-shell.d.vue.ts +24 -0
  51. package/dist/runtime/app/components/nhealth/component-shell.vue +89 -0
  52. package/dist/runtime/app/components/nhealth/component-shell.vue.d.ts +24 -0
  53. package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +14 -0
  54. package/dist/runtime/app/composables/useAnalyzedFlows.js +8 -0
  55. package/dist/runtime/app/composables/useComponentRouter.d.ts +38 -0
  56. package/dist/runtime/app/composables/useComponentRouter.js +240 -0
  57. package/dist/runtime/app/composables/useFlowRunTimeline.d.ts +82 -0
  58. package/dist/runtime/app/composables/useFlowRunTimeline.js +67 -0
  59. package/dist/runtime/app/composables/useFlowRuns.d.ts +18 -0
  60. package/dist/runtime/app/composables/useFlowRuns.js +32 -0
  61. package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +24 -0
  62. package/dist/runtime/app/composables/useFlowRunsInfinite.js +123 -0
  63. package/dist/runtime/app/composables/useFlowRunsPolling.d.ts +9 -0
  64. package/dist/runtime/app/composables/useFlowRunsPolling.js +33 -0
  65. package/dist/runtime/app/composables/useFlowState.d.ts +127 -0
  66. package/dist/runtime/app/composables/useFlowState.js +225 -0
  67. package/dist/runtime/app/composables/useFlowWebSocket.d.ts +27 -0
  68. package/dist/runtime/app/composables/useFlowWebSocket.js +222 -0
  69. package/dist/runtime/app/composables/useFlowsNavigation.d.ts +10 -0
  70. package/dist/runtime/app/composables/useFlowsNavigation.js +58 -0
  71. package/dist/runtime/app/composables/useQueueJobs.d.ts +26 -0
  72. package/dist/runtime/app/composables/useQueueJobs.js +20 -0
  73. package/dist/runtime/app/composables/useQueueUpdates.d.ts +24 -0
  74. package/dist/runtime/app/composables/useQueueUpdates.js +54 -0
  75. package/dist/runtime/app/composables/useQueues.d.ts +45 -0
  76. package/dist/runtime/app/composables/useQueues.js +26 -0
  77. package/dist/runtime/app/composables/useQueuesLive.d.ts +16 -0
  78. package/dist/runtime/app/composables/useQueuesLive.js +62 -0
  79. package/dist/runtime/app/composables/useQueuesWebSocket.d.ts +17 -0
  80. package/dist/runtime/app/composables/useQueuesWebSocket.js +159 -0
  81. package/dist/runtime/app/pages/flows/index.d.vue.ts +3 -0
  82. package/dist/runtime/app/pages/flows/index.vue +683 -0
  83. package/dist/runtime/app/pages/flows/index.vue.d.ts +3 -0
  84. package/dist/runtime/app/pages/index.d.vue.ts +3 -0
  85. package/dist/runtime/app/pages/index.vue +34 -0
  86. package/dist/runtime/app/pages/index.vue.d.ts +3 -0
  87. package/dist/runtime/app/pages/queues/index.d.vue.ts +3 -0
  88. package/dist/runtime/app/pages/queues/index.vue +229 -0
  89. package/dist/runtime/app/pages/queues/index.vue.d.ts +3 -0
  90. package/dist/runtime/app/pages/queues/job.d.vue.ts +3 -0
  91. package/dist/runtime/app/pages/queues/job.vue +262 -0
  92. package/dist/runtime/app/pages/queues/job.vue.d.ts +3 -0
  93. package/dist/runtime/app/pages/queues/jobs.d.vue.ts +3 -0
  94. package/dist/runtime/app/pages/queues/jobs.vue +291 -0
  95. package/dist/runtime/app/pages/queues/jobs.vue.d.ts +3 -0
  96. package/dist/runtime/app/plugins/vueflow.client.d.ts +2 -0
  97. package/dist/runtime/app/plugins/vueflow.client.js +11 -0
  98. package/dist/types.d.mts +7 -0
  99. package/package.json +47 -0
@@ -0,0 +1,6 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ type ModuleOptions = Record<string, unknown>;
4
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
5
+
6
+ export { _default as default };
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "nventapp",
3
+ "version": "0.1",
4
+ "configKey": "nventapp",
5
+ "builder": {
6
+ "@nuxt/module-builder": "1.0.2",
7
+ "unbuild": "3.6.1"
8
+ }
9
+ }
@@ -0,0 +1,42 @@
1
+ import { defineNuxtModule, createResolver, addImportsDir, addPlugin, addComponentsDir, addComponent } from '@nuxt/kit';
2
+ import defu from 'defu';
3
+
4
+ const meta = {
5
+ name: "nventapp",
6
+ version: "0.1",
7
+ configKey: "nventapp"
8
+ };
9
+ const module$1 = defineNuxtModule({
10
+ meta,
11
+ defaults: {},
12
+ moduleDependencies: {
13
+ "json-editor-vue/nuxt": {
14
+ version: "0.18.1"
15
+ }
16
+ },
17
+ async setup(options, nuxt) {
18
+ const { resolve } = createResolver(import.meta.url);
19
+ addImportsDir(resolve("./runtime/shared/utils"));
20
+ addImportsDir(resolve("./runtime/app/composables"));
21
+ nuxt.options.css = nuxt.options.css || [];
22
+ nuxt.options.css.push(resolve("./runtime/app/assets/vueflow.css"));
23
+ addPlugin({
24
+ src: resolve("./runtime/app/plugins/vueflow.client"),
25
+ mode: "client"
26
+ });
27
+ addComponentsDir({
28
+ path: resolve("./runtime/app/components"),
29
+ prefix: "Queue"
30
+ });
31
+ addComponent({
32
+ name: "QueueApp",
33
+ filePath: resolve("./runtime/app/pages/index.vue"),
34
+ global: true
35
+ });
36
+ nuxt.options.vite.optimizeDeps = defu(nuxt.options.vite.optimizeDeps, {
37
+ include: ["vanilla-jsoneditor"]
38
+ });
39
+ }
40
+ });
41
+
42
+ export { module$1 as default };
@@ -0,0 +1 @@
1
+ @import "@vue-flow/core/dist/style.css";@import "@vue-flow/core/dist/theme-default.css";@import "@vue-flow/controls/dist/style.css";@import "@vue-flow/minimap/dist/style.css";
@@ -0,0 +1,33 @@
1
+ interface Props {
2
+ title: string;
3
+ description?: string;
4
+ items?: string[];
5
+ warning?: string;
6
+ confirmLabel?: string;
7
+ cancelLabel?: string;
8
+ confirmColor?: 'primary' | 'error' | 'warning' | 'success';
9
+ icon?: string;
10
+ iconColor?: 'primary' | 'error' | 'warning' | 'success' | 'info';
11
+ loading?: boolean;
12
+ }
13
+ type __VLS_Props = Props;
14
+ type __VLS_ModelProps = {
15
+ 'open'?: boolean;
16
+ };
17
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
18
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
+ cancel: () => any;
20
+ "update:open": (value: boolean) => any;
21
+ confirm: () => any;
22
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
23
+ onCancel?: (() => any) | undefined;
24
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
25
+ onConfirm?: (() => any) | undefined;
26
+ }>, {
27
+ confirmLabel: string;
28
+ cancelLabel: string;
29
+ confirmColor: "primary" | "error" | "warning" | "success";
30
+ iconColor: "primary" | "error" | "warning" | "success" | "info";
31
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
+ declare const _default: typeof __VLS_export;
33
+ export default _default;
@@ -0,0 +1,121 @@
1
+ <template>
2
+ <UModal
3
+ v-model:open="isOpen"
4
+ :ui="{
5
+ content: 'max-w-md'
6
+ }"
7
+ >
8
+ <template #header>
9
+ <div class="flex items-start gap-3">
10
+ <div
11
+ v-if="icon"
12
+ class="shrink-0 rounded-full p-2"
13
+ :class="iconColorClasses"
14
+ >
15
+ <UIcon
16
+ :name="icon"
17
+ class="size-5"
18
+ />
19
+ </div>
20
+ <div class="flex-1 min-w-0">
21
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">
22
+ {{ title }}
23
+ </h3>
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <template #body>
29
+ <div class="space-y-3">
30
+ <p
31
+ v-if="description"
32
+ class="text-sm text-gray-600 dark:text-gray-400"
33
+ >
34
+ {{ description }}
35
+ </p>
36
+ <ul
37
+ v-if="items && items.length > 0"
38
+ class="space-y-2 text-sm text-gray-600 dark:text-gray-400"
39
+ >
40
+ <li
41
+ v-for="(item, index) in items"
42
+ :key="index"
43
+ class="flex items-start gap-2"
44
+ >
45
+ <UIcon
46
+ name="i-lucide-circle"
47
+ class="size-1.5 mt-1.5 shrink-0"
48
+ />
49
+ <span>{{ item }}</span>
50
+ </li>
51
+ </ul>
52
+ <p
53
+ v-if="warning"
54
+ class="text-sm font-medium text-amber-600 dark:text-amber-400"
55
+ >
56
+ {{ warning }}
57
+ </p>
58
+ </div>
59
+ </template>
60
+
61
+ <template #footer>
62
+ <div class="flex justify-end gap-2">
63
+ <UButton
64
+ color="neutral"
65
+ variant="ghost"
66
+ :disabled="loading"
67
+ @click="handleCancel"
68
+ >
69
+ {{ cancelLabel }}
70
+ </UButton>
71
+ <UButton
72
+ :color="confirmColor"
73
+ :loading="loading"
74
+ @click="handleConfirm"
75
+ >
76
+ {{ confirmLabel }}
77
+ </UButton>
78
+ </div>
79
+ </template>
80
+ </UModal>
81
+ </template>
82
+
83
+ <script setup>
84
+ import { computed } from "#imports";
85
+ import { UModal, UButton, UIcon } from "#components";
86
+ const props = defineProps({
87
+ title: { type: String, required: true },
88
+ description: { type: String, required: false },
89
+ items: { type: Array, required: false },
90
+ warning: { type: String, required: false },
91
+ confirmLabel: { type: String, required: false, default: "Confirm" },
92
+ cancelLabel: { type: String, required: false, default: "Cancel" },
93
+ confirmColor: { type: String, required: false, default: "primary" },
94
+ icon: { type: String, required: false },
95
+ iconColor: { type: String, required: false, default: "primary" },
96
+ loading: { type: Boolean, required: false }
97
+ });
98
+ const isOpen = defineModel("open", { type: Boolean, ...{ default: false } });
99
+ const emit = defineEmits(["confirm", "cancel"]);
100
+ const iconColorClasses = computed(() => {
101
+ switch (props.iconColor) {
102
+ case "error":
103
+ return "bg-red-50 dark:bg-red-950/50 text-red-600 dark:text-red-400";
104
+ case "warning":
105
+ return "bg-amber-50 dark:bg-amber-950/50 text-amber-600 dark:text-amber-400";
106
+ case "success":
107
+ return "bg-emerald-50 dark:bg-emerald-950/50 text-emerald-600 dark:text-emerald-400";
108
+ case "info":
109
+ return "bg-blue-50 dark:bg-blue-950/50 text-blue-600 dark:text-blue-400";
110
+ default:
111
+ return "bg-primary-50 dark:bg-primary-950/50 text-primary-600 dark:text-primary-400";
112
+ }
113
+ });
114
+ const handleConfirm = () => {
115
+ emit("confirm");
116
+ };
117
+ const handleCancel = () => {
118
+ isOpen.value = false;
119
+ emit("cancel");
120
+ };
121
+ </script>
@@ -0,0 +1,33 @@
1
+ interface Props {
2
+ title: string;
3
+ description?: string;
4
+ items?: string[];
5
+ warning?: string;
6
+ confirmLabel?: string;
7
+ cancelLabel?: string;
8
+ confirmColor?: 'primary' | 'error' | 'warning' | 'success';
9
+ icon?: string;
10
+ iconColor?: 'primary' | 'error' | 'warning' | 'success' | 'info';
11
+ loading?: boolean;
12
+ }
13
+ type __VLS_Props = Props;
14
+ type __VLS_ModelProps = {
15
+ 'open'?: boolean;
16
+ };
17
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
18
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
19
+ cancel: () => any;
20
+ "update:open": (value: boolean) => any;
21
+ confirm: () => any;
22
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
23
+ onCancel?: (() => any) | undefined;
24
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
25
+ onConfirm?: (() => any) | undefined;
26
+ }>, {
27
+ confirmLabel: string;
28
+ cancelLabel: string;
29
+ confirmColor: "primary" | "error" | "warning" | "success";
30
+ iconColor: "primary" | "error" | "warning" | "success" | "info";
31
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
+ declare const _default: typeof __VLS_export;
33
+ export default _default;
@@ -0,0 +1,65 @@
1
+ interface FlowEntry {
2
+ step: string;
3
+ queue: string;
4
+ workerId: string;
5
+ runtime?: 'nodejs' | 'python';
6
+ runtype?: 'inprocess' | 'task';
7
+ emits?: string[];
8
+ }
9
+ interface FlowStep {
10
+ queue: string;
11
+ workerId: string;
12
+ subscribes?: string[];
13
+ runtime?: 'nodejs' | 'python';
14
+ runtype?: 'inprocess' | 'task';
15
+ emits?: string[];
16
+ }
17
+ interface AnalyzedStep extends FlowStep {
18
+ name: string;
19
+ dependsOn: string[];
20
+ triggers: string[];
21
+ level: number;
22
+ }
23
+ interface FlowMeta {
24
+ id: string;
25
+ entry?: FlowEntry;
26
+ steps?: Record<string, FlowStep>;
27
+ analyzed?: {
28
+ levels: string[][];
29
+ maxLevel: number;
30
+ steps: Record<string, AnalyzedStep>;
31
+ };
32
+ }
33
+ interface StepStatus {
34
+ status: 'pending' | 'running' | 'completed' | 'failed' | 'retrying' | 'waiting' | 'timeout' | 'canceled';
35
+ attempt?: number;
36
+ error?: string;
37
+ }
38
+ type __VLS_Props = {
39
+ flow?: FlowMeta | null;
40
+ heightClass?: string;
41
+ showControls?: boolean;
42
+ showMiniMap?: boolean;
43
+ showBackground?: boolean;
44
+ stepStates?: Record<string, StepStatus>;
45
+ flowStatus?: 'running' | 'completed' | 'failed' | 'canceled';
46
+ };
47
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
48
+ nodeSelected: (payload: {
49
+ id: string;
50
+ }) => any;
51
+ nodeAction: (payload: {
52
+ id: string;
53
+ action: "run" | "logs" | "details";
54
+ }) => any;
55
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
56
+ onNodeSelected?: ((payload: {
57
+ id: string;
58
+ }) => any) | undefined;
59
+ onNodeAction?: ((payload: {
60
+ id: string;
61
+ action: "run" | "logs" | "details";
62
+ }) => any) | undefined;
63
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
64
+ declare const _default: typeof __VLS_export;
65
+ export default _default;
@@ -0,0 +1,341 @@
1
+ <template>
2
+ <div
3
+ :class="heightClass"
4
+ class="w-full border rounded bg-white/5"
5
+ >
6
+ <ClientOnly>
7
+ <div class="relative h-full">
8
+ <button
9
+ v-if="flowId"
10
+ class="absolute z-10 top-2 right-2 bg-gray-700 hover:bg-gray-600 text-white text-xs px-2 py-1 rounded"
11
+ type="button"
12
+ title="Reset layout"
13
+ @click="resetLayout()"
14
+ >
15
+ Reset
16
+ </button>
17
+ <VueFlow
18
+ ref="vueFlowRef"
19
+ v-model:nodes="internalNodes"
20
+ v-model:edges="internalEdges"
21
+ :fit-view-on-init="true"
22
+ class="h-full w-full"
23
+ @node-click="onNodeClick"
24
+ >
25
+ <template #node-flow-step="{ id, data }">
26
+ <FlowNodeCard
27
+ :id="id"
28
+ :data="data"
29
+ kind="step"
30
+ @action="onAction"
31
+ />
32
+ <Handle
33
+ type="target"
34
+ :position="Position.Top"
35
+ />
36
+ <Handle
37
+ type="source"
38
+ :position="Position.Bottom"
39
+ />
40
+ </template>
41
+
42
+ <template #node-flow-entry="{ id, data }">
43
+ <FlowNodeCard
44
+ :id="id"
45
+ :data="data"
46
+ kind="entry"
47
+ @action="onAction"
48
+ />
49
+ <Handle
50
+ type="source"
51
+ :position="Position.Bottom"
52
+ />
53
+ </template>
54
+
55
+ <Background
56
+ v-if="showBackground"
57
+ pattern-color="#888"
58
+ :gap="12"
59
+ />
60
+ <Controls v-if="showControls" />
61
+ <MiniMap v-if="showMiniMap" />
62
+ </VueFlow>
63
+ </div>
64
+ </ClientOnly>
65
+ </div>
66
+ </template>
67
+
68
+ <script setup>
69
+ import { computed, ref, watch, nextTick } from "#imports";
70
+ import { Handle, Position } from "@vue-flow/core";
71
+ import FlowNodeCard from "../components/FlowNodeCard.vue";
72
+ const props = defineProps({
73
+ flow: { type: [Object, null], required: false },
74
+ heightClass: { type: String, required: false },
75
+ showControls: { type: Boolean, required: false },
76
+ showMiniMap: { type: Boolean, required: false },
77
+ showBackground: { type: Boolean, required: false },
78
+ stepStates: { type: Object, required: false },
79
+ flowStatus: { type: String, required: false }
80
+ });
81
+ const heightClass = computed(() => props.heightClass || "h-80");
82
+ const emit = defineEmits(["nodeSelected", "nodeAction"]);
83
+ const flowId = computed(() => props.flow?.id);
84
+ const vueFlowRef = ref(null);
85
+ const nodes = computed(() => {
86
+ const out = [];
87
+ const f = props.flow;
88
+ if (!f) return out;
89
+ const states = props.stepStates || {};
90
+ const colWidth = 250;
91
+ const rowHeight = 140;
92
+ const horizontalGap = 50;
93
+ const verticalGap = 80;
94
+ const entryToStepsGap = 140;
95
+ const nodeWidth = 220;
96
+ let y = 0;
97
+ if (f.entry) {
98
+ const entryState = states[f.entry.step];
99
+ const status = mapStatusToNodeStatus(entryState?.status);
100
+ out.push({
101
+ id: `entry:${f.entry.step}`,
102
+ position: { x: -nodeWidth / 2, y },
103
+ data: {
104
+ label: f.entry.step,
105
+ queue: f.entry.queue,
106
+ status,
107
+ attempt: entryState?.attempt,
108
+ error: entryState?.error,
109
+ runtime: f.entry.runtime,
110
+ runtype: f.entry.runtype,
111
+ emits: f.entry.emits
112
+ },
113
+ type: "flow-entry",
114
+ style: { minWidth: `${nodeWidth}px` }
115
+ });
116
+ y += rowHeight + entryToStepsGap;
117
+ }
118
+ const steps = f.steps || {};
119
+ if (f.analyzed?.levels && f.analyzed.levels.length > 0) {
120
+ const levels = f.analyzed.levels.filter((level) => level.length > 0);
121
+ levels.forEach((levelSteps) => {
122
+ if (levelSteps.length === 0) return;
123
+ const cols = Math.min(4, levelSteps.length);
124
+ const rows = Math.ceil(levelSteps.length / cols);
125
+ levelSteps.forEach((stepName, idx) => {
126
+ const step = steps[stepName];
127
+ if (!step) return;
128
+ const stepState = states[stepName];
129
+ const status = mapStatusToNodeStatus(stepState?.status);
130
+ const col = idx % cols;
131
+ const row = Math.floor(idx / cols);
132
+ const remainingInLevel = levelSteps.length - row * cols;
133
+ const nodesInThisRow = Math.min(cols, remainingInLevel);
134
+ const rowWidth = nodesInThisRow * colWidth + (nodesInThisRow - 1) * horizontalGap;
135
+ const rowStartX = -rowWidth / 2;
136
+ const x = rowStartX + col * (colWidth + horizontalGap);
137
+ const yPos = y + row * (rowHeight + verticalGap);
138
+ out.push({
139
+ id: `step:${stepName}`,
140
+ position: { x, y: yPos },
141
+ data: {
142
+ label: stepName,
143
+ queue: step?.queue,
144
+ workerId: step?.workerId,
145
+ status,
146
+ attempt: stepState?.attempt,
147
+ error: stepState?.error,
148
+ runtime: step?.runtime,
149
+ runtype: step?.runtype,
150
+ emits: step?.emits
151
+ },
152
+ type: "flow-step",
153
+ style: { minWidth: `${nodeWidth}px` }
154
+ });
155
+ });
156
+ y += rows * (rowHeight + verticalGap);
157
+ });
158
+ } else {
159
+ const names = Object.keys(steps);
160
+ const cols = 3;
161
+ names.forEach((name, idx) => {
162
+ const step = steps[name];
163
+ const stepState = states[name];
164
+ const status = mapStatusToNodeStatus(stepState?.status);
165
+ const col = idx % cols;
166
+ const row = Math.floor(idx / cols);
167
+ const totalRows = Math.ceil(names.length / cols);
168
+ const isLastRow = row === totalRows - 1;
169
+ const nodesInThisRow = isLastRow ? names.length % cols || cols : cols;
170
+ const rowWidth = nodesInThisRow * colWidth + (nodesInThisRow - 1) * horizontalGap;
171
+ const rowStartX = -rowWidth / 2;
172
+ const x = rowStartX + col * (colWidth + horizontalGap);
173
+ const yPos = y + row * (rowHeight + verticalGap);
174
+ out.push({
175
+ id: `step:${name}`,
176
+ position: { x, y: yPos },
177
+ data: {
178
+ label: name,
179
+ queue: step?.queue,
180
+ workerId: step?.workerId,
181
+ status,
182
+ attempt: stepState?.attempt,
183
+ error: stepState?.error,
184
+ runtime: step?.runtime,
185
+ runtype: step?.runtype,
186
+ emits: step?.emits
187
+ },
188
+ type: "flow-step",
189
+ style: { minWidth: `${nodeWidth}px` }
190
+ });
191
+ });
192
+ }
193
+ return out;
194
+ });
195
+ function mapStatusToNodeStatus(status) {
196
+ switch (status) {
197
+ case "running":
198
+ case "retrying":
199
+ case "waiting":
200
+ return "running";
201
+ case "completed":
202
+ return "done";
203
+ case "failed":
204
+ case "timeout":
205
+ return "error";
206
+ case "canceled":
207
+ return "canceled";
208
+ default:
209
+ return "idle";
210
+ }
211
+ }
212
+ const edges = computed(() => {
213
+ const f = props.flow;
214
+ if (!f) return [];
215
+ const states = props.stepStates || {};
216
+ const added = /* @__PURE__ */ new Set();
217
+ const out = [];
218
+ function addEdge(source, target, label) {
219
+ const id = `${source}->${target}${label ? `:${label}` : ""}`;
220
+ if (added.has(id)) return;
221
+ const sourceStep = source.split(":")[1];
222
+ const targetStep = target.split(":")[1];
223
+ const sourceState = sourceStep ? states[sourceStep] : void 0;
224
+ const targetState = targetStep ? states[targetStep] : void 0;
225
+ const shouldAnimate = props.flowStatus === "running" && sourceState?.status === "completed" && (targetState?.status === "running" || targetState?.status === "pending" || !targetState);
226
+ added.add(id);
227
+ out.push({ id, source, target, label, animated: shouldAnimate });
228
+ }
229
+ if (f.analyzed?.steps) {
230
+ const analyzedSteps = f.analyzed.steps;
231
+ for (const [stepName, stepInfo] of Object.entries(analyzedSteps)) {
232
+ const target = `step:${stepName}`;
233
+ if (stepInfo.dependsOn.length > 0) {
234
+ for (const depName of stepInfo.dependsOn) {
235
+ const source = depName === f.entry?.step ? `entry:${depName}` : `step:${depName}`;
236
+ addEdge(source, target);
237
+ }
238
+ } else if (f.entry) {
239
+ addEdge(`entry:${f.entry.step}`, target);
240
+ }
241
+ }
242
+ } else {
243
+ console.warn("[FlowDiagram] No analyzed data available for edges");
244
+ }
245
+ return out;
246
+ });
247
+ const internalNodes = ref([]);
248
+ const internalEdges = ref([]);
249
+ function storageKey(flowId2) {
250
+ return flowId2 ? `flow-layout:${flowId2}` : "flow-layout:unknown";
251
+ }
252
+ function applySavedPositions(nodesIn, flowId2) {
253
+ if (!flowId2) return nodesIn;
254
+ try {
255
+ const raw = localStorage.getItem(storageKey(flowId2));
256
+ if (!raw) return nodesIn;
257
+ const saved = JSON.parse(raw);
258
+ const byId = new Map(saved.map((s) => [s.id, s]));
259
+ nodesIn.forEach((n) => {
260
+ const s = byId.get(n.id);
261
+ if (s) n.position = { x: s.x, y: s.y };
262
+ });
263
+ } catch {
264
+ }
265
+ return nodesIn;
266
+ }
267
+ function savePositionsDebounced(flowId2) {
268
+ if (!flowId2) return;
269
+ const payload = internalNodes.value.map((n) => ({ id: n.id, x: n.position.x, y: n.position.y }));
270
+ try {
271
+ localStorage.setItem(storageKey(flowId2), JSON.stringify(payload));
272
+ } catch {
273
+ }
274
+ }
275
+ watch(() => props.flow, (f) => {
276
+ if (!f) {
277
+ internalNodes.value = [];
278
+ internalEdges.value = [];
279
+ return;
280
+ }
281
+ const builtNodes = nodes.value.map((n) => ({ id: n.id, position: { ...n.position }, data: { ...n.data }, type: n.type, style: n.style }));
282
+ const builtEdges = edges.value.map((e) => ({ id: e.id, source: e.source, target: e.target, label: e.label, animated: e.animated }));
283
+ applySavedPositions(builtNodes, f.id);
284
+ internalNodes.value = builtNodes;
285
+ internalEdges.value = builtEdges;
286
+ setTimeout(() => {
287
+ if (vueFlowRef.value) {
288
+ vueFlowRef.value.fitView({ padding: 0.2, duration: 200 });
289
+ }
290
+ }, 100);
291
+ }, { immediate: true, deep: false });
292
+ watch(() => props.stepStates, () => {
293
+ if (!props.flow) return;
294
+ const builtNodes = nodes.value.map((n) => ({ id: n.id, position: { ...n.position }, data: { ...n.data }, type: n.type, style: n.style }));
295
+ const positionMap = new Map(internalNodes.value.map((n) => [n.id, n.position]));
296
+ builtNodes.forEach((n) => {
297
+ const existing = positionMap.get(n.id);
298
+ if (existing) n.position = existing;
299
+ });
300
+ internalNodes.value = builtNodes;
301
+ const builtEdges = edges.value.map((e) => ({ id: e.id, source: e.source, target: e.target, label: e.label, animated: e.animated }));
302
+ internalEdges.value = builtEdges;
303
+ }, { deep: true });
304
+ watch(internalNodes, () => savePositionsDebounced(props.flow?.id), { deep: true });
305
+ function onNodeClick(evt) {
306
+ const id = evt?.node?.id || evt?.id;
307
+ if (id) emit("nodeSelected", { id });
308
+ }
309
+ function onAction(payload) {
310
+ emit("nodeAction", payload);
311
+ }
312
+ function resetLayout() {
313
+ const id = flowId.value;
314
+ if (!id) return;
315
+ try {
316
+ localStorage.removeItem(storageKey(id));
317
+ } catch {
318
+ }
319
+ const freshNodes = nodes.value.map((n) => ({
320
+ id: n.id,
321
+ position: { x: n.position.x, y: n.position.y },
322
+ data: { ...n.data },
323
+ type: n.type,
324
+ style: n.style
325
+ }));
326
+ internalNodes.value = freshNodes;
327
+ nextTick(() => {
328
+ if (vueFlowRef.value) {
329
+ vueFlowRef.value.fitView({
330
+ padding: 0.2,
331
+ includeHiddenNodes: false,
332
+ duration: 300
333
+ });
334
+ }
335
+ });
336
+ }
337
+ </script>
338
+
339
+ <style scoped>
340
+ :deep(.vue-flow__node){background:transparent;border:none;box-shadow:none;display:inline-block;overflow:visible;padding:0}:deep(.vue-flow__node-input){border:none}:deep(.vue-flow__node.selected),:deep(.vue-flow__node:focus){border:none;box-shadow:none;outline:none}
341
+ </style>