@revenexx/studio-integrations 1.2.0 → 1.4.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 +581 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/CanvasShortcutsDialog.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/CanvasShortcutsDialog.vue +53 -0
- package/dist/runtime/components/Integrations/CanvasShortcutsDialog.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/EditorTip.vue +2 -15
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -2
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +15 -1
- package/dist/runtime/components/Integrations/NodeConfigField.vue +158 -49
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +15 -1
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/NodeInspector.vue +136 -81
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/NodeVersionMoveDialog.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/NodeVersionMoveDialog.vue +133 -0
- package/dist/runtime/components/Integrations/NodeVersionMoveDialog.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +80 -33
- package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.vue +111 -0
- package/dist/runtime/components/Integrations/StateNamespaceCreateDialog.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +0 -4
- package/dist/runtime/components/Integrations/TemplateCard.vue +3 -6
- package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +0 -4
- package/dist/runtime/components/Integrations/TemplateInstallStep.d.vue.ts +25 -0
- package/dist/runtime/components/Integrations/TemplateInstallStep.vue +198 -0
- package/dist/runtime/components/Integrations/TemplateInstallStep.vue.d.ts +25 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +22 -40
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +2 -2
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +94 -65
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +36 -2
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +200 -54
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +36 -2
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +6 -2
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +4 -106
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +6 -2
- package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.vue +216 -0
- package/dist/runtime/components/Integrations/WorkflowStateNamespacesDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +22 -11
- package/dist/runtime/composables/useCanvasShortcuts.d.ts +34 -0
- package/dist/runtime/composables/useCanvasShortcuts.js +11 -8
- package/dist/runtime/composables/useCredentialTypes.js +3 -9
- package/dist/runtime/composables/useIntegrationsCredentials.js +6 -14
- package/dist/runtime/composables/useIntegrationsNodes.js +2 -2
- package/dist/runtime/composables/useIntegrationsRuns.js +8 -13
- package/dist/runtime/composables/useIntegrationsSecrets.js +5 -5
- package/dist/runtime/composables/useIntegrationsTemplates.js +10 -8
- package/dist/runtime/composables/useIntegrationsTriggers.js +1 -4
- package/dist/runtime/composables/useIntegrationsWorkflows.js +11 -7
- package/dist/runtime/data/canvasShortcuts.d.ts +208 -0
- package/dist/runtime/data/canvasShortcuts.js +68 -0
- package/dist/runtime/data/integrationNodeCatalog.d.ts +25 -0
- package/dist/runtime/data/integrationNodeCatalog.js +8 -0
- package/dist/runtime/data/nodeVersionMove.d.ts +72 -0
- package/dist/runtime/data/nodeVersionMove.js +65 -0
- package/dist/runtime/i18n/messages/de.json +83 -20
- package/dist/runtime/i18n/messages/en.json +83 -20
- package/dist/runtime/pages/integrations/templates/[slug].vue +22 -132
- package/dist/runtime/pages/integrations/workflows/[id].vue +158 -53
- package/dist/runtime/pages/integrations/workflows/index.vue +23 -4
- package/dist/runtime/types/integrations.d.ts +44 -1
- package/dist/runtime/utils/apiEnvelope.d.ts +27 -0
- package/dist/runtime/utils/apiEnvelope.js +20 -0
- package/dist/runtime/utils/buildSettle.d.ts +68 -0
- package/dist/runtime/utils/buildSettle.js +29 -0
- package/dist/runtime/utils/configDefaults.d.ts +18 -0
- package/dist/runtime/utils/configDefaults.js +7 -0
- package/dist/runtime/utils/errorPathAdvice.d.ts +60 -0
- package/dist/runtime/utils/errorPathAdvice.js +19 -0
- package/dist/runtime/utils/notify.d.ts +2 -2
- package/dist/runtime/utils/runEnvelope.d.ts +12 -0
- package/dist/runtime/utils/runEnvelope.js +2 -1
- package/dist/runtime/utils/selectOptions.d.ts +48 -0
- package/dist/runtime/utils/selectOptions.js +19 -0
- package/dist/runtime/utils/settingCondition.d.ts +48 -0
- package/dist/runtime/utils/settingCondition.js +84 -0
- package/dist/runtime/utils/shortcutLabel.d.ts +40 -0
- package/dist/runtime/utils/shortcutLabel.js +48 -0
- package/dist/runtime/utils/stateNamespaceName.d.ts +23 -0
- package/dist/runtime/utils/stateNamespaceName.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
open: boolean;
|
|
3
|
+
};
|
|
4
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
5
|
+
"update:open": (args_0: boolean) => any;
|
|
6
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
7
|
+
"onUpdate:open"?: ((args_0: boolean) => any) | undefined;
|
|
8
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
export default _default;
|
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
3
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@revenexx/studio";
|
|
4
|
+
import { formatShortcut } from "../../utils/shortcutLabel";
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
label: { type: String, required: true },
|
|
6
7
|
shortcut: { type: String, required: false }
|
|
7
8
|
});
|
|
8
|
-
const
|
|
9
|
-
const keys = computed(() => {
|
|
10
|
-
if (!props.shortcut) return "";
|
|
11
|
-
const out = [];
|
|
12
|
-
for (const raw of props.shortcut.split("+")) {
|
|
13
|
-
const t = raw.toLowerCase();
|
|
14
|
-
if (t === "mod") out.push(isMac ? "\u2318" : "Ctrl");
|
|
15
|
-
else if (t === "shift") out.push("\u21E7");
|
|
16
|
-
else if (t === "alt") out.push(isMac ? "\u2325" : "Alt");
|
|
17
|
-
else if (t === "enter") out.push("\u21B5");
|
|
18
|
-
else if (t === "escape") out.push("Esc");
|
|
19
|
-
else out.push(t.toUpperCase());
|
|
20
|
-
}
|
|
21
|
-
return isMac ? out.join("") : out.join("+");
|
|
22
|
-
});
|
|
9
|
+
const keys = computed(() => props.shortcut ? formatShortcut(props.shortcut) : "");
|
|
23
10
|
</script>
|
|
24
11
|
|
|
25
12
|
<template>
|
|
@@ -3,6 +3,7 @@ import { computed } from "vue";
|
|
|
3
3
|
import { Button } from "@revenexx/studio";
|
|
4
4
|
import { IntegrationsApiError } from "../../utils/apiError";
|
|
5
5
|
import { resolveLocalized } from "../../utils/localize";
|
|
6
|
+
import { applyingFields, staleKeys } from "../../utils/settingCondition";
|
|
6
7
|
const props = defineProps({
|
|
7
8
|
slug: { type: String, required: true },
|
|
8
9
|
version: { type: String, required: true },
|
|
@@ -24,6 +25,13 @@ const q = useNodeConfigResolve({
|
|
|
24
25
|
dependsOn: () => props.field.dependsOn
|
|
25
26
|
});
|
|
26
27
|
const resolvedFields = computed(() => q.data.value?.fields ?? []);
|
|
28
|
+
const applyingResolvedFields = computed(() => applyingFields(resolvedFields.value, props.config));
|
|
29
|
+
function writeResolved(key, value) {
|
|
30
|
+
emit("set", key, value);
|
|
31
|
+
const after = { ...props.config, [key]: value };
|
|
32
|
+
for (const stale of staleKeys(resolvedFields.value, key, after))
|
|
33
|
+
emit("set", stale, void 0);
|
|
34
|
+
}
|
|
27
35
|
</script>
|
|
28
36
|
|
|
29
37
|
<template>
|
|
@@ -67,7 +75,7 @@ const resolvedFields = computed(() => q.data.value?.fields ?? []);
|
|
|
67
75
|
<legend class="px-1 text-mono text-muted-foreground">
|
|
68
76
|
{{ resolveLocalized(field.label) }}
|
|
69
77
|
</legend>
|
|
70
|
-
<template v-for="f in
|
|
78
|
+
<template v-for="f in applyingResolvedFields" :key="f.key">
|
|
71
79
|
<!-- collision guard: a resolved key that shadows a static config field is skipped -->
|
|
72
80
|
<p v-if="staticKeys.includes(f.key)" class="text-sm text-amber-600">
|
|
73
81
|
{{ t("integration.nodeConfig.dynamicSchema.fieldCollision", { key: f.key }) }}
|
|
@@ -89,7 +97,7 @@ const resolvedFields = computed(() => q.data.value?.fields ?? []);
|
|
|
89
97
|
:static-keys="staticKeys"
|
|
90
98
|
:sibling-fields="resolvedFields"
|
|
91
99
|
:depth="(depth ?? 0) + 1"
|
|
92
|
-
@set="
|
|
100
|
+
@set="writeResolved"
|
|
93
101
|
/>
|
|
94
102
|
</template>
|
|
95
103
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConfigField, WorkflowVariable } from '../../types/integrations.js';
|
|
1
|
+
import type { ConfigField, WorkflowStateNamespace, WorkflowStateRole, WorkflowStateVisibility, WorkflowVariable } from '../../types/integrations.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
field: ConfigField;
|
|
4
4
|
config: Record<string, unknown>;
|
|
@@ -11,6 +11,8 @@ type __VLS_Props = {
|
|
|
11
11
|
siblingFields?: ConfigField[];
|
|
12
12
|
depth?: number;
|
|
13
13
|
variables?: Record<string, WorkflowVariable>;
|
|
14
|
+
/** The workflow's declared state namespaces — the options a `state-ref` offers. */
|
|
15
|
+
stateNamespaces?: WorkflowStateNamespace[];
|
|
14
16
|
upstreamOutputs?: Array<{
|
|
15
17
|
nodeId: string;
|
|
16
18
|
nodeName?: string;
|
|
@@ -19,10 +21,22 @@ type __VLS_Props = {
|
|
|
19
21
|
}>;
|
|
20
22
|
/** Why the last save refused this setting, if it did. */
|
|
21
23
|
refused?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The 1-based row this setting sits in, when it is a sub-setting of a list
|
|
26
|
+
* row — set only by the list branch below, rendering itself.
|
|
27
|
+
*
|
|
28
|
+
* It is what keeps a repeating control nameable: the visible label repeats
|
|
29
|
+
* per row, so every row's control would otherwise answer to the same name
|
|
30
|
+
* (specs/accessibility.md AC-1). With it, each says which setting AND which
|
|
31
|
+
* row it belongs to.
|
|
32
|
+
*/
|
|
33
|
+
rowIndex?: number;
|
|
22
34
|
};
|
|
23
35
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
24
36
|
set: (key: string, value: unknown) => any;
|
|
37
|
+
"declare-state": (namespace: string, role: WorkflowStateRole, visibility: WorkflowStateVisibility) => any;
|
|
25
38
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
26
39
|
onSet?: ((key: string, value: unknown) => any) | undefined;
|
|
40
|
+
"onDeclare-state"?: ((namespace: string, role: WorkflowStateRole, visibility: WorkflowStateVisibility) => any) | undefined;
|
|
27
41
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
42
|
export default _default;
|
|
@@ -15,7 +15,10 @@ import {
|
|
|
15
15
|
Textarea
|
|
16
16
|
} from "@revenexx/studio";
|
|
17
17
|
import { resolveLocalized } from "../../utils/localize";
|
|
18
|
+
import { fromModelValue, toItemValue, toModelValue } from "../../utils/selectOptions";
|
|
18
19
|
import { credentialState } from "../../utils/credentialState";
|
|
20
|
+
import { defaultsFor } from "../../utils/configDefaults";
|
|
21
|
+
import { applyingFields, settingApplies, staleKeys } from "../../utils/settingCondition";
|
|
19
22
|
const props = defineProps({
|
|
20
23
|
field: { type: Object, required: true },
|
|
21
24
|
config: { type: Object, required: true },
|
|
@@ -26,10 +29,12 @@ const props = defineProps({
|
|
|
26
29
|
siblingFields: { type: Array, required: false },
|
|
27
30
|
depth: { type: Number, required: false },
|
|
28
31
|
variables: { type: Object, required: false },
|
|
32
|
+
stateNamespaces: { type: Array, required: false },
|
|
29
33
|
upstreamOutputs: { type: Array, required: false },
|
|
30
|
-
refused: { type: String, required: false }
|
|
34
|
+
refused: { type: String, required: false },
|
|
35
|
+
rowIndex: { type: Number, required: false }
|
|
31
36
|
});
|
|
32
|
-
const emit = defineEmits(["set"]);
|
|
37
|
+
const emit = defineEmits(["set", "declare-state"]);
|
|
33
38
|
const t = useIntegrationsT();
|
|
34
39
|
const value = computed(() => props.config[props.field.key]);
|
|
35
40
|
function write(v) {
|
|
@@ -37,6 +42,11 @@ function write(v) {
|
|
|
37
42
|
}
|
|
38
43
|
const exprHint = "${{ \u2026 }}";
|
|
39
44
|
const descriptionText = computed(() => resolveLocalized(props.field.description));
|
|
45
|
+
const inRow = computed(() => props.rowIndex !== void 0);
|
|
46
|
+
const controlName = computed(() => {
|
|
47
|
+
const label = resolveLocalized(props.field.label);
|
|
48
|
+
return inRow.value ? t("integration.nodeConfig.array.subFieldAria", { label, n: props.rowIndex }) : label;
|
|
49
|
+
});
|
|
40
50
|
function isEmpty(v) {
|
|
41
51
|
return v === void 0 || v === null || v === "" || Array.isArray(v) && v.length === 0;
|
|
42
52
|
}
|
|
@@ -106,12 +116,12 @@ const credOptions = computed(
|
|
|
106
116
|
() => credsForField(props.field).map((c) => ({ credential: c, state: credentialState(c) }))
|
|
107
117
|
);
|
|
108
118
|
const UNKNOWN_CREDENTIAL = computed(() => t("integration.nodeConfig.credential.unknown"));
|
|
109
|
-
|
|
110
|
-
const label =
|
|
119
|
+
const credTriggerLabel = computed(() => {
|
|
120
|
+
const label = controlName.value;
|
|
111
121
|
if (bound.value) return t("integration.nodeConfig.credential.trigger.bound", { label, name: bound.value.credential.name, state: bound.value.state.label });
|
|
112
122
|
if (value.value && credentialsLoaded.value) return t("integration.nodeConfig.credential.trigger.unknown", { label, unknown: UNKNOWN_CREDENTIAL.value });
|
|
113
123
|
return label;
|
|
114
|
-
}
|
|
124
|
+
});
|
|
115
125
|
const credCreateOpen = ref(false);
|
|
116
126
|
const sftpImportOpen = ref(false);
|
|
117
127
|
const CRED_ACTION_NEW = "__action_new_credential__";
|
|
@@ -127,6 +137,30 @@ function onCredentialSelect(v) {
|
|
|
127
137
|
}
|
|
128
138
|
write(v);
|
|
129
139
|
}
|
|
140
|
+
const stateOptions = computed(() => {
|
|
141
|
+
const role = props.field.stateRole;
|
|
142
|
+
const declared = props.stateNamespaces ?? [];
|
|
143
|
+
const list = role ? declared.filter((n) => n.role === role) : declared;
|
|
144
|
+
const selected = value.value;
|
|
145
|
+
if (typeof selected === "string" && selected && !list.some((n) => n.namespace === selected)) {
|
|
146
|
+
return [{ namespace: selected, role: role ?? "mapping", visibility: void 0 }, ...list];
|
|
147
|
+
}
|
|
148
|
+
return list;
|
|
149
|
+
});
|
|
150
|
+
const stateTakenNames = computed(() => (props.stateNamespaces ?? []).map((n) => n.namespace));
|
|
151
|
+
const stateCreateOpen = ref(false);
|
|
152
|
+
const STATE_ACTION_NEW = "__action_new_state_namespace__";
|
|
153
|
+
function onStateSelect(v) {
|
|
154
|
+
if (v === STATE_ACTION_NEW) {
|
|
155
|
+
stateCreateOpen.value = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
write(v);
|
|
159
|
+
}
|
|
160
|
+
function onStateDeclared(namespace, role, visibility) {
|
|
161
|
+
emit("declare-state", namespace, role, visibility);
|
|
162
|
+
write(namespace);
|
|
163
|
+
}
|
|
130
164
|
function acceptsSftp(field) {
|
|
131
165
|
const t2 = field.credentialType;
|
|
132
166
|
return t2 === "revenexx:sftp" || Array.isArray(t2) && t2.includes("revenexx:sftp");
|
|
@@ -242,17 +276,36 @@ function writeJson(text) {
|
|
|
242
276
|
function readArray() {
|
|
243
277
|
return Array.isArray(value.value) ? value.value : [];
|
|
244
278
|
}
|
|
279
|
+
const rowKeys = ref([]);
|
|
280
|
+
let nextRowKey = 0;
|
|
281
|
+
watch(() => readArray().length, (n) => {
|
|
282
|
+
while (rowKeys.value.length < n) rowKeys.value.push(nextRowKey++);
|
|
283
|
+
if (rowKeys.value.length > n) rowKeys.value.splice(n);
|
|
284
|
+
}, { immediate: true });
|
|
245
285
|
function pushArrayItem(defaultItem) {
|
|
246
286
|
write([...readArray(), defaultItem]);
|
|
247
287
|
}
|
|
288
|
+
function newRow() {
|
|
289
|
+
const declared = props.field.items?.properties ?? [];
|
|
290
|
+
const row = defaultsFor(declared);
|
|
291
|
+
for (; ; ) {
|
|
292
|
+
const withdrawn = declared.filter((f) => f.key in row && !settingApplies(f, row));
|
|
293
|
+
if (!withdrawn.length) return row;
|
|
294
|
+
for (const f of withdrawn) delete row[f.key];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
248
297
|
function removeArrayItem(index) {
|
|
249
298
|
const arr = readArray().slice();
|
|
250
299
|
arr.splice(index, 1);
|
|
300
|
+
rowKeys.value.splice(index, 1);
|
|
251
301
|
write(arr);
|
|
252
302
|
}
|
|
253
303
|
function setArrayItemField(index, fieldKey, v) {
|
|
254
304
|
const arr = readArray().slice();
|
|
255
|
-
|
|
305
|
+
const after = { ...arr[index] ?? {}, [fieldKey]: v };
|
|
306
|
+
for (const key of staleKeys(props.field.items?.properties ?? [], fieldKey, after))
|
|
307
|
+
delete after[key];
|
|
308
|
+
arr[index] = after;
|
|
256
309
|
write(arr);
|
|
257
310
|
}
|
|
258
311
|
</script>
|
|
@@ -326,7 +379,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
326
379
|
|
|
327
380
|
<template v-else-if="isJsonField">
|
|
328
381
|
<Textarea
|
|
329
|
-
:aria-label="
|
|
382
|
+
:aria-label="controlName"
|
|
330
383
|
:model-value="jsonDraft"
|
|
331
384
|
:rows="8"
|
|
332
385
|
spellcheck="false"
|
|
@@ -343,14 +396,14 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
343
396
|
<!-- A repeating row has no room for visible labels, so each control
|
|
344
397
|
names itself and says which row of which field it belongs to. -->
|
|
345
398
|
<Input
|
|
346
|
-
:aria-label="t('integration.nodeConfig.object.row.keyAria', { label:
|
|
399
|
+
:aria-label="t('integration.nodeConfig.object.row.keyAria', { label: controlName, n: idx + 1 })"
|
|
347
400
|
:model-value="row.key"
|
|
348
401
|
:placeholder="t('integration.nodeConfig.object.row.keyPlaceholder')"
|
|
349
402
|
class="h-7 w-[38%] rounded-md border border-black/[0.08] ring-0 shadow-none bg-white text-sm"
|
|
350
403
|
@update:model-value="(v) => setKvRow(idx, { key: String(v) })"
|
|
351
404
|
/>
|
|
352
405
|
<Input
|
|
353
|
-
:aria-label="t('integration.nodeConfig.object.row.valueAria', { label:
|
|
406
|
+
:aria-label="t('integration.nodeConfig.object.row.valueAria', { label: controlName, n: idx + 1 })"
|
|
354
407
|
:model-value="row.value"
|
|
355
408
|
:placeholder="t('integration.nodeConfig.object.row.valuePlaceholder')"
|
|
356
409
|
class="h-7 min-w-0 flex-1 rounded-md border border-black/[0.08] ring-0 shadow-none bg-white text-sm"
|
|
@@ -358,7 +411,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
358
411
|
/>
|
|
359
412
|
<button
|
|
360
413
|
type="button"
|
|
361
|
-
:aria-label="t('integration.nodeConfig.object.row.removeAria', { label:
|
|
414
|
+
:aria-label="t('integration.nodeConfig.object.row.removeAria', { label: controlName, n: idx + 1 })"
|
|
362
415
|
class="flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition hover:text-rose-600"
|
|
363
416
|
@click="removeKvRow(idx)"
|
|
364
417
|
>
|
|
@@ -385,16 +438,22 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
385
438
|
:variables="variables"
|
|
386
439
|
:upstream-outputs="upstreamOutputs"
|
|
387
440
|
:invalid="invalid"
|
|
388
|
-
:label="
|
|
441
|
+
:label="controlName"
|
|
389
442
|
:multiline="isMultiline"
|
|
390
443
|
@update:model-value="(v) => write(v)"
|
|
391
444
|
/>
|
|
392
445
|
|
|
393
446
|
<!-- dynamic options — resolved at author time via config:resolve. Ahead of
|
|
394
447
|
the generic expression branch so a dynamic select with
|
|
395
|
-
expressionAllowed still renders as a dropdown by default.
|
|
448
|
+
expressionAllowed still renders as a dropdown by default.
|
|
449
|
+
|
|
450
|
+
Not inside a list row: the resolve request carries the config the
|
|
451
|
+
setting belongs to, and a row is not that config — the platform would
|
|
452
|
+
be asked about a set of values it has never heard of. No published node
|
|
453
|
+
declares one there; a row's setting is drawn from what it declares
|
|
454
|
+
instead (specs/node-configuration.md, ## Gaps). -->
|
|
396
455
|
<IntegrationsNodeConfigDynamicField
|
|
397
|
-
v-else-if="field.dynamic"
|
|
456
|
+
v-else-if="field.dynamic && !inRow"
|
|
398
457
|
:slug="slug"
|
|
399
458
|
:version="version"
|
|
400
459
|
:field="field"
|
|
@@ -412,7 +471,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
412
471
|
:variables="variables"
|
|
413
472
|
:upstream-outputs="upstreamOutputs"
|
|
414
473
|
:invalid="invalid"
|
|
415
|
-
:label="
|
|
474
|
+
:label="controlName"
|
|
416
475
|
:multiline="isMultiline"
|
|
417
476
|
@update:model-value="(v) => write(v)"
|
|
418
477
|
/>
|
|
@@ -426,7 +485,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
426
485
|
shared control sizes itself to its content and ignores it. -->
|
|
427
486
|
<Textarea
|
|
428
487
|
v-else-if="field.type === 'string' && isMultiline"
|
|
429
|
-
:aria-label="
|
|
488
|
+
:aria-label="controlName"
|
|
430
489
|
:model-value="value ?? ''"
|
|
431
490
|
:placeholder="resolveLocalized(field.placeholder)"
|
|
432
491
|
:rows="4"
|
|
@@ -438,7 +497,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
438
497
|
<!-- plain string (no expressions) -->
|
|
439
498
|
<Input
|
|
440
499
|
v-else-if="field.type === 'string'"
|
|
441
|
-
:aria-label="
|
|
500
|
+
:aria-label="controlName"
|
|
442
501
|
:model-value="value ?? ''"
|
|
443
502
|
:placeholder="resolveLocalized(field.placeholder)"
|
|
444
503
|
class="h-8 rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white"
|
|
@@ -449,7 +508,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
449
508
|
<!-- number -->
|
|
450
509
|
<Input
|
|
451
510
|
v-else-if="field.type === 'number'"
|
|
452
|
-
:aria-label="
|
|
511
|
+
:aria-label="controlName"
|
|
453
512
|
type="number"
|
|
454
513
|
:model-value="value ?? ''"
|
|
455
514
|
class="h-8 rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white"
|
|
@@ -460,7 +519,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
460
519
|
<!-- boolean -->
|
|
461
520
|
<div v-else-if="field.type === 'boolean'" class="flex items-center gap-2 pt-1">
|
|
462
521
|
<Switch
|
|
463
|
-
:aria-label="
|
|
522
|
+
:aria-label="controlName"
|
|
464
523
|
class="data-[state=unchecked]:bg-slate-300"
|
|
465
524
|
:model-value="Boolean(value)"
|
|
466
525
|
@update:model-value="(v) => write(v)"
|
|
@@ -470,9 +529,12 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
470
529
|
</span>
|
|
471
530
|
</div>
|
|
472
531
|
|
|
473
|
-
<!-- dynamic schema — child fields resolved at author time, written into the
|
|
532
|
+
<!-- dynamic schema — child fields resolved at author time, written into the
|
|
533
|
+
flat config. Withheld inside a list row for the same reason as the
|
|
534
|
+
branch above, and one more: the fields it resolves are written into the
|
|
535
|
+
node's flat config, which a row is not. -->
|
|
474
536
|
<IntegrationsNodeConfigDynamicSchema
|
|
475
|
-
v-else-if="field.type === 'dynamic-schema'"
|
|
537
|
+
v-else-if="field.type === 'dynamic-schema' && !inRow"
|
|
476
538
|
:slug="slug"
|
|
477
539
|
:version="version"
|
|
478
540
|
:field="field"
|
|
@@ -486,21 +548,21 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
486
548
|
<!-- select -->
|
|
487
549
|
<Select
|
|
488
550
|
v-else-if="field.type === 'select'"
|
|
489
|
-
:model-value="value
|
|
490
|
-
@update:model-value="(v) => write(v)"
|
|
551
|
+
:model-value="toModelValue(value, field.options)"
|
|
552
|
+
@update:model-value="(v) => write(fromModelValue(v))"
|
|
491
553
|
>
|
|
492
|
-
<SelectTrigger :aria-label="
|
|
554
|
+
<SelectTrigger :aria-label="controlName" size="sm" class="h-8 w-full rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white">
|
|
493
555
|
<SelectValue :placeholder="resolveLocalized(field.placeholder) || t('integration.nodeConfig.select.placeholder')" />
|
|
494
556
|
</SelectTrigger>
|
|
495
557
|
<SelectContent>
|
|
496
|
-
<SelectItem v-for="opt in field.options ?? []" :key="String(opt.value)" :value="
|
|
558
|
+
<SelectItem v-for="opt in field.options ?? []" :key="String(opt.value)" :value="toItemValue(opt.value)">
|
|
497
559
|
{{ resolveLocalized(opt.label) }}
|
|
498
560
|
</SelectItem>
|
|
499
561
|
</SelectContent>
|
|
500
562
|
</Select>
|
|
501
563
|
|
|
502
564
|
<!-- multiselect — pill toggles for now -->
|
|
503
|
-
<div v-else-if="field.type === 'multiselect'" role="group" :aria-label="
|
|
565
|
+
<div v-else-if="field.type === 'multiselect'" role="group" :aria-label="controlName" class="flex flex-wrap gap-1">
|
|
504
566
|
<button
|
|
505
567
|
v-for="opt in field.options ?? []"
|
|
506
568
|
:key="String(opt.value)"
|
|
@@ -523,7 +585,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
523
585
|
:model-value="value ?? ''"
|
|
524
586
|
@update:model-value="(v) => write(v)"
|
|
525
587
|
>
|
|
526
|
-
<SelectTrigger :aria-label="
|
|
588
|
+
<SelectTrigger :aria-label="controlName" size="sm" class="h-8 w-full rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white">
|
|
527
589
|
<SelectValue :placeholder="t('integration.nodeConfig.secretRef.placeholder')" />
|
|
528
590
|
</SelectTrigger>
|
|
529
591
|
<SelectContent>
|
|
@@ -539,7 +601,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
539
601
|
:model-value="value ?? ''"
|
|
540
602
|
@update:model-value="(v) => onCredentialSelect(v)"
|
|
541
603
|
>
|
|
542
|
-
<SelectTrigger :aria-label="credTriggerLabel
|
|
604
|
+
<SelectTrigger :aria-label="credTriggerLabel" size="sm" class="h-8 w-full rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white">
|
|
543
605
|
<!-- The trigger renders the bound credential itself instead of letting
|
|
544
606
|
`SelectValue` echo the chosen row. Left to itself it shows that row's
|
|
545
607
|
`textContent`, which glues the name and the state pill into one word —
|
|
@@ -619,11 +681,50 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
619
681
|
/>
|
|
620
682
|
</template>
|
|
621
683
|
|
|
684
|
+
<!-- state-ref — pick one of the workflow's declared namespaces of this
|
|
685
|
+
field's role. Declaring lives INSIDE the picker, like creating a
|
|
686
|
+
credential does: a picker whose list is empty must not be a dead end
|
|
687
|
+
that sends the author to another dialog and back. -->
|
|
688
|
+
<template v-else-if="field.type === 'state-ref'">
|
|
689
|
+
<Select
|
|
690
|
+
:model-value="value ?? ''"
|
|
691
|
+
@update:model-value="(v) => onStateSelect(v)"
|
|
692
|
+
>
|
|
693
|
+
<SelectTrigger :aria-label="controlName" size="sm" class="h-8 w-full rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white">
|
|
694
|
+
<SelectValue :placeholder="t('integration.nodeConfig.state.placeholder')" />
|
|
695
|
+
</SelectTrigger>
|
|
696
|
+
<SelectContent>
|
|
697
|
+
<SelectItem v-for="o in stateOptions" :key="o.namespace" :value="o.namespace">
|
|
698
|
+
<span class="flex items-center gap-1.5">
|
|
699
|
+
<span class="min-w-0 truncate font-mono">{{ o.namespace }}</span>
|
|
700
|
+
<span
|
|
701
|
+
v-if="o.visibility === 'shared'"
|
|
702
|
+
class="shrink-0 rounded-full border px-1.5 py-px text-[10px] font-medium text-muted-foreground"
|
|
703
|
+
>{{ t("integration.workflows.state.visibility.shared.label") }}</span>
|
|
704
|
+
</span>
|
|
705
|
+
</SelectItem>
|
|
706
|
+
<div v-if="!stateOptions.length" class="px-2 py-1.5 text-sm text-muted-foreground">
|
|
707
|
+
{{ t("integration.nodeConfig.state.empty") }}
|
|
708
|
+
</div>
|
|
709
|
+
<SelectSeparator />
|
|
710
|
+
<SelectItem :value="STATE_ACTION_NEW" class="font-medium text-violet-600 data-[highlighted]:text-violet-700">
|
|
711
|
+
{{ t("integration.nodeConfig.state.action.new") }}
|
|
712
|
+
</SelectItem>
|
|
713
|
+
</SelectContent>
|
|
714
|
+
</Select>
|
|
715
|
+
<IntegrationsStateNamespaceCreateDialog
|
|
716
|
+
v-model:open="stateCreateOpen"
|
|
717
|
+
:role="field.stateRole ?? 'mapping'"
|
|
718
|
+
:taken="stateTakenNames"
|
|
719
|
+
@declared="onStateDeclared"
|
|
720
|
+
/>
|
|
721
|
+
</template>
|
|
722
|
+
|
|
622
723
|
<!-- array of objects (e.g. headers / mappings) -->
|
|
623
724
|
<div v-else-if="field.type === 'array' && field.items?.type === 'object'" class="space-y-2">
|
|
624
725
|
<div
|
|
625
726
|
v-for="(item, idx) in readArray()"
|
|
626
|
-
:key="idx"
|
|
727
|
+
:key="rowKeys[idx] ?? idx"
|
|
627
728
|
class="rounded-md border border-black/[0.06] bg-muted/20 p-2 space-y-1.5"
|
|
628
729
|
>
|
|
629
730
|
<div class="flex items-center justify-between">
|
|
@@ -632,39 +733,47 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
632
733
|
</span>
|
|
633
734
|
<button
|
|
634
735
|
type="button"
|
|
635
|
-
:aria-label="t('integration.nodeConfig.array.removeItemAria', { label:
|
|
736
|
+
:aria-label="t('integration.nodeConfig.array.removeItemAria', { label: controlName, n: idx + 1 })"
|
|
636
737
|
class="flex size-5 items-center justify-center rounded text-muted-foreground hover:text-rose-600"
|
|
637
738
|
@click="removeArrayItem(idx)"
|
|
638
739
|
>
|
|
639
740
|
<TrashBin :size="11" />
|
|
640
741
|
</button>
|
|
641
742
|
</div>
|
|
642
|
-
|
|
643
|
-
|
|
743
|
+
<!-- Each sub-setting is drawn by this same component, one level down,
|
|
744
|
+
the way a dynamic-schema child is: the row is its config, its own
|
|
745
|
+
`key` addresses its value in it, and what it declares — a list of
|
|
746
|
+
options, a switch, a description, a required marker — decides the
|
|
747
|
+
control, instead of every one of them being a text box.
|
|
748
|
+
|
|
749
|
+
`refused` is deliberately not handed down: it says why the LAST
|
|
750
|
+
SAVE refused this setting as a whole, so passing it on would mark
|
|
751
|
+
every sub-control of every row red with a sentence about a
|
|
752
|
+
different value. -->
|
|
753
|
+
<IntegrationsNodeConfigField
|
|
754
|
+
v-for="sub in applyingFields(field.items.properties ?? [], item ?? {})"
|
|
644
755
|
:key="sub.key"
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
/>
|
|
660
|
-
</div>
|
|
756
|
+
:field="sub"
|
|
757
|
+
:config="item"
|
|
758
|
+
:slug="slug"
|
|
759
|
+
:version="version"
|
|
760
|
+
:secret-keys="secretKeys"
|
|
761
|
+
:static-keys="staticKeys"
|
|
762
|
+
:depth="(depth ?? 0) + 1"
|
|
763
|
+
:variables="variables"
|
|
764
|
+
:state-namespaces="stateNamespaces"
|
|
765
|
+
:upstream-outputs="upstreamOutputs"
|
|
766
|
+
:row-index="idx + 1"
|
|
767
|
+
@set="(k, v) => setArrayItemField(idx, k, v)"
|
|
768
|
+
@declare-state="(ns, role, visibility) => emit('declare-state', ns, role, visibility)"
|
|
769
|
+
/>
|
|
661
770
|
</div>
|
|
662
771
|
<Button
|
|
663
772
|
type="button"
|
|
664
773
|
variant="outline"
|
|
665
774
|
size="sm"
|
|
666
775
|
class="h-7 text-sm w-full"
|
|
667
|
-
@click="pushArrayItem(
|
|
776
|
+
@click="pushArrayItem(newRow())"
|
|
668
777
|
>
|
|
669
778
|
<Plus :size="11" />
|
|
670
779
|
{{ t("integration.nodeConfig.action.addRow") }}
|
|
@@ -674,7 +783,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
674
783
|
<!-- fallback: JSON textarea for complex types -->
|
|
675
784
|
<Textarea
|
|
676
785
|
v-else
|
|
677
|
-
:aria-label="
|
|
786
|
+
:aria-label="controlName"
|
|
678
787
|
:model-value="value === void 0 ? '' : JSON.stringify(value, null, 2)"
|
|
679
788
|
rows="3"
|
|
680
789
|
class="rounded-md border border-black/[0.08] ring-0 shadow-none text-sm font-mono bg-white"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConfigField, WorkflowVariable } from '../../types/integrations.js';
|
|
1
|
+
import type { ConfigField, WorkflowStateNamespace, WorkflowStateRole, WorkflowStateVisibility, WorkflowVariable } from '../../types/integrations.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
field: ConfigField;
|
|
4
4
|
config: Record<string, unknown>;
|
|
@@ -11,6 +11,8 @@ type __VLS_Props = {
|
|
|
11
11
|
siblingFields?: ConfigField[];
|
|
12
12
|
depth?: number;
|
|
13
13
|
variables?: Record<string, WorkflowVariable>;
|
|
14
|
+
/** The workflow's declared state namespaces — the options a `state-ref` offers. */
|
|
15
|
+
stateNamespaces?: WorkflowStateNamespace[];
|
|
14
16
|
upstreamOutputs?: Array<{
|
|
15
17
|
nodeId: string;
|
|
16
18
|
nodeName?: string;
|
|
@@ -19,10 +21,22 @@ type __VLS_Props = {
|
|
|
19
21
|
}>;
|
|
20
22
|
/** Why the last save refused this setting, if it did. */
|
|
21
23
|
refused?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The 1-based row this setting sits in, when it is a sub-setting of a list
|
|
26
|
+
* row — set only by the list branch below, rendering itself.
|
|
27
|
+
*
|
|
28
|
+
* It is what keeps a repeating control nameable: the visible label repeats
|
|
29
|
+
* per row, so every row's control would otherwise answer to the same name
|
|
30
|
+
* (specs/accessibility.md AC-1). With it, each says which setting AND which
|
|
31
|
+
* row it belongs to.
|
|
32
|
+
*/
|
|
33
|
+
rowIndex?: number;
|
|
22
34
|
};
|
|
23
35
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
24
36
|
set: (key: string, value: unknown) => any;
|
|
37
|
+
"declare-state": (namespace: string, role: WorkflowStateRole, visibility: WorkflowStateVisibility) => any;
|
|
25
38
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
26
39
|
onSet?: ((key: string, value: unknown) => any) | undefined;
|
|
40
|
+
"onDeclare-state"?: ((namespace: string, role: WorkflowStateRole, visibility: WorkflowStateVisibility) => any) | undefined;
|
|
27
41
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
42
|
export default _default;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { NodeManifestOutput, WorkflowEdge, WorkflowNode, WorkflowTrigger, WorkflowVariable } from '../../types/integrations.js';
|
|
1
|
+
import type { NodeManifestOutput, WorkflowEdge, WorkflowNode, WorkflowStateNamespace, WorkflowStateRole, WorkflowStateVisibility, WorkflowTrigger, WorkflowVariable } from '../../types/integrations.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
/** Either a workflow node OR a trigger. Distinguished by presence of `nodeSlug`. */
|
|
4
4
|
selection: WorkflowNode | WorkflowTrigger | null;
|
|
5
5
|
secretKeys: string[];
|
|
6
6
|
variables?: Record<string, WorkflowVariable>;
|
|
7
|
+
/** The workflow's declared state namespaces — options for `state-ref` fields. */
|
|
8
|
+
stateNamespaces?: WorkflowStateNamespace[];
|
|
7
9
|
upstreamOutputs?: Array<{
|
|
8
10
|
nodeId: string;
|
|
9
11
|
nodeName?: string;
|
|
@@ -31,11 +33,13 @@ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {
|
|
|
31
33
|
update: (next: WorkflowTrigger | WorkflowNode) => any;
|
|
32
34
|
remove: () => any;
|
|
33
35
|
close: () => any;
|
|
36
|
+
"declare-state": (namespace: string, role: WorkflowStateRole, visibility: WorkflowStateVisibility) => any;
|
|
34
37
|
navigate: (id: string) => any;
|
|
35
38
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
36
39
|
onUpdate?: ((next: WorkflowTrigger | WorkflowNode) => any) | undefined;
|
|
37
40
|
onRemove?: (() => any) | undefined;
|
|
38
41
|
onClose?: (() => any) | undefined;
|
|
42
|
+
"onDeclare-state"?: ((namespace: string, role: WorkflowStateRole, visibility: WorkflowStateVisibility) => any) | undefined;
|
|
39
43
|
onNavigate?: ((id: string) => any) | undefined;
|
|
40
44
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
41
45
|
export default _default;
|