@revenexx/studio-integrations 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +643 -0
- package/README.md +31 -0
- package/dist/module.d.mts +17 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +12 -3
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +32 -28
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +45 -38
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +58 -58
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +10 -9
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +4 -3
- package/dist/runtime/components/Integrations/ExpressionInput.vue +21 -15
- package/dist/runtime/components/Integrations/GroupFrameView.vue +29 -19
- package/dist/runtime/components/Integrations/Layout.vue +28 -3
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +30 -16
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +10 -8
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +3 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +45 -42
- 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 +315 -221
- 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 +37 -10
- package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +2 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +40 -35
- package/dist/runtime/components/Integrations/QueryBuilder.vue +44 -43
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +36 -12
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +14 -13
- package/dist/runtime/components/Integrations/RunResponseModal.vue +11 -10
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +0 -1
- package/dist/runtime/components/Integrations/RunsTable.vue +38 -16
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +0 -1
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +8 -7
- package/dist/runtime/components/Integrations/Sidebar.vue +14 -10
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +14 -4
- package/dist/runtime/components/Integrations/StickyNoteView.vue +82 -34
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
- 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 +18 -8
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +22 -12
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +41 -39
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +6 -4
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +131 -87
- 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 +27 -24
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +6 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +7 -6
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +76 -46
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +55 -43
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +55 -43
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +16 -17
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +9 -8
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -13
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +79 -39
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +83 -45
- package/dist/runtime/composables/integrationsApi.js +4 -2
- package/dist/runtime/composables/useCredentialTypes.js +4 -2
- package/dist/runtime/composables/useIntegrationsCredentials.d.ts +11 -0
- package/dist/runtime/composables/useIntegrationsCredentials.js +8 -6
- package/dist/runtime/composables/useIntegrationsFocus.d.ts +12 -0
- package/dist/runtime/composables/useIntegrationsLocale.d.ts +26 -0
- package/dist/runtime/composables/useIntegrationsLocale.js +8 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +14 -2
- package/dist/runtime/composables/useIntegrationsRealtime.d.ts +149 -0
- package/dist/runtime/composables/useIntegrationsRealtime.js +80 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +9 -7
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +1 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +3 -1
- package/dist/runtime/composables/useIntegrationsSchedules.d.ts +3 -2
- package/dist/runtime/composables/useIntegrationsSecrets.js +4 -2
- package/dist/runtime/composables/useIntegrationsT.d.ts +35 -0
- package/dist/runtime/composables/useIntegrationsT.js +9 -0
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +3 -3
- package/dist/runtime/composables/useIntegrationsTemplates.js +16 -8
- package/dist/runtime/composables/useIntegrationsTriggers.d.ts +23 -3
- package/dist/runtime/composables/useIntegrationsTriggers.js +21 -8
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +10 -1
- package/dist/runtime/composables/useIntegrationsWorkflows.js +30 -7
- package/dist/runtime/composables/useNodeConfigResolve.js +4 -2
- package/dist/runtime/data/integrationNodeCatalog.d.ts +63 -1
- package/dist/runtime/data/integrationNodeCatalog.js +22 -4
- package/dist/runtime/data/integrationTemplates.d.ts +9 -0
- package/dist/runtime/data/integrationTemplates.js +7 -1
- package/dist/runtime/data/nodeInputData.d.ts +58 -1
- package/dist/runtime/data/nodeInputData.js +30 -5
- package/dist/runtime/data/payloadForm.js +2 -1
- package/dist/runtime/data/triggerGlyphs.d.ts +47 -0
- package/dist/runtime/data/triggerGlyphs.js +11 -0
- package/dist/runtime/data/workflowIconCatalog.js +2 -1
- package/dist/runtime/i18n/messages/de.json +1224 -0
- package/dist/runtime/i18n/messages/en.json +1224 -0
- package/dist/runtime/i18n/translate.d.ts +83 -0
- package/dist/runtime/i18n/translate.js +41 -0
- package/dist/runtime/pages/integrations/audit.vue +52 -34
- package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +3 -2
- package/dist/runtime/pages/integrations/credentials.vue +73 -52
- package/dist/runtime/pages/integrations/index.vue +66 -73
- package/dist/runtime/pages/integrations/runs.vue +33 -21
- package/dist/runtime/pages/integrations/schedules.vue +49 -28
- package/dist/runtime/pages/integrations/secrets.vue +34 -34
- package/dist/runtime/pages/integrations/templates/[slug].vue +89 -72
- package/dist/runtime/pages/integrations/templates/index.vue +35 -27
- package/dist/runtime/pages/integrations/workflows/[id].vue +351 -197
- package/dist/runtime/pages/integrations/workflows/index.vue +10 -5
- package/dist/runtime/plugins/locale.d.ts +10 -0
- package/dist/runtime/plugins/locale.js +6 -0
- package/dist/runtime/plugins/realtime.client.d.ts +32 -0
- package/dist/runtime/plugins/realtime.client.js +4 -0
- package/dist/runtime/types/integrations.d.ts +25 -1
- package/dist/runtime/utils/cockpitSources.d.ts +22 -3
- package/dist/runtime/utils/credentialState.d.ts +30 -2
- package/dist/runtime/utils/credentialState.js +26 -11
- package/dist/runtime/utils/localize.d.ts +52 -5
- package/dist/runtime/utils/localize.js +47 -9
- package/dist/runtime/utils/pollBudget.d.ts +72 -0
- package/dist/runtime/utils/pollBudget.js +17 -0
- package/dist/runtime/utils/schedule.d.ts +0 -25
- package/dist/runtime/utils/schedule.js +37 -25
- package/dist/runtime/utils/servedPaths.d.ts +22 -0
- package/dist/runtime/utils/servedPaths.js +3 -0
- package/dist/runtime/utils/statusStyle.d.ts +26 -2
- package/dist/runtime/utils/statusStyle.js +9 -1
- package/dist/runtime/utils/validationFindings.d.ts +2 -2
- package/dist/runtime/utils/validationFindings.js +4 -3
- package/dist/types.d.mts +2 -6
- package/package.json +3 -2
|
@@ -56,13 +56,15 @@ import { findingsByNodeField } from "../../utils/validationFindings";
|
|
|
56
56
|
import { resolveDisplayName, resolveLocalized } from "../../utils/localize";
|
|
57
57
|
const props = defineProps({
|
|
58
58
|
workflow: { type: Object, required: true },
|
|
59
|
+
draft: { type: [Object, null], required: false },
|
|
59
60
|
runBusy: { type: Boolean, required: false },
|
|
60
61
|
runWaiting: { type: Boolean, required: false },
|
|
61
62
|
overlayOpen: { type: Boolean, required: false }
|
|
62
63
|
});
|
|
63
64
|
const emit = defineEmits(["change", "run", "toggleActive", "openSettings"]);
|
|
65
|
+
const t = useIntegrationsT();
|
|
64
66
|
const { keys: secretKeys } = useIntegrationsSecrets();
|
|
65
|
-
const blob = ref(clone(props.workflow.blob));
|
|
67
|
+
const blob = ref(clone(props.draft ?? props.workflow.blob));
|
|
66
68
|
watch(() => props.workflow.id, () => {
|
|
67
69
|
blob.value = clone(props.workflow.blob);
|
|
68
70
|
syncFromBlob();
|
|
@@ -127,7 +129,7 @@ function triggerPosition(id, index) {
|
|
|
127
129
|
function nodePosition(id) {
|
|
128
130
|
const wn = blob.value.nodes.find((n) => n.id === id);
|
|
129
131
|
if (wn) return { x: wn.position?.x ?? 0, y: wn.position?.y ?? 0 };
|
|
130
|
-
const ti = blob.value.triggers.findIndex((
|
|
132
|
+
const ti = blob.value.triggers.findIndex((t2) => t2.id === id);
|
|
131
133
|
if (ti >= 0) return triggerPosition(id, ti);
|
|
132
134
|
return { x: 0, y: 0 };
|
|
133
135
|
}
|
|
@@ -197,7 +199,7 @@ function ungroup(id) {
|
|
|
197
199
|
push();
|
|
198
200
|
}
|
|
199
201
|
function createGroupFromSelection(ids) {
|
|
200
|
-
const valid = ids.filter((id2) => blob.value.nodes.some((n) => n.id === id2) || blob.value.triggers.some((
|
|
202
|
+
const valid = ids.filter((id2) => blob.value.nodes.some((n) => n.id === id2) || blob.value.triggers.some((t2) => t2.id === id2));
|
|
201
203
|
if (valid.length < 1) return;
|
|
202
204
|
for (const g of blob.value.groups ?? []) {
|
|
203
205
|
g.nodeIds = g.nodeIds.filter((x) => !valid.includes(x));
|
|
@@ -206,7 +208,7 @@ function createGroupFromSelection(ids) {
|
|
|
206
208
|
const id = `g-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
207
209
|
blob.value.groups = [...blob.value.groups ?? [], {
|
|
208
210
|
id,
|
|
209
|
-
label:
|
|
211
|
+
label: t("integration.editor.group.defaultLabel", { n: (blob.value.groups?.length ?? 0) + 1 }),
|
|
210
212
|
nodeIds: valid
|
|
211
213
|
}];
|
|
212
214
|
syncFromBlob();
|
|
@@ -267,12 +269,12 @@ function syncFromBlob() {
|
|
|
267
269
|
onRemove: () => removeBlobNode(s.id)
|
|
268
270
|
}
|
|
269
271
|
})),
|
|
270
|
-
...blob.value.triggers.filter((
|
|
271
|
-
id:
|
|
272
|
+
...blob.value.triggers.filter((t2) => !collapsedBy.has(t2.id)).map((t2, i) => ({
|
|
273
|
+
id: t2.id,
|
|
272
274
|
type: "trigger",
|
|
273
|
-
position: triggerPosition(
|
|
274
|
-
class: bornClass(
|
|
275
|
-
data:
|
|
275
|
+
position: triggerPosition(t2.id, i),
|
|
276
|
+
class: bornClass(t2.id),
|
|
277
|
+
data: t2
|
|
276
278
|
})),
|
|
277
279
|
...blob.value.nodes.filter((n) => !collapsedBy.has(n.id)).map((n) => ({
|
|
278
280
|
id: n.id,
|
|
@@ -394,7 +396,7 @@ function persistDraggedNodes(evt) {
|
|
|
394
396
|
const dy = Math.round(n.position.y - origin.frame.y);
|
|
395
397
|
for (const [id, memberPos] of Object.entries(origin.members)) {
|
|
396
398
|
const target = { x: Math.round(memberPos.x + dx), y: Math.round(memberPos.y + dy) };
|
|
397
|
-
if (blob.value.triggers.some((
|
|
399
|
+
if (blob.value.triggers.some((t2) => t2.id === id)) {
|
|
398
400
|
blob.value.triggerPositions = { ...blob.value.triggerPositions ?? {}, [id]: target };
|
|
399
401
|
} else {
|
|
400
402
|
const wn = blob.value.nodes.find((b) => b.id === id);
|
|
@@ -407,7 +409,7 @@ function persistDraggedNodes(evt) {
|
|
|
407
409
|
}
|
|
408
410
|
continue;
|
|
409
411
|
}
|
|
410
|
-
if (blob.value.triggers.some((
|
|
412
|
+
if (blob.value.triggers.some((t2) => t2.id === n.id)) {
|
|
411
413
|
blob.value.triggerPositions = { ...blob.value.triggerPositions ?? {}, [n.id]: pos };
|
|
412
414
|
} else if (blob.value.stickies?.some((s) => s.id === n.id)) {
|
|
413
415
|
const st = blob.value.stickies.find((s) => s.id === n.id);
|
|
@@ -456,7 +458,7 @@ flow.onConnectEnd((event) => {
|
|
|
456
458
|
pendingConnect.value = { sourceId: start.sourceId, output: start.output, pos: { x: snap(pos.x), y: snap(pos.y) } };
|
|
457
459
|
pendingSpliceEdgeId.value = null;
|
|
458
460
|
void focusPalette();
|
|
459
|
-
toast.info("
|
|
461
|
+
toast.info(t("integration.editor.quickConnect.toast"));
|
|
460
462
|
});
|
|
461
463
|
flow.onEdgeUpdate(({ edge, connection }) => {
|
|
462
464
|
if (!connection.source || !connection.target) return;
|
|
@@ -468,20 +470,49 @@ flow.onEdgeUpdate(({ edge, connection }) => {
|
|
|
468
470
|
syncFromBlob();
|
|
469
471
|
push();
|
|
470
472
|
});
|
|
471
|
-
|
|
473
|
+
let pendingEdgeRemovals = [];
|
|
474
|
+
let pendingNodeRemovals = [];
|
|
475
|
+
let removalFlushQueued = false;
|
|
476
|
+
function queueRemovalFlush() {
|
|
477
|
+
if (removalFlushQueued) return;
|
|
478
|
+
removalFlushQueued = true;
|
|
479
|
+
void nextTick(flushRemovals);
|
|
480
|
+
}
|
|
481
|
+
function flushRemovals() {
|
|
482
|
+
removalFlushQueued = false;
|
|
483
|
+
const edges = pendingEdgeRemovals;
|
|
484
|
+
const nodes = pendingNodeRemovals;
|
|
485
|
+
pendingEdgeRemovals = [];
|
|
486
|
+
pendingNodeRemovals = [];
|
|
487
|
+
const goneNodes = new Set(nodes);
|
|
472
488
|
let mutated = false;
|
|
489
|
+
for (const c of edges) {
|
|
490
|
+
if (goneNodes.has(c.source) || goneNodes.has(c.target)) continue;
|
|
491
|
+
blob.value.edges = blob.value.edges.filter((e) => e.id !== c.id);
|
|
492
|
+
mutated = true;
|
|
493
|
+
}
|
|
494
|
+
if (nodes.length) {
|
|
495
|
+
dropFromBlob(nodes);
|
|
496
|
+
mutated = true;
|
|
497
|
+
}
|
|
498
|
+
if (mutated) {
|
|
499
|
+
syncFromBlob();
|
|
500
|
+
push();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
flow.onEdgesChange((changes) => {
|
|
473
504
|
for (const c of changes) {
|
|
474
505
|
if (c.type === "remove") {
|
|
475
|
-
|
|
476
|
-
mutated = true;
|
|
506
|
+
pendingEdgeRemovals.push({ id: c.id, source: c.source, target: c.target });
|
|
477
507
|
}
|
|
478
508
|
}
|
|
479
|
-
if (
|
|
509
|
+
if (pendingEdgeRemovals.length) queueRemovalFlush();
|
|
480
510
|
});
|
|
481
511
|
flow.onNodesChange((changes) => {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
512
|
+
const removed = changes.filter((c) => c.type === "remove").map((c) => c.id);
|
|
513
|
+
if (!removed.length) return;
|
|
514
|
+
pendingNodeRemovals.push(...removed);
|
|
515
|
+
queueRemovalFlush();
|
|
485
516
|
});
|
|
486
517
|
function stripFromGroups(drop) {
|
|
487
518
|
if (!blob.value.groups?.length) return;
|
|
@@ -496,7 +527,7 @@ function removeBlobNode(id) {
|
|
|
496
527
|
stripFromGroups(/* @__PURE__ */ new Set([id]));
|
|
497
528
|
blob.value.nodes = blob.value.nodes.filter((n) => n.id !== id);
|
|
498
529
|
if (blob.value.stickies?.length) blob.value.stickies = blob.value.stickies.filter((s) => s.id !== id);
|
|
499
|
-
blob.value.triggers = blob.value.triggers.filter((
|
|
530
|
+
blob.value.triggers = blob.value.triggers.filter((t2) => t2.id !== id);
|
|
500
531
|
blob.value.edges = blob.value.edges.filter((e) => e.from.nodeId !== id && e.to.nodeId !== id);
|
|
501
532
|
if (blob.value.triggerPositions?.[id]) delete blob.value.triggerPositions[id];
|
|
502
533
|
if (selectedId.value === id) selectedId.value = null;
|
|
@@ -527,7 +558,7 @@ function addSticky(at) {
|
|
|
527
558
|
id: `s-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`,
|
|
528
559
|
// n8n-style starter content: teaches the edit gesture and the supported
|
|
529
560
|
// markdown by example.
|
|
530
|
-
content: "
|
|
561
|
+
content: t("integration.editor.sticky.starterContent"),
|
|
531
562
|
position: at ? { x: Math.round(at.x), y: Math.round(at.y) } : {
|
|
532
563
|
x: Math.round(center.x - STICKY_DEFAULT_SIZE.width / 2),
|
|
533
564
|
y: Math.round(center.y - STICKY_DEFAULT_SIZE.height / 2)
|
|
@@ -538,18 +569,22 @@ function addSticky(at) {
|
|
|
538
569
|
syncFromBlob();
|
|
539
570
|
push();
|
|
540
571
|
}
|
|
541
|
-
function
|
|
572
|
+
function dropFromBlob(ids) {
|
|
542
573
|
if (!ids.length) return;
|
|
543
574
|
const drop = new Set(ids);
|
|
544
575
|
stripFromGroups(drop);
|
|
545
576
|
blob.value.nodes = blob.value.nodes.filter((n) => !drop.has(n.id));
|
|
546
|
-
blob.value.triggers = blob.value.triggers.filter((
|
|
577
|
+
blob.value.triggers = blob.value.triggers.filter((t2) => !drop.has(t2.id));
|
|
547
578
|
blob.value.edges = blob.value.edges.filter((e) => !drop.has(e.from.nodeId) && !drop.has(e.to.nodeId));
|
|
548
579
|
if (blob.value.stickies?.length) blob.value.stickies = blob.value.stickies.filter((s) => !drop.has(s.id));
|
|
549
580
|
for (const id of ids) {
|
|
550
581
|
if (blob.value.triggerPositions?.[id]) delete blob.value.triggerPositions[id];
|
|
551
582
|
}
|
|
552
583
|
if (selectedId.value && drop.has(selectedId.value)) selectedId.value = null;
|
|
584
|
+
}
|
|
585
|
+
function removeBlobNodes(ids) {
|
|
586
|
+
if (!ids.length) return;
|
|
587
|
+
dropFromBlob(ids);
|
|
553
588
|
syncFromBlob();
|
|
554
589
|
push();
|
|
555
590
|
}
|
|
@@ -602,9 +637,9 @@ async function copyNodes(ids) {
|
|
|
602
637
|
const edges = blob.value.edges.filter((e) => inner.has(e.from.nodeId) && inner.has(e.to.nodeId));
|
|
603
638
|
try {
|
|
604
639
|
await navigator.clipboard.writeText(JSON.stringify({ kind: CLIP_KIND, version: 1, nodes, edges }, null, 2));
|
|
605
|
-
toast.success(
|
|
640
|
+
toast.success(t("integration.editor.clipboard.toast.copied", nodes.length));
|
|
606
641
|
} catch {
|
|
607
|
-
toast.error("
|
|
642
|
+
toast.error(t("integration.editor.clipboard.error.write"));
|
|
608
643
|
}
|
|
609
644
|
}
|
|
610
645
|
async function pasteNodes(at) {
|
|
@@ -612,18 +647,18 @@ async function pasteNodes(at) {
|
|
|
612
647
|
try {
|
|
613
648
|
text = await navigator.clipboard.readText();
|
|
614
649
|
} catch {
|
|
615
|
-
toast.error("
|
|
650
|
+
toast.error(t("integration.editor.clipboard.error.read"));
|
|
616
651
|
return;
|
|
617
652
|
}
|
|
618
653
|
let payload;
|
|
619
654
|
try {
|
|
620
655
|
payload = JSON.parse(text);
|
|
621
656
|
} catch {
|
|
622
|
-
toast.error("
|
|
657
|
+
toast.error(t("integration.editor.clipboard.error.empty"));
|
|
623
658
|
return;
|
|
624
659
|
}
|
|
625
660
|
if (payload?.kind !== CLIP_KIND || !Array.isArray(payload.nodes) || !payload.nodes.length) {
|
|
626
|
-
toast.error("
|
|
661
|
+
toast.error(t("integration.editor.clipboard.error.empty"));
|
|
627
662
|
return;
|
|
628
663
|
}
|
|
629
664
|
const idMap = /* @__PURE__ */ new Map();
|
|
@@ -739,9 +774,9 @@ function isValidConnection(conn) {
|
|
|
739
774
|
const selectedId = ref(null);
|
|
740
775
|
const selection = computed(() => {
|
|
741
776
|
if (!selectedId.value) return null;
|
|
742
|
-
return blob.value.nodes.find((n) => n.id === selectedId.value) ?? blob.value.triggers.find((
|
|
777
|
+
return blob.value.nodes.find((n) => n.id === selectedId.value) ?? blob.value.triggers.find((t2) => t2.id === selectedId.value) ?? null;
|
|
743
778
|
});
|
|
744
|
-
const selectionIsTrigger = computed(() => !!selectedId.value && blob.value.triggers.some((
|
|
779
|
+
const selectionIsTrigger = computed(() => !!selectedId.value && blob.value.triggers.some((t2) => t2.id === selectedId.value));
|
|
745
780
|
flow.onNodeDoubleClick((evt) => {
|
|
746
781
|
if (evt.node.type === "sticky" || evt.node.type === "group") return;
|
|
747
782
|
selectedId.value = evt.node.id;
|
|
@@ -762,7 +797,7 @@ function centerOnNode(id) {
|
|
|
762
797
|
function revealNode(id) {
|
|
763
798
|
if (paneReady.value) centerOnNode(id);
|
|
764
799
|
else pendingReveal.value = id;
|
|
765
|
-
if (blob.value.nodes.some((n) => n.id === id) || blob.value.triggers.some((
|
|
800
|
+
if (blob.value.nodes.some((n) => n.id === id) || blob.value.triggers.some((t2) => t2.id === id)) {
|
|
766
801
|
renameIntent.value = false;
|
|
767
802
|
selectedId.value = id;
|
|
768
803
|
}
|
|
@@ -776,20 +811,20 @@ const selectedUpstreamOutputs = computed(() => {
|
|
|
776
811
|
return blob.value.edges.filter((e) => e.to.nodeId === id).flatMap((e) => {
|
|
777
812
|
const source = blob.value.nodes.find((n) => n.id === e.from.nodeId);
|
|
778
813
|
if (!source) return [];
|
|
779
|
-
const
|
|
814
|
+
const port = portsForNode(source, findManifest(source.nodeSlug, source.nodeVersion), dynamicOutputs.value[source.id]).find((p) => p.name === e.from.output);
|
|
780
815
|
return [{
|
|
781
816
|
nodeId: source.id,
|
|
782
817
|
// A node's own name is text the user typed — never trimmed, never read
|
|
783
818
|
// as JSON. The manifest strings below it get the normalising resolver.
|
|
784
819
|
nodeName: resolveDisplayName(source.name) || source.nodeSlug,
|
|
785
820
|
output: e.from.output,
|
|
786
|
-
fields:
|
|
821
|
+
fields: port?.fields.map((f) => f.name)
|
|
787
822
|
}];
|
|
788
823
|
});
|
|
789
824
|
});
|
|
790
825
|
function applyInspectorUpdate(next) {
|
|
791
826
|
const id = next.id;
|
|
792
|
-
const triggerIdx = blob.value.triggers.findIndex((
|
|
827
|
+
const triggerIdx = blob.value.triggers.findIndex((t2) => t2.id === id);
|
|
793
828
|
if (triggerIdx >= 0) {
|
|
794
829
|
blob.value.triggers[triggerIdx] = next;
|
|
795
830
|
} else {
|
|
@@ -862,7 +897,7 @@ function addFromPayload(payload, pos) {
|
|
|
862
897
|
const id2 = `g-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
863
898
|
blob.value.groups = [...blob.value.groups ?? [], {
|
|
864
899
|
id: id2,
|
|
865
|
-
label:
|
|
900
|
+
label: t("integration.editor.group.defaultLabel", { n: (blob.value.groups?.length ?? 0) + 1 }),
|
|
866
901
|
nodeIds: [],
|
|
867
902
|
rect: { x: at.x, y: at.y, ...EMPTY_GROUP_SIZE }
|
|
868
903
|
}];
|
|
@@ -872,7 +907,7 @@ function addFromPayload(payload, pos) {
|
|
|
872
907
|
return;
|
|
873
908
|
}
|
|
874
909
|
if (payload.kind === "trigger") {
|
|
875
|
-
const tpl = TRIGGER_TEMPLATES.find((
|
|
910
|
+
const tpl = TRIGGER_TEMPLATES.find((t2) => t2.type === payload.type);
|
|
876
911
|
if (!tpl) return;
|
|
877
912
|
const id2 = crypto.randomUUID();
|
|
878
913
|
blob.value.triggers.push({ id: id2, type: tpl.type, config: { ...tpl.defaultConfig } });
|
|
@@ -950,7 +985,7 @@ provide("workflow-edge-actions", {
|
|
|
950
985
|
if (!blob.value.edges.some((e) => e.id === edgeId)) return;
|
|
951
986
|
pendingSpliceEdgeId.value = edgeId;
|
|
952
987
|
void focusPalette();
|
|
953
|
-
toast.info("
|
|
988
|
+
toast.info(t("integration.editor.splice.toast"));
|
|
954
989
|
}
|
|
955
990
|
});
|
|
956
991
|
function primaryOutputName(slug, version) {
|
|
@@ -962,7 +997,7 @@ function spliceIntoEdge(edgeId, payload) {
|
|
|
962
997
|
const manifest = findManifest(payload.slug, payload.version);
|
|
963
998
|
if (!edge || !manifest) return;
|
|
964
999
|
if (isTerminalManifest(manifest)) {
|
|
965
|
-
toast.error(
|
|
1000
|
+
toast.error(t("integration.editor.splice.error.terminal", { name: resolveLocalized(manifest.name) || manifest.slug }));
|
|
966
1001
|
return;
|
|
967
1002
|
}
|
|
968
1003
|
const a = nodePosition(edge.from.nodeId);
|
|
@@ -1210,6 +1245,14 @@ async function focusPalette() {
|
|
|
1210
1245
|
paletteRef.value?.focusSearch();
|
|
1211
1246
|
}
|
|
1212
1247
|
const lightboxOpen = computed(() => !!selection.value);
|
|
1248
|
+
watch(lightboxOpen, (open) => {
|
|
1249
|
+
if (!open) renameIntent.value = false;
|
|
1250
|
+
});
|
|
1251
|
+
function onConfigOpenAutoFocus(event) {
|
|
1252
|
+
if (renameIntent.value) return;
|
|
1253
|
+
event.preventDefault();
|
|
1254
|
+
event.target?.focus();
|
|
1255
|
+
}
|
|
1213
1256
|
const canvasHasKeyboard = computed(() => !lightboxOpen.value && !variablesOpen.value && !props.overlayOpen);
|
|
1214
1257
|
const singleSelected = computed(() => flow.getSelectedNodes.value.length === 1 ? flow.getSelectedNodes.value[0] : null);
|
|
1215
1258
|
useCanvasShortcuts(() => ({
|
|
@@ -1298,7 +1341,7 @@ const edgeTypes = {
|
|
|
1298
1341
|
:kind="menu.kind"
|
|
1299
1342
|
:selection-count="flow.getSelectedNodes.value.length"
|
|
1300
1343
|
:total-nodes="vfNodes.length"
|
|
1301
|
-
:target-is-trigger="!!menu.targetId && blob.triggers.some((
|
|
1344
|
+
:target-is-trigger="!!menu.targetId && blob.triggers.some((t2) => t2.id === menu.targetId)"
|
|
1302
1345
|
:run-busy="runBusy"
|
|
1303
1346
|
@action="onMenuAction"
|
|
1304
1347
|
>
|
|
@@ -1351,7 +1394,7 @@ const edgeTypes = {
|
|
|
1351
1394
|
class="absolute -top-0.5 left-1/2 -translate-x-1/2 -translate-y-full rounded-md px-1.5 py-0.5 text-xs font-medium text-white shadow-sm"
|
|
1352
1395
|
:class="nodeDrag.dragging.value?.kind === 'trigger' ? 'bg-emerald-500' : nodeDrag.dragging.value?.kind === 'group' ? 'bg-slate-500' : 'bg-violet-500'"
|
|
1353
1396
|
>
|
|
1354
|
-
|
|
1397
|
+
{{ t("integration.editor.dropGhost.label") }}
|
|
1355
1398
|
</span>
|
|
1356
1399
|
</div>
|
|
1357
1400
|
</Transition>
|
|
@@ -1404,11 +1447,11 @@ const edgeTypes = {
|
|
|
1404
1447
|
<button
|
|
1405
1448
|
type="button"
|
|
1406
1449
|
class="flex h-9 items-center gap-1.5 rounded-lg bg-white px-3 text-sm font-medium transition hover:bg-muted/75 hover:cursor-pointer"
|
|
1407
|
-
title="
|
|
1450
|
+
:title="t('integration.editor.palette.action.expand.title')"
|
|
1408
1451
|
@click="void focusPalette()"
|
|
1409
1452
|
>
|
|
1410
1453
|
<Plus :size="14" class="text-emerald-600" />
|
|
1411
|
-
|
|
1454
|
+
{{ t("integration.editor.palette.title") }}
|
|
1412
1455
|
</button>
|
|
1413
1456
|
</div>
|
|
1414
1457
|
</div>
|
|
@@ -1427,29 +1470,29 @@ const edgeTypes = {
|
|
|
1427
1470
|
@pointerdown="palette.startDrag($event, paletteShellRef)"
|
|
1428
1471
|
>
|
|
1429
1472
|
<GripVertical :size="14" class="shrink-0 text-muted-foreground/60" />
|
|
1430
|
-
<span class="min-w-0 flex-1 truncate text-sm font-medium">
|
|
1473
|
+
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ t("integration.editor.palette.title") }}</span>
|
|
1431
1474
|
<DropdownMenu>
|
|
1432
1475
|
<DropdownMenuTrigger as-child>
|
|
1433
1476
|
<button
|
|
1434
1477
|
type="button"
|
|
1435
1478
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
|
1436
|
-
title="
|
|
1479
|
+
:title="t('integration.editor.palette.action.placement.title')"
|
|
1437
1480
|
>
|
|
1438
1481
|
<EllipsisVertical :size="14" />
|
|
1439
1482
|
</button>
|
|
1440
1483
|
</DropdownMenuTrigger>
|
|
1441
1484
|
<DropdownMenuContent align="end" class="w-36 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
|
|
1442
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('left')">
|
|
1443
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('right')">
|
|
1444
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="palette.floatHere(paletteShellRef)">
|
|
1485
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('left')">{{ t("integration.editor.palette.action.dockLeft") }}</DropdownMenuItem>
|
|
1486
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('right')">{{ t("integration.editor.palette.action.dockRight") }}</DropdownMenuItem>
|
|
1487
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="palette.floatHere(paletteShellRef)">{{ t("integration.editor.panel.action.floatHere") }}</DropdownMenuItem>
|
|
1445
1488
|
<DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
|
|
1446
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="palette.setCollapsed(true)">
|
|
1489
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="palette.setCollapsed(true)">{{ t("integration.editor.panel.action.collapse") }}</DropdownMenuItem>
|
|
1447
1490
|
</DropdownMenuContent>
|
|
1448
1491
|
</DropdownMenu>
|
|
1449
1492
|
<button
|
|
1450
1493
|
type="button"
|
|
1451
1494
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
|
1452
|
-
title="
|
|
1495
|
+
:title="t('integration.editor.palette.action.collapse.title')"
|
|
1453
1496
|
@click="palette.setCollapsed(true)"
|
|
1454
1497
|
>
|
|
1455
1498
|
<Minus :size="14" />
|
|
@@ -1491,11 +1534,11 @@ const edgeTypes = {
|
|
|
1491
1534
|
<button
|
|
1492
1535
|
type="button"
|
|
1493
1536
|
class="flex h-9 items-center gap-1.5 rounded-lg bg-white px-3 text-sm font-medium transition hover:bg-muted/75 hover:cursor-pointer"
|
|
1494
|
-
title="
|
|
1537
|
+
:title="t('integration.editor.minimap.action.expand.title')"
|
|
1495
1538
|
@click="minimapLayout.setCollapsed(false)"
|
|
1496
1539
|
>
|
|
1497
1540
|
<MapIcon :size="14" class="text-emerald-600" />
|
|
1498
|
-
|
|
1541
|
+
{{ t("integration.editor.minimap.title") }}
|
|
1499
1542
|
</button>
|
|
1500
1543
|
</div>
|
|
1501
1544
|
</div>
|
|
@@ -1514,31 +1557,31 @@ const edgeTypes = {
|
|
|
1514
1557
|
@pointerdown="minimapLayout.startDrag($event, minimapShellRef)"
|
|
1515
1558
|
>
|
|
1516
1559
|
<GripVertical :size="14" class="shrink-0 text-muted-foreground/60" />
|
|
1517
|
-
<span class="min-w-0 flex-1 truncate text-sm font-medium">
|
|
1560
|
+
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ t("integration.editor.minimap.title") }}</span>
|
|
1518
1561
|
<DropdownMenu>
|
|
1519
1562
|
<DropdownMenuTrigger as-child>
|
|
1520
1563
|
<button
|
|
1521
1564
|
type="button"
|
|
1522
1565
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
|
1523
|
-
title="
|
|
1566
|
+
:title="t('integration.editor.minimap.action.placement.title')"
|
|
1524
1567
|
>
|
|
1525
1568
|
<EllipsisVertical :size="14" />
|
|
1526
1569
|
</button>
|
|
1527
1570
|
</DropdownMenuTrigger>
|
|
1528
1571
|
<DropdownMenuContent align="end" class="w-40 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
|
|
1529
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('top-left')">
|
|
1530
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('top-right')">
|
|
1531
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('bottom-left')">
|
|
1532
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('bottom-right')">
|
|
1533
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.floatHere(minimapShellRef)">
|
|
1572
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('top-left')">{{ t("integration.editor.minimap.action.topLeft") }}</DropdownMenuItem>
|
|
1573
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('top-right')">{{ t("integration.editor.minimap.action.topRight") }}</DropdownMenuItem>
|
|
1574
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('bottom-left')">{{ t("integration.editor.minimap.action.bottomLeft") }}</DropdownMenuItem>
|
|
1575
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.dock('bottom-right')">{{ t("integration.editor.minimap.action.bottomRight") }}</DropdownMenuItem>
|
|
1576
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.floatHere(minimapShellRef)">{{ t("integration.editor.panel.action.floatHere") }}</DropdownMenuItem>
|
|
1534
1577
|
<DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
|
|
1535
|
-
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.setCollapsed(true)">
|
|
1578
|
+
<DropdownMenuItem class="rounded-md text-sm" @select="minimapLayout.setCollapsed(true)">{{ t("integration.editor.panel.action.collapse") }}</DropdownMenuItem>
|
|
1536
1579
|
</DropdownMenuContent>
|
|
1537
1580
|
</DropdownMenu>
|
|
1538
1581
|
<button
|
|
1539
1582
|
type="button"
|
|
1540
1583
|
class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
|
|
1541
|
-
title="
|
|
1584
|
+
:title="t('integration.editor.minimap.action.collapse.title')"
|
|
1542
1585
|
@click="minimapLayout.setCollapsed(true)"
|
|
1543
1586
|
>
|
|
1544
1587
|
<Minus :size="14" />
|
|
@@ -1564,7 +1607,7 @@ const edgeTypes = {
|
|
|
1564
1607
|
<!-- Smart Run: the page decides what "run" means — workflows that
|
|
1565
1608
|
consume trigger.payload open the payload dialog, payload-free
|
|
1566
1609
|
ones fire immediately. One button either way. -->
|
|
1567
|
-
<IntegrationsEditorTip :label="runWaiting ? '
|
|
1610
|
+
<IntegrationsEditorTip :label="runWaiting ? t('integration.editor.tool.run.tooltipWaiting') : t('integration.editor.tool.run.tooltip')" shortcut="mod+enter">
|
|
1568
1611
|
<button
|
|
1569
1612
|
type="button"
|
|
1570
1613
|
class="relative flex h-8 items-center gap-1.5 rounded-lg bg-gradient-to-b from-emerald-500 to-emerald-600 px-3.5 text-sm font-medium text-white transition after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:shadow-[inset_0_1px_0_rgba(255,255,255,0.3),inset_0_-2px_0_rgba(0,0,0,0.25)] hover:from-emerald-600 hover:to-emerald-600 active:translate-y-px disabled:opacity-60"
|
|
@@ -1573,7 +1616,7 @@ const edgeTypes = {
|
|
|
1573
1616
|
>
|
|
1574
1617
|
<Loader2 v-if="runBusy" :size="14" class="animate-spin" />
|
|
1575
1618
|
<Play v-else :size="14" />
|
|
1576
|
-
{{ runWaiting ? "
|
|
1619
|
+
{{ runWaiting ? t("integration.editor.run.waitingForBuild") : t("integration.editor.run.button") }}
|
|
1577
1620
|
</button>
|
|
1578
1621
|
</IntegrationsEditorTip>
|
|
1579
1622
|
|
|
@@ -1582,10 +1625,10 @@ const edgeTypes = {
|
|
|
1582
1625
|
<div class="flex items-center gap-0.5 rounded-lg bg-black/[0.05] p-0.5 shadow-[inset_0_1px_2px_rgba(15,23,42,0.12)]">
|
|
1583
1626
|
<!-- Icon-only, and the tooltip is not an accessible name: each one
|
|
1584
1627
|
says what it does itself, and whether it is the active mode. -->
|
|
1585
|
-
<IntegrationsEditorTip label="
|
|
1628
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.select')" shortcut="v">
|
|
1586
1629
|
<button
|
|
1587
1630
|
type="button"
|
|
1588
|
-
aria-label="
|
|
1631
|
+
:aria-label="t('integration.editor.tool.select')"
|
|
1589
1632
|
:aria-pressed="pointerMode === 'select'"
|
|
1590
1633
|
class="flex size-7 items-center justify-center rounded-lg transition"
|
|
1591
1634
|
:class="pointerMode === 'select' ? 'bg-gradient-to-b from-white to-gray-50 text-foreground shadow-[0_1px_2px_rgba(15,23,42,0.18),inset_0_1px_0_rgba(255,255,255,0.9)]' : 'text-muted-foreground hover:text-foreground'"
|
|
@@ -1594,10 +1637,10 @@ const edgeTypes = {
|
|
|
1594
1637
|
<MousePointer2 :size="14" />
|
|
1595
1638
|
</button>
|
|
1596
1639
|
</IntegrationsEditorTip>
|
|
1597
|
-
<IntegrationsEditorTip label="
|
|
1640
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.hand')" shortcut="h">
|
|
1598
1641
|
<button
|
|
1599
1642
|
type="button"
|
|
1600
|
-
aria-label="
|
|
1643
|
+
:aria-label="t('integration.editor.tool.hand')"
|
|
1601
1644
|
:aria-pressed="pointerMode === 'hand'"
|
|
1602
1645
|
class="flex size-7 items-center justify-center rounded-lg transition"
|
|
1603
1646
|
:class="pointerMode === 'hand' ? 'bg-gradient-to-b from-white to-gray-50 text-foreground shadow-[0_1px_2px_rgba(15,23,42,0.18),inset_0_1px_0_rgba(255,255,255,0.9)]' : 'text-muted-foreground hover:text-foreground'"
|
|
@@ -1608,74 +1651,74 @@ const edgeTypes = {
|
|
|
1608
1651
|
</IntegrationsEditorTip>
|
|
1609
1652
|
</div>
|
|
1610
1653
|
|
|
1611
|
-
<IntegrationsEditorTip label="
|
|
1654
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.tidyUp')" shortcut="shift+alt+t">
|
|
1612
1655
|
<button
|
|
1613
1656
|
type="button"
|
|
1614
1657
|
class="flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium text-foreground transition hover:bg-black/[0.04]"
|
|
1615
1658
|
@click="tidyUp"
|
|
1616
1659
|
>
|
|
1617
1660
|
<LayoutGrid :size="13" />
|
|
1618
|
-
|
|
1661
|
+
{{ t("integration.editor.tool.tidyUp") }}
|
|
1619
1662
|
</button>
|
|
1620
1663
|
</IntegrationsEditorTip>
|
|
1621
|
-
<IntegrationsEditorTip label="
|
|
1664
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.fitView.tooltip')" shortcut="1">
|
|
1622
1665
|
<button
|
|
1623
1666
|
type="button"
|
|
1624
1667
|
class="flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium text-foreground transition hover:bg-black/[0.04]"
|
|
1625
1668
|
@click="flow.fitView({ padding: 0.2, duration: 400, maxZoom: 1 })"
|
|
1626
1669
|
>
|
|
1627
1670
|
<Sparkles :size="13" />
|
|
1628
|
-
|
|
1671
|
+
{{ t("integration.editor.tool.fitView.label") }}
|
|
1629
1672
|
</button>
|
|
1630
1673
|
</IntegrationsEditorTip>
|
|
1631
|
-
<IntegrationsEditorTip label="
|
|
1674
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.addSticky.tooltip')" shortcut="shift+s">
|
|
1632
1675
|
<button
|
|
1633
1676
|
type="button"
|
|
1634
1677
|
class="flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium text-foreground transition hover:bg-black/[0.04]"
|
|
1635
1678
|
@click="addSticky()"
|
|
1636
1679
|
>
|
|
1637
1680
|
<StickyNote :size="13" />
|
|
1638
|
-
|
|
1681
|
+
{{ t("integration.editor.tool.addSticky.label") }}
|
|
1639
1682
|
</button>
|
|
1640
1683
|
</IntegrationsEditorTip>
|
|
1641
|
-
<IntegrationsEditorTip label="
|
|
1684
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.addGroup.tooltip')">
|
|
1642
1685
|
<button
|
|
1643
1686
|
type="button"
|
|
1644
1687
|
class="flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium text-foreground transition hover:bg-black/[0.04]"
|
|
1645
1688
|
@click="addGroupFrame"
|
|
1646
1689
|
>
|
|
1647
1690
|
<GroupIcon :size="13" />
|
|
1648
|
-
|
|
1691
|
+
{{ t("integration.editor.tool.addGroup.label") }}
|
|
1649
1692
|
</button>
|
|
1650
1693
|
</IntegrationsEditorTip>
|
|
1651
|
-
<IntegrationsEditorTip label="
|
|
1694
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.variables.tooltip')" shortcut="shift+v">
|
|
1652
1695
|
<button
|
|
1653
1696
|
type="button"
|
|
1654
1697
|
class="flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium text-foreground transition hover:bg-black/[0.04]"
|
|
1655
1698
|
@click="variablesOpen = true"
|
|
1656
1699
|
>
|
|
1657
1700
|
<Braces :size="13" />
|
|
1658
|
-
|
|
1701
|
+
{{ t("integration.editor.tool.variables.label") }}
|
|
1659
1702
|
<span v-if="variableCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ variableCount }}</span>
|
|
1660
1703
|
</button>
|
|
1661
1704
|
</IntegrationsEditorTip>
|
|
1662
|
-
<IntegrationsEditorTip label="
|
|
1705
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.settings.tooltip')" shortcut="shift+alt+s">
|
|
1663
1706
|
<button
|
|
1664
1707
|
type="button"
|
|
1665
1708
|
class="flex h-8 items-center gap-1.5 rounded-lg px-2.5 text-sm font-medium text-foreground transition hover:bg-black/[0.04]"
|
|
1666
1709
|
@click="emit('openSettings')"
|
|
1667
1710
|
>
|
|
1668
1711
|
<Settings2 :size="13" />
|
|
1669
|
-
|
|
1712
|
+
{{ t("integration.editor.tool.settings.label") }}
|
|
1670
1713
|
</button>
|
|
1671
1714
|
</IntegrationsEditorTip>
|
|
1672
1715
|
|
|
1673
1716
|
<div class="mx-1 h-5 w-px bg-black/10" />
|
|
1674
1717
|
|
|
1675
|
-
<IntegrationsEditorTip label="
|
|
1718
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.zoomOut')" shortcut="-">
|
|
1676
1719
|
<button
|
|
1677
1720
|
type="button"
|
|
1678
|
-
aria-label="
|
|
1721
|
+
:aria-label="t('integration.editor.tool.zoomOut')"
|
|
1679
1722
|
class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
|
|
1680
1723
|
@click="flow.zoomOut({ duration: 150 })"
|
|
1681
1724
|
>
|
|
@@ -1685,7 +1728,7 @@ const edgeTypes = {
|
|
|
1685
1728
|
<!-- Tooltip sits on a wrapper span so its as-child merge never
|
|
1686
1729
|
touches the dropdown trigger (merging both onto one button
|
|
1687
1730
|
swallowed the trigger's pointerdown and the menu never opened). -->
|
|
1688
|
-
<IntegrationsEditorTip label="
|
|
1731
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.zoomLevel.tooltip')" shortcut="0">
|
|
1689
1732
|
<span class="inline-flex">
|
|
1690
1733
|
<DropdownMenu v-model:open="zoomMenuOpen">
|
|
1691
1734
|
<DropdownMenuTrigger as-child>
|
|
@@ -1695,7 +1738,7 @@ const edgeTypes = {
|
|
|
1695
1738
|
users do not need told and without the value they do. -->
|
|
1696
1739
|
<button
|
|
1697
1740
|
type="button"
|
|
1698
|
-
:aria-label="
|
|
1741
|
+
:aria-label="t('integration.editor.tool.zoomLevel.aria', { pct: zoomPct })"
|
|
1699
1742
|
class="w-12 rounded-lg py-1 text-center text-sm tabular-nums text-muted-foreground transition hover:bg-black/[0.04] hover:text-foreground"
|
|
1700
1743
|
>
|
|
1701
1744
|
{{ zoomPct }}
|
|
@@ -1707,7 +1750,7 @@ const edgeTypes = {
|
|
|
1707
1750
|
v-model="zoomInputText"
|
|
1708
1751
|
type="text"
|
|
1709
1752
|
inputmode="numeric"
|
|
1710
|
-
aria-label="
|
|
1753
|
+
:aria-label="t('integration.editor.tool.zoomLevel.inputAria')"
|
|
1711
1754
|
class="h-6 w-full rounded-md border border-black/[0.08] bg-white px-1 text-center text-sm tabular-nums focus:outline-none focus:ring-2 focus:ring-primary/40"
|
|
1712
1755
|
placeholder="%"
|
|
1713
1756
|
@keydown.stop
|
|
@@ -1727,10 +1770,10 @@ const edgeTypes = {
|
|
|
1727
1770
|
</DropdownMenu>
|
|
1728
1771
|
</span>
|
|
1729
1772
|
</IntegrationsEditorTip>
|
|
1730
|
-
<IntegrationsEditorTip label="
|
|
1773
|
+
<IntegrationsEditorTip :label="t('integration.editor.tool.zoomIn')" shortcut="=">
|
|
1731
1774
|
<button
|
|
1732
1775
|
type="button"
|
|
1733
|
-
aria-label="
|
|
1776
|
+
:aria-label="t('integration.editor.tool.zoomIn')"
|
|
1734
1777
|
class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
|
|
1735
1778
|
@click="flow.zoomIn({ duration: 150 })"
|
|
1736
1779
|
>
|
|
@@ -1749,10 +1792,10 @@ const edgeTypes = {
|
|
|
1749
1792
|
>
|
|
1750
1793
|
<div class="rounded-lg border border-dashed border-black/[0.12] bg-white/80 px-5 py-4 text-center text-sm text-muted-foreground shadow-sm backdrop-blur-sm">
|
|
1751
1794
|
<div class="font-medium text-foreground mb-1">
|
|
1752
|
-
|
|
1795
|
+
{{ t("integration.editor.emptyState.title") }}
|
|
1753
1796
|
</div>
|
|
1754
1797
|
<p>
|
|
1755
|
-
|
|
1798
|
+
{{ t("integration.editor.emptyState.hint") }}
|
|
1756
1799
|
</p>
|
|
1757
1800
|
</div>
|
|
1758
1801
|
</div>
|
|
@@ -1787,6 +1830,7 @@ const edgeTypes = {
|
|
|
1787
1830
|
form as a strip down the left of an empty workbench. -->
|
|
1788
1831
|
<DialogContent
|
|
1789
1832
|
:show-close-button="false"
|
|
1833
|
+
@open-auto-focus="onConfigOpenAutoFocus"
|
|
1790
1834
|
class="flex max-w-none flex-col gap-0 overflow-hidden rounded-lg p-0 sm:max-w-none"
|
|
1791
1835
|
:class="selectionIsTrigger ? 'h-[min(520px,86vh)] w-[min(600px,94vw)]' : 'h-[min(680px,86vh)] w-[min(1100px,94vw)]'"
|
|
1792
1836
|
>
|