@revenexx/studio-integrations 1.3.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 +426 -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/NodeConfigField.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +94 -49
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/NodeInspector.vue +37 -60
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +80 -33
- 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 +165 -56
- 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/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 +5 -8
- package/dist/runtime/composables/useIntegrationsTriggers.js +1 -4
- package/dist/runtime/composables/useIntegrationsWorkflows.js +5 -7
- package/dist/runtime/data/canvasShortcuts.d.ts +208 -0
- package/dist/runtime/data/canvasShortcuts.js +68 -0
- package/dist/runtime/i18n/messages/de.json +30 -16
- package/dist/runtime/i18n/messages/en.json +30 -16
- package/dist/runtime/pages/integrations/templates/[slug].vue +22 -132
- package/dist/runtime/pages/integrations/workflows/[id].vue +128 -33
- package/dist/runtime/utils/apiEnvelope.d.ts +27 -0
- package/dist/runtime/utils/apiEnvelope.js +20 -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/shortcutLabel.d.ts +40 -0
- package/dist/runtime/utils/shortcutLabel.js +48 -0
- package/package.json +1 -1
|
@@ -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 },
|
|
@@ -28,7 +31,8 @@ const props = defineProps({
|
|
|
28
31
|
variables: { type: Object, required: false },
|
|
29
32
|
stateNamespaces: { type: Array, required: false },
|
|
30
33
|
upstreamOutputs: { type: Array, required: false },
|
|
31
|
-
refused: { type: String, required: false }
|
|
34
|
+
refused: { type: String, required: false },
|
|
35
|
+
rowIndex: { type: Number, required: false }
|
|
32
36
|
});
|
|
33
37
|
const emit = defineEmits(["set", "declare-state"]);
|
|
34
38
|
const t = useIntegrationsT();
|
|
@@ -38,6 +42,11 @@ function write(v) {
|
|
|
38
42
|
}
|
|
39
43
|
const exprHint = "${{ \u2026 }}";
|
|
40
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
|
+
});
|
|
41
50
|
function isEmpty(v) {
|
|
42
51
|
return v === void 0 || v === null || v === "" || Array.isArray(v) && v.length === 0;
|
|
43
52
|
}
|
|
@@ -107,12 +116,12 @@ const credOptions = computed(
|
|
|
107
116
|
() => credsForField(props.field).map((c) => ({ credential: c, state: credentialState(c) }))
|
|
108
117
|
);
|
|
109
118
|
const UNKNOWN_CREDENTIAL = computed(() => t("integration.nodeConfig.credential.unknown"));
|
|
110
|
-
|
|
111
|
-
const label =
|
|
119
|
+
const credTriggerLabel = computed(() => {
|
|
120
|
+
const label = controlName.value;
|
|
112
121
|
if (bound.value) return t("integration.nodeConfig.credential.trigger.bound", { label, name: bound.value.credential.name, state: bound.value.state.label });
|
|
113
122
|
if (value.value && credentialsLoaded.value) return t("integration.nodeConfig.credential.trigger.unknown", { label, unknown: UNKNOWN_CREDENTIAL.value });
|
|
114
123
|
return label;
|
|
115
|
-
}
|
|
124
|
+
});
|
|
116
125
|
const credCreateOpen = ref(false);
|
|
117
126
|
const sftpImportOpen = ref(false);
|
|
118
127
|
const CRED_ACTION_NEW = "__action_new_credential__";
|
|
@@ -267,17 +276,36 @@ function writeJson(text) {
|
|
|
267
276
|
function readArray() {
|
|
268
277
|
return Array.isArray(value.value) ? value.value : [];
|
|
269
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 });
|
|
270
285
|
function pushArrayItem(defaultItem) {
|
|
271
286
|
write([...readArray(), defaultItem]);
|
|
272
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
|
+
}
|
|
273
297
|
function removeArrayItem(index) {
|
|
274
298
|
const arr = readArray().slice();
|
|
275
299
|
arr.splice(index, 1);
|
|
300
|
+
rowKeys.value.splice(index, 1);
|
|
276
301
|
write(arr);
|
|
277
302
|
}
|
|
278
303
|
function setArrayItemField(index, fieldKey, v) {
|
|
279
304
|
const arr = readArray().slice();
|
|
280
|
-
|
|
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;
|
|
281
309
|
write(arr);
|
|
282
310
|
}
|
|
283
311
|
</script>
|
|
@@ -351,7 +379,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
351
379
|
|
|
352
380
|
<template v-else-if="isJsonField">
|
|
353
381
|
<Textarea
|
|
354
|
-
:aria-label="
|
|
382
|
+
:aria-label="controlName"
|
|
355
383
|
:model-value="jsonDraft"
|
|
356
384
|
:rows="8"
|
|
357
385
|
spellcheck="false"
|
|
@@ -368,14 +396,14 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
368
396
|
<!-- A repeating row has no room for visible labels, so each control
|
|
369
397
|
names itself and says which row of which field it belongs to. -->
|
|
370
398
|
<Input
|
|
371
|
-
:aria-label="t('integration.nodeConfig.object.row.keyAria', { label:
|
|
399
|
+
:aria-label="t('integration.nodeConfig.object.row.keyAria', { label: controlName, n: idx + 1 })"
|
|
372
400
|
:model-value="row.key"
|
|
373
401
|
:placeholder="t('integration.nodeConfig.object.row.keyPlaceholder')"
|
|
374
402
|
class="h-7 w-[38%] rounded-md border border-black/[0.08] ring-0 shadow-none bg-white text-sm"
|
|
375
403
|
@update:model-value="(v) => setKvRow(idx, { key: String(v) })"
|
|
376
404
|
/>
|
|
377
405
|
<Input
|
|
378
|
-
:aria-label="t('integration.nodeConfig.object.row.valueAria', { label:
|
|
406
|
+
:aria-label="t('integration.nodeConfig.object.row.valueAria', { label: controlName, n: idx + 1 })"
|
|
379
407
|
:model-value="row.value"
|
|
380
408
|
:placeholder="t('integration.nodeConfig.object.row.valuePlaceholder')"
|
|
381
409
|
class="h-7 min-w-0 flex-1 rounded-md border border-black/[0.08] ring-0 shadow-none bg-white text-sm"
|
|
@@ -383,7 +411,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
383
411
|
/>
|
|
384
412
|
<button
|
|
385
413
|
type="button"
|
|
386
|
-
:aria-label="t('integration.nodeConfig.object.row.removeAria', { label:
|
|
414
|
+
:aria-label="t('integration.nodeConfig.object.row.removeAria', { label: controlName, n: idx + 1 })"
|
|
387
415
|
class="flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition hover:text-rose-600"
|
|
388
416
|
@click="removeKvRow(idx)"
|
|
389
417
|
>
|
|
@@ -410,16 +438,22 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
410
438
|
:variables="variables"
|
|
411
439
|
:upstream-outputs="upstreamOutputs"
|
|
412
440
|
:invalid="invalid"
|
|
413
|
-
:label="
|
|
441
|
+
:label="controlName"
|
|
414
442
|
:multiline="isMultiline"
|
|
415
443
|
@update:model-value="(v) => write(v)"
|
|
416
444
|
/>
|
|
417
445
|
|
|
418
446
|
<!-- dynamic options — resolved at author time via config:resolve. Ahead of
|
|
419
447
|
the generic expression branch so a dynamic select with
|
|
420
|
-
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). -->
|
|
421
455
|
<IntegrationsNodeConfigDynamicField
|
|
422
|
-
v-else-if="field.dynamic"
|
|
456
|
+
v-else-if="field.dynamic && !inRow"
|
|
423
457
|
:slug="slug"
|
|
424
458
|
:version="version"
|
|
425
459
|
:field="field"
|
|
@@ -437,7 +471,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
437
471
|
:variables="variables"
|
|
438
472
|
:upstream-outputs="upstreamOutputs"
|
|
439
473
|
:invalid="invalid"
|
|
440
|
-
:label="
|
|
474
|
+
:label="controlName"
|
|
441
475
|
:multiline="isMultiline"
|
|
442
476
|
@update:model-value="(v) => write(v)"
|
|
443
477
|
/>
|
|
@@ -451,7 +485,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
451
485
|
shared control sizes itself to its content and ignores it. -->
|
|
452
486
|
<Textarea
|
|
453
487
|
v-else-if="field.type === 'string' && isMultiline"
|
|
454
|
-
:aria-label="
|
|
488
|
+
:aria-label="controlName"
|
|
455
489
|
:model-value="value ?? ''"
|
|
456
490
|
:placeholder="resolveLocalized(field.placeholder)"
|
|
457
491
|
:rows="4"
|
|
@@ -463,7 +497,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
463
497
|
<!-- plain string (no expressions) -->
|
|
464
498
|
<Input
|
|
465
499
|
v-else-if="field.type === 'string'"
|
|
466
|
-
:aria-label="
|
|
500
|
+
:aria-label="controlName"
|
|
467
501
|
:model-value="value ?? ''"
|
|
468
502
|
:placeholder="resolveLocalized(field.placeholder)"
|
|
469
503
|
class="h-8 rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white"
|
|
@@ -474,7 +508,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
474
508
|
<!-- number -->
|
|
475
509
|
<Input
|
|
476
510
|
v-else-if="field.type === 'number'"
|
|
477
|
-
:aria-label="
|
|
511
|
+
:aria-label="controlName"
|
|
478
512
|
type="number"
|
|
479
513
|
:model-value="value ?? ''"
|
|
480
514
|
class="h-8 rounded-md border border-black/[0.08] ring-0 shadow-none text-sm bg-white"
|
|
@@ -485,7 +519,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
485
519
|
<!-- boolean -->
|
|
486
520
|
<div v-else-if="field.type === 'boolean'" class="flex items-center gap-2 pt-1">
|
|
487
521
|
<Switch
|
|
488
|
-
:aria-label="
|
|
522
|
+
:aria-label="controlName"
|
|
489
523
|
class="data-[state=unchecked]:bg-slate-300"
|
|
490
524
|
:model-value="Boolean(value)"
|
|
491
525
|
@update:model-value="(v) => write(v)"
|
|
@@ -495,9 +529,12 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
495
529
|
</span>
|
|
496
530
|
</div>
|
|
497
531
|
|
|
498
|
-
<!-- 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. -->
|
|
499
536
|
<IntegrationsNodeConfigDynamicSchema
|
|
500
|
-
v-else-if="field.type === 'dynamic-schema'"
|
|
537
|
+
v-else-if="field.type === 'dynamic-schema' && !inRow"
|
|
501
538
|
:slug="slug"
|
|
502
539
|
:version="version"
|
|
503
540
|
:field="field"
|
|
@@ -511,21 +548,21 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
511
548
|
<!-- select -->
|
|
512
549
|
<Select
|
|
513
550
|
v-else-if="field.type === 'select'"
|
|
514
|
-
:model-value="value
|
|
515
|
-
@update:model-value="(v) => write(v)"
|
|
551
|
+
:model-value="toModelValue(value, field.options)"
|
|
552
|
+
@update:model-value="(v) => write(fromModelValue(v))"
|
|
516
553
|
>
|
|
517
|
-
<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">
|
|
518
555
|
<SelectValue :placeholder="resolveLocalized(field.placeholder) || t('integration.nodeConfig.select.placeholder')" />
|
|
519
556
|
</SelectTrigger>
|
|
520
557
|
<SelectContent>
|
|
521
|
-
<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)">
|
|
522
559
|
{{ resolveLocalized(opt.label) }}
|
|
523
560
|
</SelectItem>
|
|
524
561
|
</SelectContent>
|
|
525
562
|
</Select>
|
|
526
563
|
|
|
527
564
|
<!-- multiselect — pill toggles for now -->
|
|
528
|
-
<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">
|
|
529
566
|
<button
|
|
530
567
|
v-for="opt in field.options ?? []"
|
|
531
568
|
:key="String(opt.value)"
|
|
@@ -548,7 +585,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
548
585
|
:model-value="value ?? ''"
|
|
549
586
|
@update:model-value="(v) => write(v)"
|
|
550
587
|
>
|
|
551
|
-
<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">
|
|
552
589
|
<SelectValue :placeholder="t('integration.nodeConfig.secretRef.placeholder')" />
|
|
553
590
|
</SelectTrigger>
|
|
554
591
|
<SelectContent>
|
|
@@ -564,7 +601,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
564
601
|
:model-value="value ?? ''"
|
|
565
602
|
@update:model-value="(v) => onCredentialSelect(v)"
|
|
566
603
|
>
|
|
567
|
-
<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">
|
|
568
605
|
<!-- The trigger renders the bound credential itself instead of letting
|
|
569
606
|
`SelectValue` echo the chosen row. Left to itself it shows that row's
|
|
570
607
|
`textContent`, which glues the name and the state pill into one word —
|
|
@@ -653,7 +690,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
653
690
|
:model-value="value ?? ''"
|
|
654
691
|
@update:model-value="(v) => onStateSelect(v)"
|
|
655
692
|
>
|
|
656
|
-
<SelectTrigger :aria-label="
|
|
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">
|
|
657
694
|
<SelectValue :placeholder="t('integration.nodeConfig.state.placeholder')" />
|
|
658
695
|
</SelectTrigger>
|
|
659
696
|
<SelectContent>
|
|
@@ -687,7 +724,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
687
724
|
<div v-else-if="field.type === 'array' && field.items?.type === 'object'" class="space-y-2">
|
|
688
725
|
<div
|
|
689
726
|
v-for="(item, idx) in readArray()"
|
|
690
|
-
:key="idx"
|
|
727
|
+
:key="rowKeys[idx] ?? idx"
|
|
691
728
|
class="rounded-md border border-black/[0.06] bg-muted/20 p-2 space-y-1.5"
|
|
692
729
|
>
|
|
693
730
|
<div class="flex items-center justify-between">
|
|
@@ -696,39 +733,47 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
696
733
|
</span>
|
|
697
734
|
<button
|
|
698
735
|
type="button"
|
|
699
|
-
:aria-label="t('integration.nodeConfig.array.removeItemAria', { label:
|
|
736
|
+
:aria-label="t('integration.nodeConfig.array.removeItemAria', { label: controlName, n: idx + 1 })"
|
|
700
737
|
class="flex size-5 items-center justify-center rounded text-muted-foreground hover:text-rose-600"
|
|
701
738
|
@click="removeArrayItem(idx)"
|
|
702
739
|
>
|
|
703
740
|
<TrashBin :size="11" />
|
|
704
741
|
</button>
|
|
705
742
|
</div>
|
|
706
|
-
|
|
707
|
-
|
|
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 ?? {})"
|
|
708
755
|
:key="sub.key"
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
/>
|
|
724
|
-
</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
|
+
/>
|
|
725
770
|
</div>
|
|
726
771
|
<Button
|
|
727
772
|
type="button"
|
|
728
773
|
variant="outline"
|
|
729
774
|
size="sm"
|
|
730
775
|
class="h-7 text-sm w-full"
|
|
731
|
-
@click="pushArrayItem(
|
|
776
|
+
@click="pushArrayItem(newRow())"
|
|
732
777
|
>
|
|
733
778
|
<Plus :size="11" />
|
|
734
779
|
{{ t("integration.nodeConfig.action.addRow") }}
|
|
@@ -738,7 +783,7 @@ function setArrayItemField(index, fieldKey, v) {
|
|
|
738
783
|
<!-- fallback: JSON textarea for complex types -->
|
|
739
784
|
<Textarea
|
|
740
785
|
v-else
|
|
741
|
-
:aria-label="
|
|
786
|
+
:aria-label="controlName"
|
|
742
787
|
:model-value="value === void 0 ? '' : JSON.stringify(value, null, 2)"
|
|
743
788
|
rows="3"
|
|
744
789
|
class="rounded-md border border-black/[0.08] ring-0 shadow-none text-sm font-mono bg-white"
|
|
@@ -21,6 +21,16 @@ type __VLS_Props = {
|
|
|
21
21
|
}>;
|
|
22
22
|
/** Why the last save refused this setting, if it did. */
|
|
23
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;
|
|
24
34
|
};
|
|
25
35
|
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
26
36
|
set: (key: string, value: unknown) => any;
|
|
@@ -191,11 +191,16 @@ const applyingConfig = computed(
|
|
|
191
191
|
);
|
|
192
192
|
const parameterFields = computed(() => applyingConfig.value.filter((f) => !SETTINGS_FIELD_KEY.test(f.key)));
|
|
193
193
|
const settingsFields = computed(() => applyingConfig.value.filter((f) => SETTINGS_FIELD_KEY.test(f.key)));
|
|
194
|
-
const
|
|
195
|
-
const
|
|
194
|
+
const FOLD_ABOVE = 4;
|
|
195
|
+
const ALWAYS_DRAWN = 3;
|
|
196
|
+
const foldedFields = computed(
|
|
197
|
+
() => parameterFields.value.length > FOLD_ABOVE ? parameterFields.value.slice(ALWAYS_DRAWN).filter((f) => !f.required) : []
|
|
198
|
+
);
|
|
199
|
+
const foldedKeys = computed(() => new Set(foldedFields.value.map((f) => f.key)));
|
|
200
|
+
const foldAnchorKey = computed(() => foldedFields.value[0]?.key);
|
|
196
201
|
function optionalShouldOpen() {
|
|
197
|
-
if (!
|
|
198
|
-
return
|
|
202
|
+
if (!parameterFields.value.some((f) => f.required)) return true;
|
|
203
|
+
return foldedFields.value.some((f) => props.refusedFields?.[f.key] !== void 0);
|
|
199
204
|
}
|
|
200
205
|
const optionalOpen = ref(optionalShouldOpen());
|
|
201
206
|
watch([() => props.selection?.id, manifest], () => {
|
|
@@ -1805,67 +1810,39 @@ async function runTestStep() {
|
|
|
1805
1810
|
</button>
|
|
1806
1811
|
</div>
|
|
1807
1812
|
</div>
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
:secret-keys="secretKeys"
|
|
1816
|
-
:static-keys="(manifest.config ?? []).map((f2) => f2.key)"
|
|
1817
|
-
:sibling-fields="manifest.config ?? []"
|
|
1818
|
-
:depth="0"
|
|
1819
|
-
:variables="variables"
|
|
1820
|
-
:state-namespaces="offeredStateNamespaces"
|
|
1821
|
-
@declare-state="stageStateNamespace"
|
|
1822
|
-
:upstream-outputs="upstreamOutputs"
|
|
1823
|
-
:refused="refusedFields?.[field.key]"
|
|
1824
|
-
@set="(k, v) => writeConfig(k, v)"
|
|
1825
|
-
/>
|
|
1826
|
-
|
|
1827
|
-
<!-- Optional settings, folded. The count is on the control on
|
|
1828
|
-
purpose: "Show 9 optional settings" is a decision the user can
|
|
1829
|
-
take without opening it, "Show more" is not. -->
|
|
1830
|
-
<template v-if="optionalFields.length">
|
|
1813
|
+
<!-- One list, in the order the node declared it. The fold's
|
|
1814
|
+
control is drawn at the first setting it hides rather than
|
|
1815
|
+
under the form, so opening it inserts the tail where it
|
|
1816
|
+
belongs instead of moving anything. The count is on the
|
|
1817
|
+
control on purpose: "Show 9 optional settings" is a decision
|
|
1818
|
+
the user can take without opening it, "Show more" is not. -->
|
|
1819
|
+
<template v-for="field in parameterFields" :key="field.key">
|
|
1831
1820
|
<button
|
|
1832
|
-
v-if="
|
|
1821
|
+
v-if="field.key === foldAnchorKey"
|
|
1833
1822
|
type="button"
|
|
1834
1823
|
class="flex items-center gap-1.5 rounded px-1 py-1 text-sm text-muted-foreground transition hover:text-foreground"
|
|
1835
|
-
@click="optionalOpen =
|
|
1824
|
+
@click="optionalOpen = !optionalOpen"
|
|
1836
1825
|
>
|
|
1837
|
-
<ChevronRight :size="13" />
|
|
1838
|
-
{{ t("integration.nodeInspector.parameters.optional.show",
|
|
1826
|
+
<component :is="optionalOpen ? ChevronDown : ChevronRight" :size="13" />
|
|
1827
|
+
{{ optionalOpen ? t("integration.nodeInspector.parameters.optional.hide", foldedFields.length) : t("integration.nodeInspector.parameters.optional.show", foldedFields.length) }}
|
|
1839
1828
|
</button>
|
|
1840
|
-
<
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
:secret-keys="secretKeys"
|
|
1858
|
-
:static-keys="(manifest.config ?? []).map((f2) => f2.key)"
|
|
1859
|
-
:sibling-fields="manifest.config ?? []"
|
|
1860
|
-
:depth="0"
|
|
1861
|
-
:variables="variables"
|
|
1862
|
-
:state-namespaces="offeredStateNamespaces"
|
|
1863
|
-
@declare-state="stageStateNamespace"
|
|
1864
|
-
:upstream-outputs="upstreamOutputs"
|
|
1865
|
-
:refused="refusedFields?.[field.key]"
|
|
1866
|
-
@set="(k, v) => writeConfig(k, v)"
|
|
1867
|
-
/>
|
|
1868
|
-
</template>
|
|
1829
|
+
<IntegrationsNodeConfigField
|
|
1830
|
+
v-if="optionalOpen || !foldedKeys.has(field.key)"
|
|
1831
|
+
:field="field"
|
|
1832
|
+
:config="draftNode.config ?? {}"
|
|
1833
|
+
:slug="draftNode.nodeSlug"
|
|
1834
|
+
:version="draftNode.nodeVersion"
|
|
1835
|
+
:secret-keys="secretKeys"
|
|
1836
|
+
:static-keys="(manifest.config ?? []).map((f2) => f2.key)"
|
|
1837
|
+
:sibling-fields="manifest.config ?? []"
|
|
1838
|
+
:depth="0"
|
|
1839
|
+
:variables="variables"
|
|
1840
|
+
:state-namespaces="offeredStateNamespaces"
|
|
1841
|
+
@declare-state="stageStateNamespace"
|
|
1842
|
+
:upstream-outputs="upstreamOutputs"
|
|
1843
|
+
:refused="refusedFields?.[field.key]"
|
|
1844
|
+
@set="(k, v) => writeConfig(k, v)"
|
|
1845
|
+
/>
|
|
1869
1846
|
</template>
|
|
1870
1847
|
|
|
1871
1848
|
<p v-if="!parameterFields.length" class="text-sm text-muted-foreground italic">
|