@revenexx/studio-integrations 0.7.0 → 1.1.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/CHANGELOG.md +633 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +4 -1
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +8 -2
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +2 -6
- package/dist/runtime/components/Integrations/ExpressionInput.vue +5 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +16 -3
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +1 -0
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +2 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/NodeInspector.vue +135 -10
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +2 -1
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue +34 -8
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +2 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +12 -8
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +9 -6
- package/dist/runtime/components/Integrations/RunsTable.vue +2 -2
- package/dist/runtime/components/Integrations/StickyNoteView.vue +45 -11
- package/dist/runtime/components/Integrations/SurfaceCard.d.vue.ts +50 -0
- package/dist/runtime/components/Integrations/SurfaceCard.vue +55 -0
- package/dist/runtime/components/Integrations/SurfaceCard.vue.d.ts +50 -0
- package/dist/runtime/components/Integrations/TemplateCard.vue +3 -3
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +12 -3
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +5 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +79 -12
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +13 -3
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +95 -29
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +11 -10
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +37 -26
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +4 -5
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +53 -14
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +9 -4
- package/dist/runtime/composables/useIntegrationsCredentials.d.ts +11 -0
- package/dist/runtime/composables/useIntegrationsCredentials.js +2 -2
- package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +9 -2
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +27 -18
- package/dist/runtime/composables/useIntegrationsNodes.js +11 -1
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +30 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +70 -29
- package/dist/runtime/composables/useIntegrationsSchedules.d.ts +7 -14
- package/dist/runtime/composables/useIntegrationsSchedules.js +80 -35
- package/dist/runtime/composables/useIntegrationsTriggers.d.ts +30 -3
- package/dist/runtime/composables/useIntegrationsTriggers.js +18 -7
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +10 -1
- package/dist/runtime/composables/useIntegrationsWorkflows.js +25 -4
- package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
- package/dist/runtime/data/integrationNodeCatalog.d.ts +22 -1
- package/dist/runtime/data/integrationNodeCatalog.js +8 -4
- package/dist/runtime/data/loopBody.d.ts +129 -0
- package/dist/runtime/data/loopBody.js +76 -0
- package/dist/runtime/data/nodeInputData.d.ts +58 -1
- package/dist/runtime/data/nodeInputData.js +30 -5
- package/dist/runtime/data/triggerGlyphs.d.ts +47 -0
- package/dist/runtime/data/triggerGlyphs.js +11 -0
- package/dist/runtime/i18n/messages/de.json +62 -33
- package/dist/runtime/i18n/messages/en.json +32 -3
- package/dist/runtime/pages/integrations/credentials.vue +4 -1
- package/dist/runtime/pages/integrations/index.vue +32 -34
- package/dist/runtime/pages/integrations/runs.vue +22 -10
- package/dist/runtime/pages/integrations/schedules.vue +64 -7
- package/dist/runtime/pages/integrations/templates/[slug].vue +13 -5
- package/dist/runtime/pages/integrations/workflows/[id].vue +193 -104
- package/dist/runtime/types/integrations.d.ts +64 -1
- package/dist/runtime/utils/cockpitSources.d.ts +22 -3
- package/dist/runtime/utils/credentialState.d.ts +6 -1
- package/dist/runtime/utils/credentialState.js +1 -1
- package/dist/runtime/utils/localize.d.ts +34 -0
- package/dist/runtime/utils/localize.js +22 -7
- package/dist/runtime/utils/pollBudget.d.ts +1 -1
- package/dist/runtime/utils/servedPaths.d.ts +22 -0
- package/dist/runtime/utils/servedPaths.js +3 -0
- package/dist/runtime/utils/statusStyle.d.ts +52 -0
- package/dist/runtime/utils/statusStyle.js +26 -0
- package/package.json +1 -1
|
@@ -49,6 +49,7 @@ import WorkflowEdgeView from "./WorkflowEdgeView.vue";
|
|
|
49
49
|
import { layoutWorkflowNodes } from "./workflowLayout";
|
|
50
50
|
import { TRIGGER_TEMPLATES, findManifest } from "../../data/integrationNodeCatalog";
|
|
51
51
|
import { isTerminalManifest, portsForNode } from "../../data/nodeInputData";
|
|
52
|
+
import { computeLoopBodies, iteratorNodeIds, nodeLoopData } from "../../data/loopBody";
|
|
52
53
|
import { portFromHandleId } from "../../data/handleId";
|
|
53
54
|
import { groupFindingsByNode } from "../../utils/validationFindings";
|
|
54
55
|
import { reorderBranchSubtrees } from "../../data/branchReorder";
|
|
@@ -56,6 +57,7 @@ import { findingsByNodeField } from "../../utils/validationFindings";
|
|
|
56
57
|
import { resolveDisplayName, resolveLocalized } from "../../utils/localize";
|
|
57
58
|
const props = defineProps({
|
|
58
59
|
workflow: { type: Object, required: true },
|
|
60
|
+
draft: { type: [Object, null], required: false },
|
|
59
61
|
runBusy: { type: Boolean, required: false },
|
|
60
62
|
runWaiting: { type: Boolean, required: false },
|
|
61
63
|
overlayOpen: { type: Boolean, required: false }
|
|
@@ -63,7 +65,7 @@ const props = defineProps({
|
|
|
63
65
|
const emit = defineEmits(["change", "run", "toggleActive", "openSettings"]);
|
|
64
66
|
const t = useIntegrationsT();
|
|
65
67
|
const { keys: secretKeys } = useIntegrationsSecrets();
|
|
66
|
-
const blob = ref(clone(props.workflow.blob));
|
|
68
|
+
const blob = ref(clone(props.draft ?? props.workflow.blob));
|
|
67
69
|
watch(() => props.workflow.id, () => {
|
|
68
70
|
blob.value = clone(props.workflow.blob);
|
|
69
71
|
syncFromBlob();
|
|
@@ -88,6 +90,29 @@ watch(() => JSON.stringify(invalidNodeFields.value), () => {
|
|
|
88
90
|
if (dragInFlight) return;
|
|
89
91
|
syncFromBlob();
|
|
90
92
|
});
|
|
93
|
+
const selectedLoopBody = computed(() => {
|
|
94
|
+
const selected = new Set(flow.getSelectedNodes.value.map((n) => n.id));
|
|
95
|
+
if (!selected.size) return /* @__PURE__ */ new Set();
|
|
96
|
+
const loops = iteratorNodeIds(blob.value.nodes);
|
|
97
|
+
if (!loops.length) return /* @__PURE__ */ new Set();
|
|
98
|
+
const out = /* @__PURE__ */ new Set();
|
|
99
|
+
for (const body of computeLoopBodies(loops, blob.value.edges)) {
|
|
100
|
+
const touched = selected.has(body.iteratorId) || [...body.nodeIds].some((id) => selected.has(id));
|
|
101
|
+
if (!touched) continue;
|
|
102
|
+
out.add(body.iteratorId);
|
|
103
|
+
for (const id of body.nodeIds) out.add(id);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
});
|
|
107
|
+
provide("workflow-loop-highlight", selectedLoopBody);
|
|
108
|
+
watch(() => iteratorNodeIds(blob.value.nodes).join("|"), () => {
|
|
109
|
+
if (dragInFlight) return;
|
|
110
|
+
syncFromBlob();
|
|
111
|
+
});
|
|
112
|
+
watch(useIntegrationsUiLocale(), () => {
|
|
113
|
+
if (dragInFlight) return;
|
|
114
|
+
syncFromBlob();
|
|
115
|
+
});
|
|
91
116
|
const dynamicOutputNodes = computed(() => blob.value.nodes.filter((n) => {
|
|
92
117
|
const m = findManifest(n.nodeSlug, n.nodeVersion);
|
|
93
118
|
return (m?.outputs ?? []).some((o) => o.resolveOutputs === true);
|
|
@@ -228,6 +253,7 @@ function bornClass(id) {
|
|
|
228
253
|
return bornIds.value.has(id) ? "node-pop-in" : void 0;
|
|
229
254
|
}
|
|
230
255
|
function syncFromBlob() {
|
|
256
|
+
const loops = nodeLoopData(blob.value.nodes, blob.value.edges);
|
|
231
257
|
const stickies = [...blob.value.stickies ?? []].sort((a, b) => stickyArea(b) - stickyArea(a));
|
|
232
258
|
const collapsedBy = collapsedGroupOf();
|
|
233
259
|
const groupNodes = (blob.value.groups ?? []).map((g, i) => {
|
|
@@ -283,7 +309,8 @@ function syncFromBlob() {
|
|
|
283
309
|
data: {
|
|
284
310
|
...n,
|
|
285
311
|
__resolvedOutputs: dynamicOutputs.value[n.id],
|
|
286
|
-
__invalidFields: invalidNodeFields.value[n.id]
|
|
312
|
+
__invalidFields: invalidNodeFields.value[n.id],
|
|
313
|
+
__loop: loops.get(n.id)
|
|
287
314
|
}
|
|
288
315
|
}))
|
|
289
316
|
];
|
|
@@ -469,22 +496,49 @@ flow.onEdgeUpdate(({ edge, connection }) => {
|
|
|
469
496
|
syncFromBlob();
|
|
470
497
|
push();
|
|
471
498
|
});
|
|
472
|
-
|
|
499
|
+
let pendingEdgeRemovals = [];
|
|
500
|
+
let pendingNodeRemovals = [];
|
|
501
|
+
let removalFlushQueued = false;
|
|
502
|
+
function queueRemovalFlush() {
|
|
503
|
+
if (removalFlushQueued) return;
|
|
504
|
+
removalFlushQueued = true;
|
|
505
|
+
void nextTick(flushRemovals);
|
|
506
|
+
}
|
|
507
|
+
function flushRemovals() {
|
|
508
|
+
removalFlushQueued = false;
|
|
509
|
+
const edges = pendingEdgeRemovals;
|
|
510
|
+
const nodes = pendingNodeRemovals;
|
|
511
|
+
pendingEdgeRemovals = [];
|
|
512
|
+
pendingNodeRemovals = [];
|
|
513
|
+
const goneNodes = new Set(nodes);
|
|
473
514
|
let mutated = false;
|
|
515
|
+
for (const c of edges) {
|
|
516
|
+
if (goneNodes.has(c.source) || goneNodes.has(c.target)) continue;
|
|
517
|
+
blob.value.edges = blob.value.edges.filter((e) => e.id !== c.id);
|
|
518
|
+
mutated = true;
|
|
519
|
+
}
|
|
520
|
+
if (nodes.length) {
|
|
521
|
+
dropFromBlob(nodes);
|
|
522
|
+
mutated = true;
|
|
523
|
+
}
|
|
524
|
+
if (mutated) {
|
|
525
|
+
syncFromBlob();
|
|
526
|
+
push();
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
flow.onEdgesChange((changes) => {
|
|
474
530
|
for (const c of changes) {
|
|
475
531
|
if (c.type === "remove") {
|
|
476
|
-
|
|
477
|
-
mutated = true;
|
|
532
|
+
pendingEdgeRemovals.push({ id: c.id, source: c.source, target: c.target });
|
|
478
533
|
}
|
|
479
534
|
}
|
|
480
|
-
if (
|
|
535
|
+
if (pendingEdgeRemovals.length) queueRemovalFlush();
|
|
481
536
|
});
|
|
482
537
|
flow.onNodesChange((changes) => {
|
|
483
538
|
const removed = changes.filter((c) => c.type === "remove").map((c) => c.id);
|
|
484
539
|
if (!removed.length) return;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
});
|
|
540
|
+
pendingNodeRemovals.push(...removed);
|
|
541
|
+
queueRemovalFlush();
|
|
488
542
|
});
|
|
489
543
|
function stripFromGroups(drop) {
|
|
490
544
|
if (!blob.value.groups?.length) return;
|
|
@@ -541,7 +595,7 @@ function addSticky(at) {
|
|
|
541
595
|
syncFromBlob();
|
|
542
596
|
push();
|
|
543
597
|
}
|
|
544
|
-
function
|
|
598
|
+
function dropFromBlob(ids) {
|
|
545
599
|
if (!ids.length) return;
|
|
546
600
|
const drop = new Set(ids);
|
|
547
601
|
stripFromGroups(drop);
|
|
@@ -553,6 +607,10 @@ function removeBlobNodes(ids) {
|
|
|
553
607
|
if (blob.value.triggerPositions?.[id]) delete blob.value.triggerPositions[id];
|
|
554
608
|
}
|
|
555
609
|
if (selectedId.value && drop.has(selectedId.value)) selectedId.value = null;
|
|
610
|
+
}
|
|
611
|
+
function removeBlobNodes(ids) {
|
|
612
|
+
if (!ids.length) return;
|
|
613
|
+
dropFromBlob(ids);
|
|
556
614
|
syncFromBlob();
|
|
557
615
|
push();
|
|
558
616
|
}
|
|
@@ -779,14 +837,14 @@ const selectedUpstreamOutputs = computed(() => {
|
|
|
779
837
|
return blob.value.edges.filter((e) => e.to.nodeId === id).flatMap((e) => {
|
|
780
838
|
const source = blob.value.nodes.find((n) => n.id === e.from.nodeId);
|
|
781
839
|
if (!source) return [];
|
|
782
|
-
const
|
|
840
|
+
const port = portsForNode(source, findManifest(source.nodeSlug, source.nodeVersion), dynamicOutputs.value[source.id]).find((p) => p.name === e.from.output);
|
|
783
841
|
return [{
|
|
784
842
|
nodeId: source.id,
|
|
785
843
|
// A node's own name is text the user typed — never trimmed, never read
|
|
786
844
|
// as JSON. The manifest strings below it get the normalising resolver.
|
|
787
845
|
nodeName: resolveDisplayName(source.name) || source.nodeSlug,
|
|
788
846
|
output: e.from.output,
|
|
789
|
-
fields:
|
|
847
|
+
fields: port?.fields.map((f) => f.name)
|
|
790
848
|
}];
|
|
791
849
|
});
|
|
792
850
|
});
|
|
@@ -1213,6 +1271,14 @@ async function focusPalette() {
|
|
|
1213
1271
|
paletteRef.value?.focusSearch();
|
|
1214
1272
|
}
|
|
1215
1273
|
const lightboxOpen = computed(() => !!selection.value);
|
|
1274
|
+
watch(lightboxOpen, (open) => {
|
|
1275
|
+
if (!open) renameIntent.value = false;
|
|
1276
|
+
});
|
|
1277
|
+
function onConfigOpenAutoFocus(event) {
|
|
1278
|
+
if (renameIntent.value) return;
|
|
1279
|
+
event.preventDefault();
|
|
1280
|
+
event.target?.focus();
|
|
1281
|
+
}
|
|
1216
1282
|
const canvasHasKeyboard = computed(() => !lightboxOpen.value && !variablesOpen.value && !props.overlayOpen);
|
|
1217
1283
|
const singleSelected = computed(() => flow.getSelectedNodes.value.length === 1 ? flow.getSelectedNodes.value[0] : null);
|
|
1218
1284
|
useCanvasShortcuts(() => ({
|
|
@@ -1790,6 +1856,7 @@ const edgeTypes = {
|
|
|
1790
1856
|
form as a strip down the left of an empty workbench. -->
|
|
1791
1857
|
<DialogContent
|
|
1792
1858
|
:show-close-button="false"
|
|
1859
|
+
@open-auto-focus="onConfigOpenAutoFocus"
|
|
1793
1860
|
class="flex max-w-none flex-col gap-0 overflow-hidden rounded-lg p-0 sm:max-w-none"
|
|
1794
1861
|
:class="selectionIsTrigger ? 'h-[min(520px,86vh)] w-[min(600px,94vw)]' : 'h-[min(680px,86vh)] w-[min(1100px,94vw)]'"
|
|
1795
1862
|
>
|
|
@@ -4,6 +4,22 @@ import '@vue-flow/minimap/dist/style.css';
|
|
|
4
4
|
import type { Workflow, WorkflowBlob } from '../../types/integrations.js';
|
|
5
5
|
type __VLS_Props = {
|
|
6
6
|
workflow: Workflow;
|
|
7
|
+
/**
|
|
8
|
+
* Unsaved work the page is still holding for this workflow, read ONCE, at
|
|
9
|
+
* mount (behaviour specs/workflow-save.md).
|
|
10
|
+
*
|
|
11
|
+
* The editor is mounted per tab, so a trip to the Runs tab and back seeded a
|
|
12
|
+
* fresh working copy from the SAVED blob while the page still held the draft:
|
|
13
|
+
* the canvas showed one graph, the header said "Unsaved changes" about
|
|
14
|
+
* another, and Save wrote the one nobody could see. Seeding from the draft is
|
|
15
|
+
* what makes a move inside the workflow keep the work.
|
|
16
|
+
*
|
|
17
|
+
* Deliberately not watched. The page's draft is a copy of what this editor
|
|
18
|
+
* last emitted, so following it would re-seed the canvas — and drop the
|
|
19
|
+
* selection, the undo stack and any open dialog with it — on the user's own
|
|
20
|
+
* keystrokes.
|
|
21
|
+
*/
|
|
22
|
+
draft?: WorkflowBlob | null;
|
|
7
23
|
/** Busy flag for the control-pane Play button (a run is being triggered). */
|
|
8
24
|
runBusy?: boolean;
|
|
9
25
|
/** The run is queued behind a bundle build — busy, but worth saying why. */
|
|
@@ -3,6 +3,12 @@ import '@vue-flow/core/dist/theme-default.css';
|
|
|
3
3
|
import type { Workflow } from '../../types/integrations.js';
|
|
4
4
|
type __VLS_Props = {
|
|
5
5
|
workflow: Workflow;
|
|
6
|
+
/**
|
|
7
|
+
* The editor has unsaved work the page is holding. Nothing here shows it — the
|
|
8
|
+
* canvas behind this tab is not mounted — but a restore is what destroys it, so
|
|
9
|
+
* the question this panel asks has to name it (specs/workflow-save.md).
|
|
10
|
+
*/
|
|
11
|
+
draftPending?: boolean;
|
|
6
12
|
};
|
|
7
13
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
8
14
|
restored: () => any;
|
|
@@ -18,9 +18,11 @@ import GroupFrameView from "./GroupFrameView.vue";
|
|
|
18
18
|
import WorkflowEdgeView from "./WorkflowEdgeView.vue";
|
|
19
19
|
import NodeDynamicOutputsResolver from "./NodeDynamicOutputsResolver.vue";
|
|
20
20
|
import { findManifest } from "../../data/integrationNodeCatalog";
|
|
21
|
+
import { iteratorNodeIds, nodeLoopData } from "../../data/loopBody";
|
|
21
22
|
import { useIntegrationsRevisions } from "../../composables/useIntegrationsRevisions";
|
|
22
23
|
const props = defineProps({
|
|
23
|
-
workflow: { type: Object, required: true }
|
|
24
|
+
workflow: { type: Object, required: true },
|
|
25
|
+
draftPending: { type: Boolean, required: false }
|
|
24
26
|
});
|
|
25
27
|
const emit = defineEmits(["restored"]);
|
|
26
28
|
const revisionsApi = useIntegrationsRevisions();
|
|
@@ -141,6 +143,7 @@ function buildPreview(blob) {
|
|
|
141
143
|
}
|
|
142
144
|
return "default";
|
|
143
145
|
};
|
|
146
|
+
const loops = nodeLoopData(blob.nodes, blob.edges);
|
|
144
147
|
const stickies = [...blob.stickies ?? []].sort((a, b) => stickyArea(b) - stickyArea(a));
|
|
145
148
|
previewNodes.value = [
|
|
146
149
|
...previewGroupNodes(blob, nodePos),
|
|
@@ -166,7 +169,7 @@ function buildPreview(blob) {
|
|
|
166
169
|
type: "workflow",
|
|
167
170
|
position: n.position ?? { x: 240, y: 80 },
|
|
168
171
|
draggable: false,
|
|
169
|
-
data: { ...n, __resolvedOutputs: dynamicOutputs.value[n.id] }
|
|
172
|
+
data: { ...n, __resolvedOutputs: dynamicOutputs.value[n.id], __loop: loops.get(n.id) }
|
|
170
173
|
}))
|
|
171
174
|
];
|
|
172
175
|
previewEdges.value = blob.edges.map((e) => {
|
|
@@ -225,7 +228,8 @@ async function select(revision) {
|
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
async function restoreRevision(rev) {
|
|
228
|
-
|
|
231
|
+
const question = props.draftPending ? t("integration.history.confirm.restoreWithDraft", { revision: rev.revision }) : t("integration.history.confirm.restore", { revision: rev.revision });
|
|
232
|
+
if (!confirm(question)) return;
|
|
229
233
|
restoring.value = rev.revision;
|
|
230
234
|
try {
|
|
231
235
|
await revisionsApi.restore(props.workflow.id, rev.revision);
|
|
@@ -243,6 +247,12 @@ async function restoreRevision(rev) {
|
|
|
243
247
|
watch(() => props.workflow.revision, () => {
|
|
244
248
|
if (selected.value === null) buildPreview(props.workflow.blob);
|
|
245
249
|
});
|
|
250
|
+
watch(() => iteratorNodeIds(previewBlob.value.nodes).join("|"), () => {
|
|
251
|
+
buildPreview(previewBlob.value);
|
|
252
|
+
});
|
|
253
|
+
watch(useIntegrationsUiLocale(), () => {
|
|
254
|
+
buildPreview(previewBlob.value);
|
|
255
|
+
});
|
|
246
256
|
function formatDateTime(iso) {
|
|
247
257
|
if (!iso) return "\u2014";
|
|
248
258
|
try {
|
|
@@ -3,6 +3,12 @@ import '@vue-flow/core/dist/theme-default.css';
|
|
|
3
3
|
import type { Workflow } from '../../types/integrations.js';
|
|
4
4
|
type __VLS_Props = {
|
|
5
5
|
workflow: Workflow;
|
|
6
|
+
/**
|
|
7
|
+
* The editor has unsaved work the page is holding. Nothing here shows it — the
|
|
8
|
+
* canvas behind this tab is not mounted — but a restore is what destroys it, so
|
|
9
|
+
* the question this panel asks has to name it (specs/workflow-save.md).
|
|
10
|
+
*/
|
|
11
|
+
draftPending?: boolean;
|
|
6
12
|
};
|
|
7
13
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
8
14
|
restored: () => any;
|
|
@@ -9,6 +9,19 @@ interface NodeData extends WorkflowNode {
|
|
|
9
9
|
* injected by the editor the same non-persisting way. Read-only canvases
|
|
10
10
|
* (run replay, history preview) never pass them, so nothing is marked there. */
|
|
11
11
|
__invalidFields?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Where this node stands with respect to an iterator's loop body (PO-277),
|
|
14
|
+
* computed per sync from the blob's edges (`data/loopBody.ts`) and injected
|
|
15
|
+
* like the two above. Unlike them it reaches ALL THREE canvases: what repeats
|
|
16
|
+
* is a property of the workflow rather than of editing it, and a run replay is
|
|
17
|
+
* where the repetition is most real.
|
|
18
|
+
*/
|
|
19
|
+
__loop?: {
|
|
20
|
+
/** The iterators whose body this node is in, outermost first, by label. */
|
|
21
|
+
inside?: string[];
|
|
22
|
+
/** Set on an iterator itself: how many nodes hang off its `item` port. */
|
|
23
|
+
bodySize?: number;
|
|
24
|
+
};
|
|
12
25
|
}
|
|
13
26
|
type __VLS_Props = {
|
|
14
27
|
id: string;
|
|
@@ -3,8 +3,8 @@ import { computed, inject, nextTick, ref, watch } from "vue";
|
|
|
3
3
|
import { Handle, Position, useVueFlow } from "@vue-flow/core";
|
|
4
4
|
import { NodeToolbar } from "@vue-flow/node-toolbar";
|
|
5
5
|
import * as Lucide from "lucide-vue-next";
|
|
6
|
-
import { Copy, TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
7
|
-
import { NODE_CATEGORIES, findManifest } from "../../data/integrationNodeCatalog";
|
|
6
|
+
import { Copy, DangerTriangle as AlertTriangle, RefreshCircle as Loader2, Repeat, TrashBinMinimalistic as TrashBin } from "#solar-icons";
|
|
7
|
+
import { NODE_CATEGORIES, findManifest, isVersionExecutable } from "../../data/integrationNodeCatalog";
|
|
8
8
|
import { isTerminalManifest } from "../../data/nodeInputData";
|
|
9
9
|
import { resolveDisplayName, resolveLocalized } from "../../utils/localize";
|
|
10
10
|
const props = defineProps({
|
|
@@ -20,9 +20,14 @@ const category = computed(() => NODE_CATEGORIES.find((c) => c.key === manifest.v
|
|
|
20
20
|
const title = computed(
|
|
21
21
|
() => resolveDisplayName(props.data.name) || resolveLocalized(manifest.value?.name) || props.data.nodeSlug
|
|
22
22
|
);
|
|
23
|
-
const inputRequired = computed(() => manifest.value?.input?.required !== false);
|
|
24
23
|
const invalidFields = computed(() => props.data.__invalidFields ?? []);
|
|
25
24
|
const invalidLabel = computed(() => t("integration.canvas.node.badge.invalidConfig", invalidFields.value.length, { fields: invalidFields.value.join(", ") }));
|
|
25
|
+
const insideLoops = computed(() => props.data.__loop?.inside ?? []);
|
|
26
|
+
const repeatsLabel = computed(() => t("integration.canvas.node.badge.repeats", insideLoops.value.length, { iterators: insideLoops.value.join(", ") }));
|
|
27
|
+
const bodySize = computed(() => props.data.__loop?.bodySize);
|
|
28
|
+
const bodySizeLabel = computed(() => t("integration.canvas.node.badge.loopBody", bodySize.value ?? 0));
|
|
29
|
+
const loopHighlight = inject("workflow-loop-highlight", null);
|
|
30
|
+
const inSelectedLoop = computed(() => !!loopHighlight?.value.has(props.id));
|
|
26
31
|
const notes = computed(() => (props.data.notes ?? "").trim());
|
|
27
32
|
const noteOpen = ref(false);
|
|
28
33
|
const noteReady = ref(false);
|
|
@@ -31,11 +36,15 @@ const notePlacement = ref(Position.Bottom);
|
|
|
31
36
|
const noteMaxHeight = ref(0);
|
|
32
37
|
let noteTimer = null;
|
|
33
38
|
const notePanelId = computed(() => `node-note-${props.id}`);
|
|
39
|
+
function handleTitle(p) {
|
|
40
|
+
return p.hint ? p.label + " \u2014 " + p.hint : p.label;
|
|
41
|
+
}
|
|
34
42
|
function portsFromOutputs(list) {
|
|
35
43
|
const ports = [];
|
|
36
44
|
for (const o of list) {
|
|
37
45
|
if (o.name) {
|
|
38
|
-
|
|
46
|
+
const hint = resolveLocalized(o.description);
|
|
47
|
+
ports.push({ name: o.name, kind: o.kind, label: resolveLocalized(o.label) || o.name, ...hint ? { hint } : {} });
|
|
39
48
|
} else if (o.sourceFromConfig) {
|
|
40
49
|
const raw = props.data.config?.[o.sourceFromConfig];
|
|
41
50
|
const values = Array.isArray(raw) ? raw : [];
|
|
@@ -160,6 +169,8 @@ function releaseNote() {
|
|
|
160
169
|
}, 250);
|
|
161
170
|
}
|
|
162
171
|
const nodeActions = inject("workflow-node-actions", null);
|
|
172
|
+
const unbuildableVersion = computed(() => nodeActions !== null && !isVersionExecutable(props.data.nodeSlug, props.data.nodeVersion));
|
|
173
|
+
const unbuildableLabel = computed(() => t("integration.canvas.node.badge.noBuiltCode", { version: props.data.nodeVersion }));
|
|
163
174
|
const canConfigure = computed(() => typeof nodeActions?.configure === "function");
|
|
164
175
|
function configure() {
|
|
165
176
|
nodeActions?.configure?.(props.id);
|
|
@@ -226,17 +237,15 @@ function remove() {
|
|
|
226
237
|
|
|
227
238
|
<!-- Input: ONE logical input, attachable from the left or the top — both
|
|
228
239
|
dots feed the same `__in` port. Clearly visible, ring + shadow. -->
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
/>
|
|
239
|
-
</template>
|
|
240
|
+
<Handle
|
|
241
|
+
v-for="side in IN_SIDES"
|
|
242
|
+
:id="`__in__${side}`"
|
|
243
|
+
:key="`in-${side}`"
|
|
244
|
+
type="target"
|
|
245
|
+
:position="sideToPosition(side)"
|
|
246
|
+
:title="t('integration.canvas.node.handle.input')"
|
|
247
|
+
class="!size-3 !rounded-full !border-2 !border-white !bg-slate-500 !shadow-sm transition-all hover:!size-3.5 hover:!bg-slate-700"
|
|
248
|
+
/>
|
|
240
249
|
|
|
241
250
|
<!-- Output handles: right + bottom, distributed per side. Color encodes the
|
|
242
251
|
port semantics (true/false/error/branch/default), matching the edges. -->
|
|
@@ -247,7 +256,7 @@ function remove() {
|
|
|
247
256
|
:key="`${p.name}-${side}`"
|
|
248
257
|
type="source"
|
|
249
258
|
:position="sideToPosition(side)"
|
|
250
|
-
:title="p
|
|
259
|
+
:title="handleTitle(p)"
|
|
251
260
|
:style="distributedStyle(side, i, outputs.length)"
|
|
252
261
|
class="!size-3 !rounded-full !border-2 !border-white !shadow-sm transition-all hover:!size-3.5"
|
|
253
262
|
:class="portTone(p).handle"
|
|
@@ -261,23 +270,76 @@ function remove() {
|
|
|
261
270
|
@mouseenter="onHoverEnter"
|
|
262
271
|
@mouseleave="onHoverLeave"
|
|
263
272
|
:class="[
|
|
264
|
-
selected ? 'border-emerald-500/50 shadow-[0_0_0_2px_rgba(16,185,129,0.18)]' : invalidFields.length ? 'border-amber-500/70 shadow-[0_0_0_2px_rgba(245,158,11,0.22)] hover:shadow-md' : 'border-black/[0.08] shadow-sm hover:shadow-md hover:border-black/[0.16]',
|
|
273
|
+
selected ? 'border-emerald-500/50 shadow-[0_0_0_2px_rgba(16,185,129,0.18)]' : invalidFields.length ? 'border-amber-500/70 shadow-[0_0_0_2px_rgba(245,158,11,0.22)] hover:shadow-md' : inSelectedLoop ? 'border-slate-400/80 shadow-[0_0_0_2px_rgba(100,116,139,0.20)]' : 'border-black/[0.08] shadow-sm hover:shadow-md hover:border-black/[0.16]',
|
|
265
274
|
showPortLabels ? 'pb-3.5' : ''
|
|
266
275
|
]"
|
|
267
276
|
>
|
|
268
|
-
<!--
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
277
|
+
<!-- The card's marks, in one row at the corner the input handles leave free.
|
|
278
|
+
A row rather than two absolutely-placed badges: the amber one's width
|
|
279
|
+
follows its count, so any fixed offset for a second badge overlaps it as
|
|
280
|
+
soon as the count reaches two digits. Hoverable on purpose — a symbol or
|
|
281
|
+
a bare count at a corner is a riddle, so each carries its own title;
|
|
282
|
+
`nodrag` keeps that hover from starting a card drag. -->
|
|
272
283
|
<span
|
|
273
|
-
v-if="invalidFields.length"
|
|
274
|
-
|
|
275
|
-
:aria-label="invalidLabel"
|
|
276
|
-
:title="invalidLabel"
|
|
277
|
-
class="nodrag absolute -left-2 -top-2 z-10 flex cursor-help items-center gap-0.5 rounded-full bg-amber-500 px-1.5 py-0.5 text-[9px] font-bold leading-none text-white shadow-sm"
|
|
284
|
+
v-if="invalidFields.length || unbuildableVersion || insideLoops.length || bodySize !== void 0"
|
|
285
|
+
class="nodrag absolute -left-2 -top-2 z-10 flex items-center gap-1"
|
|
278
286
|
>
|
|
279
|
-
<
|
|
280
|
-
|
|
287
|
+
<span
|
|
288
|
+
v-if="invalidFields.length"
|
|
289
|
+
role="img"
|
|
290
|
+
:aria-label="invalidLabel"
|
|
291
|
+
:title="invalidLabel"
|
|
292
|
+
class="flex cursor-help items-center gap-0.5 rounded-full bg-amber-500 px-1.5 py-0.5 text-[9px] font-bold leading-none text-white shadow-sm"
|
|
293
|
+
>
|
|
294
|
+
<!-- `weight="Bold"` for the same reason the Lucide draw carried
|
|
295
|
+
`stroke-width="2.5"`: 9px of white on amber. Solar's default
|
|
296
|
+
BoldDuotone washes out at that size against the pill. -->
|
|
297
|
+
<AlertTriangle :size="9" weight="Bold" />
|
|
298
|
+
{{ invalidFields.length }}
|
|
299
|
+
</span>
|
|
300
|
+
<!-- The pinned version cannot be run. Rose rather than amber, and no
|
|
301
|
+
count: amber on this card means unfinished work of the user's own —
|
|
302
|
+
settings still to fill in — while this is the platform missing a piece,
|
|
303
|
+
which the user cannot fix by typing. Hence the stronger colour despite
|
|
304
|
+
being the one that does NOT block activation. -->
|
|
305
|
+
<span
|
|
306
|
+
v-if="unbuildableVersion"
|
|
307
|
+
role="img"
|
|
308
|
+
:aria-label="unbuildableLabel"
|
|
309
|
+
:title="unbuildableLabel"
|
|
310
|
+
class="flex cursor-help items-center rounded-full bg-rose-600 px-1.5 py-0.5 text-[9px] font-bold leading-none text-white shadow-sm"
|
|
311
|
+
>
|
|
312
|
+
<AlertTriangle :size="9" weight="Bold" />
|
|
313
|
+
</span>
|
|
314
|
+
<!-- Repetition (PO-277). Slate, not amber or rose: a loop is how the
|
|
315
|
+
workflow is built, not something wrong with it. The glyph is the
|
|
316
|
+
mark; the count next to it is the depth, printed from 2 up because
|
|
317
|
+
"repeats" is the news at one loop and "repeats twice over" is the
|
|
318
|
+
news above it. Both marks carry their sentence as their accessible
|
|
319
|
+
name — a bare arrow at a corner is a riddle. -->
|
|
320
|
+
<span
|
|
321
|
+
v-if="insideLoops.length"
|
|
322
|
+
role="img"
|
|
323
|
+
:aria-label="repeatsLabel"
|
|
324
|
+
:title="repeatsLabel"
|
|
325
|
+
class="flex cursor-help items-center gap-0.5 rounded-full bg-slate-600 px-1.5 py-0.5 text-[9px] font-bold leading-none text-white shadow-sm"
|
|
326
|
+
>
|
|
327
|
+
<Repeat :size="9" weight="Bold" />
|
|
328
|
+
<template v-if="insideLoops.length > 1">{{ insideLoops.length }}</template>
|
|
329
|
+
</span>
|
|
330
|
+
<!-- The loop itself, and how much of the canvas it repeats. Outlined
|
|
331
|
+
rather than filled: it names the same fact from the other end, and two
|
|
332
|
+
solid slate pills side by side on a nested iterator would read as one. -->
|
|
333
|
+
<span
|
|
334
|
+
v-if="bodySize !== void 0"
|
|
335
|
+
role="img"
|
|
336
|
+
:aria-label="bodySizeLabel"
|
|
337
|
+
:title="bodySizeLabel"
|
|
338
|
+
class="flex cursor-help items-center gap-0.5 rounded-full border border-slate-300 bg-white px-1.5 py-0.5 text-[9px] font-bold leading-none text-slate-600 shadow-sm"
|
|
339
|
+
>
|
|
340
|
+
<Repeat :size="9" weight="Bold" />
|
|
341
|
+
{{ bodySize }}
|
|
342
|
+
</span>
|
|
281
343
|
</span>
|
|
282
344
|
<!-- Always-visible edit affordance: single click opens the config lightbox.
|
|
283
345
|
(Double-click on the whole card works too.) Hidden in read-only hosts
|
|
@@ -330,7 +392,11 @@ function remove() {
|
|
|
330
392
|
v-if="resolvePending"
|
|
331
393
|
class="pointer-events-none absolute bottom-1 left-1/2 flex -translate-x-1/2 select-none items-center gap-1 whitespace-nowrap text-[9px] font-semibold uppercase tracking-wide text-muted-foreground"
|
|
332
394
|
>
|
|
333
|
-
|
|
395
|
+
<!-- `weight="Bold"` for the badge's reason one size down: the default
|
|
396
|
+
BoldDuotone draws a filled disc across the whole viewBox, and a disc
|
|
397
|
+
turning looks like a disc standing still. Bold knocks the arrows out
|
|
398
|
+
of the ring, so 9px of it still reads as spinning. -->
|
|
399
|
+
<Loader2 :size="9" weight="Bold" class="animate-spin" />
|
|
334
400
|
{{ t("integration.canvas.node.port.resolving") }}
|
|
335
401
|
</span>
|
|
336
402
|
<template v-else>
|
|
@@ -9,6 +9,19 @@ interface NodeData extends WorkflowNode {
|
|
|
9
9
|
* injected by the editor the same non-persisting way. Read-only canvases
|
|
10
10
|
* (run replay, history preview) never pass them, so nothing is marked there. */
|
|
11
11
|
__invalidFields?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Where this node stands with respect to an iterator's loop body (PO-277),
|
|
14
|
+
* computed per sync from the blob's edges (`data/loopBody.ts`) and injected
|
|
15
|
+
* like the two above. Unlike them it reaches ALL THREE canvases: what repeats
|
|
16
|
+
* is a property of the workflow rather than of editing it, and a run replay is
|
|
17
|
+
* where the repetition is most real.
|
|
18
|
+
*/
|
|
19
|
+
__loop?: {
|
|
20
|
+
/** The iterators whose body this node is in, outermost first, by label. */
|
|
21
|
+
inside?: string[];
|
|
22
|
+
/** Set on an iterator itself: how many nodes hang off its `item` port. */
|
|
23
|
+
bodySize?: number;
|
|
24
|
+
};
|
|
12
25
|
}
|
|
13
26
|
type __VLS_Props = {
|
|
14
27
|
id: string;
|
|
@@ -11,9 +11,7 @@ import {
|
|
|
11
11
|
AltArrowRight as Next,
|
|
12
12
|
Play,
|
|
13
13
|
Pause,
|
|
14
|
-
RefreshCircle as Loader2
|
|
15
|
-
ClockCircle as Clock,
|
|
16
|
-
LinkRound as Webhook
|
|
14
|
+
RefreshCircle as Loader2
|
|
17
15
|
} from "#solar-icons";
|
|
18
16
|
import { Badge, Button } from "@revenexx/studio";
|
|
19
17
|
import { toast } from "vue-sonner";
|
|
@@ -26,8 +24,10 @@ import StickyNoteView from "./StickyNoteView.vue";
|
|
|
26
24
|
import GroupFrameView from "./GroupFrameView.vue";
|
|
27
25
|
import WorkflowEdgeView from "./WorkflowEdgeView.vue";
|
|
28
26
|
import { findManifest } from "../../data/integrationNodeCatalog";
|
|
27
|
+
import { nodeLoopData } from "../../data/loopBody";
|
|
28
|
+
import { triggerGlyph } from "../../data/triggerGlyphs";
|
|
29
29
|
import { SAFETY_POLL_MS } from "../../utils/pollBudget";
|
|
30
|
-
import {
|
|
30
|
+
import { runStatusClass, runStatusLabel } from "../../utils/statusStyle";
|
|
31
31
|
const props = defineProps({
|
|
32
32
|
workflow: { type: Object, required: true },
|
|
33
33
|
initialRunId: { type: [String, null], required: false },
|
|
@@ -237,6 +237,7 @@ function replayNodeClass(id, type) {
|
|
|
237
237
|
const active = activeStep.value?.node_id === id ? " replay-node-active" : "";
|
|
238
238
|
return `${tone}${reached ? "" : " replay-unreached"}${active}`;
|
|
239
239
|
}
|
|
240
|
+
const loops = computed(() => nodeLoopData(props.workflow.blob.nodes, props.workflow.blob.edges));
|
|
240
241
|
const vfNodes = computed(() => {
|
|
241
242
|
const blob = props.workflow.blob;
|
|
242
243
|
const triggerPos = (id, i) => blob.triggerPositions?.[id] ?? { x: -40, y: 60 + i * 140 };
|
|
@@ -275,7 +276,7 @@ const vfNodes = computed(() => {
|
|
|
275
276
|
position: n.position ?? { x: 240, y: 80 },
|
|
276
277
|
draggable: false,
|
|
277
278
|
class: replayNodeClass(n.id, "workflow"),
|
|
278
|
-
data: { ...n, __resolvedOutputs: dynamicOutputs.value[n.id] }
|
|
279
|
+
data: { ...n, __resolvedOutputs: dynamicOutputs.value[n.id], __loop: loops.value.get(n.id) }
|
|
279
280
|
}))
|
|
280
281
|
];
|
|
281
282
|
});
|
|
@@ -408,7 +409,7 @@ function pretty(v) {
|
|
|
408
409
|
}
|
|
409
410
|
}
|
|
410
411
|
function triggerIcon(type) {
|
|
411
|
-
return type
|
|
412
|
+
return triggerGlyph(type);
|
|
412
413
|
}
|
|
413
414
|
const activeStepTitle = computed(() => {
|
|
414
415
|
const s = activeStep.value;
|
|
@@ -472,7 +473,7 @@ watch(() => props.initialRunId, (id) => {
|
|
|
472
473
|
<div class="absolute top-3 left-3 z-10 flex items-center gap-2 rounded-lg border border-black/[0.08] bg-white/95 px-2.5 py-1.5 text-sm shadow-sm backdrop-blur-sm">
|
|
473
474
|
<Play :size="13" class="text-muted-foreground" />
|
|
474
475
|
<span class="font-medium">{{ selectedRunId ? t("integration.replay.canvas.runLabel", { id: selectedRunId }) : t("integration.replay.canvas.pickRun") }}</span>
|
|
475
|
-
<Badge v-if="runStatus" variant="outline" class="border text-xs font-medium uppercase" :class="
|
|
476
|
+
<Badge v-if="runStatus" variant="outline" class="border text-xs font-medium uppercase" :class="runStatusClass(runStatus)">
|
|
476
477
|
{{ runStatusLabel(runStatus) }}
|
|
477
478
|
</Badge>
|
|
478
479
|
<Badge v-if="stepsSource" variant="outline" class="border text-xs font-medium uppercase text-muted-foreground">
|
|
@@ -536,7 +537,7 @@ watch(() => props.initialRunId, (id) => {
|
|
|
536
537
|
<template v-else>{{ t("integration.replay.debug.noStepSelected") }}</template>
|
|
537
538
|
</div>
|
|
538
539
|
</div>
|
|
539
|
-
<Badge v-if="activeStep" variant="outline" class="shrink-0 border text-xs font-medium uppercase" :class="
|
|
540
|
+
<Badge v-if="activeStep" variant="outline" class="shrink-0 border text-xs font-medium uppercase" :class="runStatusClass(activeStep.status)">
|
|
540
541
|
{{ runStatusLabel(activeStep.status) }}
|
|
541
542
|
</Badge>
|
|
542
543
|
</div>
|
|
@@ -614,8 +615,8 @@ watch(() => props.initialRunId, (id) => {
|
|
|
614
615
|
<component :is="triggerIcon(r.triggerType)" :size="12" class="shrink-0 text-muted-foreground" />
|
|
615
616
|
<span class="truncate">#{{ r.id }}</span>
|
|
616
617
|
</span>
|
|
617
|
-
<Badge variant="outline" class="shrink-0 border text-xs font-medium uppercase" :class="
|
|
618
|
-
{{ r.status }}
|
|
618
|
+
<Badge variant="outline" class="shrink-0 border text-xs font-medium uppercase" :class="runStatusClass(r.status)">
|
|
619
|
+
{{ runStatusLabel(r.status) }}
|
|
619
620
|
</Badge>
|
|
620
621
|
</div>
|
|
621
622
|
<div class="mt-0.5 flex items-center justify-between gap-2 text-sm text-muted-foreground">
|
|
@@ -4,33 +4,32 @@ export interface ExecutionModeOption {
|
|
|
4
4
|
label: string;
|
|
5
5
|
hint: string;
|
|
6
6
|
}
|
|
7
|
+
/** What this dialog edits — the values as they stand, saved or not. */
|
|
8
|
+
export interface WorkflowSettingsValues {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
tags: string[];
|
|
12
|
+
icon: WorkflowIcon | null;
|
|
13
|
+
executionMode: string;
|
|
14
|
+
}
|
|
7
15
|
type __VLS_Props = {
|
|
8
16
|
open: boolean;
|
|
9
17
|
workflow: Workflow;
|
|
10
18
|
modes: ExecutionModeOption[];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The values to edit: the editor's unsaved ones where it has any, the saved ones
|
|
21
|
+
* otherwise. Reopening the dialog therefore shows what the operator last entered
|
|
22
|
+
* rather than what the platform still holds.
|
|
23
|
+
*/
|
|
24
|
+
current: WorkflowSettingsValues;
|
|
14
25
|
/** Tag suggestions (tags already used across the tenant's workflows). */
|
|
15
26
|
tagSuggestions?: string[];
|
|
16
27
|
};
|
|
17
28
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
18
29
|
"update:open": (args_0: boolean) => any;
|
|
19
|
-
|
|
20
|
-
"save-meta": (meta: {
|
|
21
|
-
name: string;
|
|
22
|
-
description: string;
|
|
23
|
-
tags: string[];
|
|
24
|
-
icon: WorkflowIcon | null;
|
|
25
|
-
}) => any;
|
|
30
|
+
apply: (values: WorkflowSettingsValues) => any;
|
|
26
31
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
27
32
|
"onUpdate:open"?: ((args_0: boolean) => any) | undefined;
|
|
28
|
-
|
|
29
|
-
"onSave-meta"?: ((meta: {
|
|
30
|
-
name: string;
|
|
31
|
-
description: string;
|
|
32
|
-
tags: string[];
|
|
33
|
-
icon: WorkflowIcon | null;
|
|
34
|
-
}) => any) | undefined;
|
|
33
|
+
onApply?: ((values: WorkflowSettingsValues) => any) | undefined;
|
|
35
34
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
36
35
|
export default _default;
|