@revenexx/studio-integrations 1.4.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +297 -0
  2. package/README.md +2 -1
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +6 -6
  5. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +134 -131
  6. package/dist/runtime/components/Integrations/CredentialLogo.vue +1 -1
  7. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +1 -1
  8. package/dist/runtime/components/Integrations/CredentialTypePicker.vue +6 -6
  9. package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +2 -0
  10. package/dist/runtime/components/Integrations/ExpressionInput.vue +90 -11
  11. package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +2 -0
  12. package/dist/runtime/components/Integrations/GroupFrameView.vue +121 -121
  13. package/dist/runtime/components/Integrations/NodeConfigField.vue +20 -5
  14. package/dist/runtime/components/Integrations/NodeInspector.vue +44 -41
  15. package/dist/runtime/components/Integrations/NodePalette.vue +52 -37
  16. package/dist/runtime/components/Integrations/RunActionsMenu.vue +6 -5
  17. package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +7 -0
  18. package/dist/runtime/components/Integrations/RunDetailPanel.vue +14 -2
  19. package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +7 -0
  20. package/dist/runtime/components/Integrations/RunsTable.vue +72 -70
  21. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +20 -6
  22. package/dist/runtime/components/Integrations/StickyNoteView.vue +1 -0
  23. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +10 -6
  24. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +0 -34
  25. package/dist/runtime/components/Integrations/WorkflowEditor.vue +226 -277
  26. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +0 -34
  27. package/dist/runtime/components/Integrations/WorkflowExportDialog.d.vue.ts +6 -0
  28. package/dist/runtime/components/Integrations/WorkflowExportDialog.vue +71 -0
  29. package/dist/runtime/components/Integrations/WorkflowExportDialog.vue.d.ts +6 -0
  30. package/dist/runtime/components/Integrations/WorkflowImportFileStep.d.vue.ts +8 -0
  31. package/dist/runtime/components/Integrations/WorkflowImportFileStep.vue +95 -0
  32. package/dist/runtime/components/Integrations/WorkflowImportFileStep.vue.d.ts +8 -0
  33. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +5 -4
  34. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +0 -7
  35. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +2 -2
  36. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +2 -3
  37. package/dist/runtime/components/Integrations/WorkflowsTable.vue +14 -8
  38. package/dist/runtime/composables/useIntegrationsNodes.js +4 -1
  39. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +3 -1
  40. package/dist/runtime/composables/useIntegrationsWorkflows.js +5 -22
  41. package/dist/runtime/composables/useNow.d.ts +17 -0
  42. package/dist/runtime/composables/useNow.js +16 -0
  43. package/dist/runtime/composables/useWorkflowFile.d.ts +21 -0
  44. package/dist/runtime/composables/useWorkflowFile.js +63 -0
  45. package/dist/runtime/data/expressionEditor.d.ts +11 -1
  46. package/dist/runtime/data/expressionEditor.js +2 -1
  47. package/dist/runtime/data/nodeIcons.d.ts +14 -0
  48. package/dist/runtime/data/nodeIcons.js +57 -14
  49. package/dist/runtime/data/nodeRunData.js +1 -1
  50. package/dist/runtime/data/payloadForm.js +1 -1
  51. package/dist/runtime/i18n/messages/de.json +47 -11
  52. package/dist/runtime/i18n/messages/en.json +47 -11
  53. package/dist/runtime/pages/integrations/audit.vue +3 -3
  54. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +2 -2
  55. package/dist/runtime/pages/integrations/credentials.vue +17 -5
  56. package/dist/runtime/pages/integrations/index.vue +5 -10
  57. package/dist/runtime/pages/integrations/schedules.vue +24 -13
  58. package/dist/runtime/pages/integrations/templates/index.vue +40 -40
  59. package/dist/runtime/pages/integrations/workflows/[id].vue +324 -324
  60. package/dist/runtime/types/integrations.d.ts +13 -0
  61. package/dist/runtime/utils/cockpitSources.d.ts +68 -1
  62. package/dist/runtime/utils/credentialState.d.ts +15 -7
  63. package/dist/runtime/utils/credentialState.js +4 -12
  64. package/dist/runtime/utils/localize.d.ts +12 -0
  65. package/dist/runtime/utils/localize.js +1 -1
  66. package/dist/runtime/utils/paletteOrder.d.ts +29 -0
  67. package/dist/runtime/utils/paletteOrder.js +46 -0
  68. package/dist/runtime/utils/relativeTime.d.ts +29 -0
  69. package/dist/runtime/utils/relativeTime.js +23 -0
  70. package/dist/runtime/utils/runFeedQuery.d.ts +12 -6
  71. package/dist/runtime/utils/runFeedQuery.js +3 -18
  72. package/dist/runtime/utils/settingValue.d.ts +33 -0
  73. package/dist/runtime/utils/settingValue.js +8 -0
  74. package/dist/runtime/utils/statusStyle.d.ts +8 -8
  75. package/dist/runtime/utils/statusStyle.js +4 -1
  76. package/dist/runtime/utils/workflowFile.d.ts +119 -0
  77. package/dist/runtime/utils/workflowFile.js +192 -0
  78. package/package.json +10 -5
  79. package/dist/runtime/utils/errorPathAdvice.d.ts +0 -60
  80. package/dist/runtime/utils/errorPathAdvice.js +0 -19
@@ -51,17 +51,15 @@ import { toast } from "vue-sonner";
51
51
  import WorkflowEdgeView from "./WorkflowEdgeView.vue";
52
52
  import { layoutWorkflowNodes } from "./workflowLayout";
53
53
  import { defaultsFor } from "../../utils/configDefaults";
54
- import { nodeDisplayLabel } from "../../utils/validationFindings";
55
- import { TRIGGER_TEMPLATES, findManifest, newestExecutableVersion } from "../../data/integrationNodeCatalog";
54
+ import { TRIGGER_TEMPLATES, findManifest } from "../../data/integrationNodeCatalog";
56
55
  import { isTerminalManifest, portsForNode } from "../../data/nodeInputData";
57
56
  import { computeLoopBodies, iteratorNodeIds, nodeLoopData } from "../../data/loopBody";
58
57
  import { inputHandleId, outputHandleId, portFromHandleId } from "../../data/handleId";
59
58
  import { OPPOSITE_SIDE } from "./canvasSides";
60
59
  import { deepClone } from "../../utils/deepClone";
61
60
  import { copyToClipboard } from "../../utils/clipboard";
62
- import { groupFindingsByNode } from "../../utils/validationFindings";
61
+ import { groupFindingsByNode, findingsByNodeField } from "../../utils/validationFindings";
63
62
  import { reorderBranchSubtrees } from "../../data/branchReorder";
64
- import { findingsByNodeField } from "../../utils/validationFindings";
65
63
  import { resolveDisplayName, resolveLocalized } from "../../utils/localize";
66
64
  const props = defineProps({
67
65
  workflow: { type: Object, required: true },
@@ -865,17 +863,7 @@ function revealNode(id) {
865
863
  selectedId.value = id;
866
864
  }
867
865
  }
868
- function showNodeOnCanvas(id) {
869
- const folded = collapsedGroupOf().get(id);
870
- if (folded) patchGroup(folded.id, { collapsed: false });
871
- if (blob.value.nodes.some((n) => n.id === id)) {
872
- markBorn(id);
873
- syncFromBlob();
874
- }
875
- if (paneReady.value) centerOnNode(id);
876
- else pendingReveal.value = id;
877
- }
878
- defineExpose({ revealNode, showNodeOnCanvas, attachErrorStop });
866
+ defineExpose({ revealNode });
879
867
  const selectedUpstreamOutputs = computed(() => {
880
868
  const id = selectedId.value;
881
869
  if (!id) return [];
@@ -1108,45 +1096,6 @@ function spliceIntoEdge(edgeId, payload) {
1108
1096
  syncFromBlob();
1109
1097
  push();
1110
1098
  }
1111
- const STOP_AND_ERROR_SLUG = "revenexx:stop-and-error";
1112
- function attachErrorStop(sourceNodeId, port) {
1113
- const source = blob.value.nodes.find((n) => n.id === sourceNodeId);
1114
- const version = newestExecutableVersion(STOP_AND_ERROR_SLUG);
1115
- const manifest = version ? findManifest(STOP_AND_ERROR_SLUG, version) : null;
1116
- if (!source || !manifest) return;
1117
- const at = nodePosition(sourceNodeId);
1118
- const id = `n-${Date.now()}`;
1119
- blob.value.nodes.push({
1120
- id,
1121
- nodeSlug: manifest.slug,
1122
- nodeVersion: manifest.version,
1123
- config: {
1124
- ...defaultsFor(manifest.config ?? []),
1125
- // The template convention verbatim: name the step that failed, then quote
1126
- // its own message, so the run record says what went wrong rather than only
1127
- // that something did. `errorCode` keeps the node's own default.
1128
- message: t("integration.editor.errorPath.message", {
1129
- label: nodeDisplayLabel(source),
1130
- nodeId: source.id,
1131
- port
1132
- })
1133
- },
1134
- // Below the source and on the grid, the way the templates place theirs.
1135
- // `freeSpotNear` keeps two of them off each other.
1136
- position: freeSpotNear({ x: at.x, y: at.y + 180 })
1137
- });
1138
- blob.value.edges.push({
1139
- id: `e-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`,
1140
- from: { nodeId: sourceNodeId, output: port },
1141
- to: { nodeId: id, input: void 0 }
1142
- });
1143
- markBorn(id);
1144
- syncFromBlob();
1145
- if (paneReady.value) centerOnNode(id);
1146
- else pendingReveal.value = id;
1147
- push();
1148
- toast.info(t("integration.editor.errorPath.toast.added", { label: nodeDisplayLabel(source) }));
1149
- }
1150
1099
  function nodeDims(id) {
1151
1100
  const d = flow.findNode(id)?.dimensions;
1152
1101
  return d && d.width > 0 ? d : { width: 220, height: 64 };
@@ -1606,41 +1555,41 @@ const edgeTypes = {
1606
1555
  :style="palette.mode.value === 'floating' ? palette.floatingStyle.value : void 0"
1607
1556
  >
1608
1557
  <div class="flex min-h-0 flex-1 flex-col overflow-hidden border border-white bg-white rounded-lg bg-gradient-to-br from-white via-violet-50/40 to-white relative overflow-hidden">
1609
- <div
1610
- class="flex shrink-0 touch-none select-none items-center gap-1 border-b border-black/[0.06] bg-white px-2 py-1.5"
1611
- :class="palette.dragging.value ? 'cursor-grabbing' : 'cursor-grab'"
1612
- @pointerdown="palette.startDrag($event, paletteShellRef)"
1613
- >
1614
- <GripVertical :size="14" class="shrink-0 text-muted-foreground/60" />
1615
- <span class="min-w-0 flex-1 truncate text-sm font-medium">{{ t("integration.editor.palette.title") }}</span>
1616
- <DropdownMenu>
1617
- <DropdownMenuTrigger as-child>
1618
- <button
1619
- type="button"
1620
- class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
1621
- :title="t('integration.editor.palette.action.placement.title')"
1622
- >
1623
- <EllipsisVertical :size="14" />
1624
- </button>
1625
- </DropdownMenuTrigger>
1626
- <DropdownMenuContent align="end" class="w-36 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
1627
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('left')">{{ t("integration.editor.palette.action.dockLeft") }}</DropdownMenuItem>
1628
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('right')">{{ t("integration.editor.palette.action.dockRight") }}</DropdownMenuItem>
1629
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.floatHere(paletteShellRef)">{{ t("integration.editor.panel.action.floatHere") }}</DropdownMenuItem>
1630
- <DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
1631
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.setCollapsed(true)">{{ t("integration.editor.panel.action.collapse") }}</DropdownMenuItem>
1632
- </DropdownMenuContent>
1633
- </DropdownMenu>
1634
- <button
1635
- type="button"
1636
- class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
1637
- :title="t('integration.editor.palette.action.collapse.title')"
1638
- @click="palette.setCollapsed(true)"
1558
+ <div
1559
+ class="flex shrink-0 touch-none select-none items-center gap-1 border-b border-black/[0.06] bg-white px-2 py-1.5"
1560
+ :class="palette.dragging.value ? 'cursor-grabbing' : 'cursor-grab'"
1561
+ @pointerdown="palette.startDrag($event, paletteShellRef)"
1639
1562
  >
1640
- <Minus :size="14" />
1641
- </button>
1642
- </div>
1643
- <NodePalette ref="paletteRef" class="min-h-0 flex-1" @insert="onPaletteInsert" />
1563
+ <GripVertical :size="14" class="shrink-0 text-muted-foreground/60" />
1564
+ <span class="min-w-0 flex-1 truncate text-sm font-medium">{{ t("integration.editor.palette.title") }}</span>
1565
+ <DropdownMenu>
1566
+ <DropdownMenuTrigger as-child>
1567
+ <button
1568
+ type="button"
1569
+ class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
1570
+ :title="t('integration.editor.palette.action.placement.title')"
1571
+ >
1572
+ <EllipsisVertical :size="14" />
1573
+ </button>
1574
+ </DropdownMenuTrigger>
1575
+ <DropdownMenuContent align="end" class="w-36 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
1576
+ <DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('left')">{{ t("integration.editor.palette.action.dockLeft") }}</DropdownMenuItem>
1577
+ <DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('right')">{{ t("integration.editor.palette.action.dockRight") }}</DropdownMenuItem>
1578
+ <DropdownMenuItem class="rounded-md text-sm" @select="palette.floatHere(paletteShellRef)">{{ t("integration.editor.panel.action.floatHere") }}</DropdownMenuItem>
1579
+ <DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
1580
+ <DropdownMenuItem class="rounded-md text-sm" @select="palette.setCollapsed(true)">{{ t("integration.editor.panel.action.collapse") }}</DropdownMenuItem>
1581
+ </DropdownMenuContent>
1582
+ </DropdownMenu>
1583
+ <button
1584
+ type="button"
1585
+ class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
1586
+ :title="t('integration.editor.palette.action.collapse.title')"
1587
+ @click="palette.setCollapsed(true)"
1588
+ >
1589
+ <Minus :size="14" />
1590
+ </button>
1591
+ </div>
1592
+ <NodePalette ref="paletteRef" class="min-h-0 flex-1" @insert="onPaletteInsert" />
1644
1593
  </div>
1645
1594
  </div>
1646
1595
  </Transition>
@@ -1744,213 +1693,213 @@ const edgeTypes = {
1744
1693
  <!-- Floating control pane — bottom center. -->
1745
1694
  <div ref="controlBar" class="absolute bottom-3 left-1/2 z-10 -translate-x-1/2">
1746
1695
  <TooltipProvider :delay-duration="250">
1747
- <div class="rounded-lg border border-gray-400/10 outline-1 outline-white/30 backdrop-blur-sm">
1748
- <div class="flex items-center gap-0.5 whitespace-nowrap border border-white bg-white rounded-lg bg-gradient-to-br from-white via-violet-50/40 to-white px-2 py-1">
1749
- <!-- Smart Run: the page decides what "run" means — a workflow that
1696
+ <div class="rounded-lg border border-gray-400/10 outline-1 outline-white/30 backdrop-blur-sm">
1697
+ <div class="flex items-center gap-0.5 whitespace-nowrap border border-white bg-white rounded-lg bg-gradient-to-br from-white via-violet-50/40 to-white px-2 py-1">
1698
+ <!-- Smart Run: the page decides what "run" means — a workflow that
1750
1699
  consumes trigger.payload, or leaves the mode to the caller, opens
1751
1700
  the run dialog; one that asks neither fires immediately. One
1752
1701
  button either way. -->
1753
- <IntegrationsEditorTip :label="runWaiting ? t('integration.editor.tool.run.tooltipWaiting') : t('integration.editor.tool.run.tooltip')" :shortcut="primaryShortcut('run')">
1754
- <button
1755
- type="button"
1756
- 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"
1757
- :disabled="runBusy"
1758
- @click="emit('run')"
1759
- >
1760
- <Loader2 v-if="runBusy" :size="14" class="animate-spin" />
1761
- <Play v-else :size="14" />
1762
- {{ runWaiting ? t("integration.editor.run.waitingForBuild") : t("integration.editor.run.button") }}
1763
- </button>
1764
- </IntegrationsEditorTip>
1702
+ <IntegrationsEditorTip :label="runWaiting ? t('integration.editor.tool.run.tooltipWaiting') : t('integration.editor.tool.run.tooltip')" :shortcut="primaryShortcut('run')">
1703
+ <button
1704
+ type="button"
1705
+ 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"
1706
+ :disabled="runBusy"
1707
+ @click="emit('run')"
1708
+ >
1709
+ <Loader2 v-if="runBusy" :size="14" class="animate-spin" />
1710
+ <Play v-else :size="14" />
1711
+ {{ runWaiting ? t("integration.editor.run.waitingForBuild") : t("integration.editor.run.button") }}
1712
+ </button>
1713
+ </IntegrationsEditorTip>
1765
1714
 
1766
- <div class="mx-1 h-5 w-px bg-black/10" />
1715
+ <div class="mx-1 h-5 w-px bg-black/10" />
1767
1716
 
1768
- <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)]">
1769
- <!-- Icon-only, and the tooltip is not an accessible name: each one
1717
+ <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)]">
1718
+ <!-- Icon-only, and the tooltip is not an accessible name: each one
1770
1719
  says what it does itself, and whether it is the active mode. -->
1771
- <IntegrationsEditorTip :label="t('integration.editor.tool.select')" :shortcut="primaryShortcut('pointerSelect')">
1772
- <button
1773
- type="button"
1774
- :aria-label="t('integration.editor.tool.select')"
1775
- :aria-pressed="pointerMode === 'select'"
1776
- class="flex size-7 items-center justify-center rounded-lg transition"
1777
- :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'"
1778
- @click="setPointerMode('select')"
1779
- >
1780
- <MousePointer2 :size="14" />
1781
- </button>
1782
- </IntegrationsEditorTip>
1783
- <IntegrationsEditorTip :label="t('integration.editor.tool.hand')" :shortcut="primaryShortcut('pointerHand')">
1784
- <button
1785
- type="button"
1786
- :aria-label="t('integration.editor.tool.hand')"
1787
- :aria-pressed="pointerMode === 'hand'"
1788
- class="flex size-7 items-center justify-center rounded-lg transition"
1789
- :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'"
1790
- @click="setPointerMode('hand')"
1791
- >
1792
- <Hand :size="14" />
1793
- </button>
1794
- </IntegrationsEditorTip>
1795
- </div>
1720
+ <IntegrationsEditorTip :label="t('integration.editor.tool.select')" :shortcut="primaryShortcut('pointerSelect')">
1721
+ <button
1722
+ type="button"
1723
+ :aria-label="t('integration.editor.tool.select')"
1724
+ :aria-pressed="pointerMode === 'select'"
1725
+ class="flex size-7 items-center justify-center rounded-lg transition"
1726
+ :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'"
1727
+ @click="setPointerMode('select')"
1728
+ >
1729
+ <MousePointer2 :size="14" />
1730
+ </button>
1731
+ </IntegrationsEditorTip>
1732
+ <IntegrationsEditorTip :label="t('integration.editor.tool.hand')" :shortcut="primaryShortcut('pointerHand')">
1733
+ <button
1734
+ type="button"
1735
+ :aria-label="t('integration.editor.tool.hand')"
1736
+ :aria-pressed="pointerMode === 'hand'"
1737
+ class="flex size-7 items-center justify-center rounded-lg transition"
1738
+ :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'"
1739
+ @click="setPointerMode('hand')"
1740
+ >
1741
+ <Hand :size="14" />
1742
+ </button>
1743
+ </IntegrationsEditorTip>
1744
+ </div>
1796
1745
 
1797
- <IntegrationsEditorTip :label="t('integration.editor.tool.tidyUp')" :shortcut="primaryShortcut('tidyUp')">
1798
- <button
1799
- type="button"
1800
- 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]"
1801
- @click="tidyUp"
1802
- >
1803
- <LayoutGrid :size="13" />
1804
- {{ t("integration.editor.tool.tidyUp") }}
1805
- </button>
1806
- </IntegrationsEditorTip>
1807
- <IntegrationsEditorTip :label="t('integration.editor.tool.fitView.tooltip')" :shortcut="primaryShortcut('fitView')">
1808
- <button
1809
- type="button"
1810
- 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]"
1811
- @click="flow.fitView({ padding: 0.2, duration: 400, maxZoom: 1 })"
1812
- >
1813
- <Sparkles :size="13" />
1814
- {{ t("integration.editor.tool.fitView.label") }}
1815
- </button>
1816
- </IntegrationsEditorTip>
1817
- <IntegrationsEditorTip :label="t('integration.editor.tool.addSticky.tooltip')" :shortcut="primaryShortcut('addSticky')">
1818
- <button
1819
- type="button"
1820
- 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]"
1821
- @click="addSticky()"
1822
- >
1823
- <StickyNote :size="13" />
1824
- {{ t("integration.editor.tool.addSticky.label") }}
1825
- </button>
1826
- </IntegrationsEditorTip>
1827
- <IntegrationsEditorTip :label="t('integration.editor.tool.addGroup.tooltip')">
1828
- <button
1829
- type="button"
1830
- 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]"
1831
- @click="addGroupFrame"
1832
- >
1833
- <GroupIcon :size="13" />
1834
- {{ t("integration.editor.tool.addGroup.label") }}
1835
- </button>
1836
- </IntegrationsEditorTip>
1837
- <IntegrationsEditorTip :label="t('integration.editor.tool.variables.tooltip')" :shortcut="primaryShortcut('variables')">
1838
- <button
1839
- type="button"
1840
- 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]"
1841
- @click="variablesOpen = true"
1842
- >
1843
- <Braces :size="13" />
1844
- {{ t("integration.editor.tool.variables.label") }}
1845
- <span v-if="variableCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ variableCount }}</span>
1846
- </button>
1847
- </IntegrationsEditorTip>
1848
- <IntegrationsEditorTip :label="t('integration.editor.tool.state.tooltip')" :shortcut="primaryShortcut('state')">
1849
- <button
1850
- type="button"
1851
- 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]"
1852
- @click="stateOpen = true"
1853
- >
1854
- <Database :size="13" />
1855
- {{ t("integration.editor.tool.state.label") }}
1856
- <span v-if="stateCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ stateCount }}</span>
1857
- </button>
1858
- </IntegrationsEditorTip>
1859
- <IntegrationsEditorTip :label="t('integration.editor.tool.settings.tooltip')" :shortcut="primaryShortcut('settings')">
1860
- <button
1861
- type="button"
1862
- 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]"
1863
- @click="emit('openSettings')"
1864
- >
1865
- <Settings2 :size="13" />
1866
- {{ t("integration.editor.tool.settings.label") }}
1867
- </button>
1868
- </IntegrationsEditorTip>
1869
- <!-- The overview, and the one control that has to be reachable without
1746
+ <IntegrationsEditorTip :label="t('integration.editor.tool.tidyUp')" :shortcut="primaryShortcut('tidyUp')">
1747
+ <button
1748
+ type="button"
1749
+ 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]"
1750
+ @click="tidyUp"
1751
+ >
1752
+ <LayoutGrid :size="13" />
1753
+ {{ t("integration.editor.tool.tidyUp") }}
1754
+ </button>
1755
+ </IntegrationsEditorTip>
1756
+ <IntegrationsEditorTip :label="t('integration.editor.tool.fitView.tooltip')" :shortcut="primaryShortcut('fitView')">
1757
+ <button
1758
+ type="button"
1759
+ 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]"
1760
+ @click="flow.fitView({ padding: 0.2, duration: 400, maxZoom: 1 })"
1761
+ >
1762
+ <Sparkles :size="13" />
1763
+ {{ t("integration.editor.tool.fitView.label") }}
1764
+ </button>
1765
+ </IntegrationsEditorTip>
1766
+ <IntegrationsEditorTip :label="t('integration.editor.tool.addSticky.tooltip')" :shortcut="primaryShortcut('addSticky')">
1767
+ <button
1768
+ type="button"
1769
+ 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]"
1770
+ @click="addSticky()"
1771
+ >
1772
+ <StickyNote :size="13" />
1773
+ {{ t("integration.editor.tool.addSticky.label") }}
1774
+ </button>
1775
+ </IntegrationsEditorTip>
1776
+ <IntegrationsEditorTip :label="t('integration.editor.tool.addGroup.tooltip')">
1777
+ <button
1778
+ type="button"
1779
+ 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]"
1780
+ @click="addGroupFrame"
1781
+ >
1782
+ <GroupIcon :size="13" />
1783
+ {{ t("integration.editor.tool.addGroup.label") }}
1784
+ </button>
1785
+ </IntegrationsEditorTip>
1786
+ <IntegrationsEditorTip :label="t('integration.editor.tool.variables.tooltip')" :shortcut="primaryShortcut('variables')">
1787
+ <button
1788
+ type="button"
1789
+ 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]"
1790
+ @click="variablesOpen = true"
1791
+ >
1792
+ <Braces :size="13" />
1793
+ {{ t("integration.editor.tool.variables.label") }}
1794
+ <span v-if="variableCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ variableCount }}</span>
1795
+ </button>
1796
+ </IntegrationsEditorTip>
1797
+ <IntegrationsEditorTip :label="t('integration.editor.tool.state.tooltip')" :shortcut="primaryShortcut('state')">
1798
+ <button
1799
+ type="button"
1800
+ 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]"
1801
+ @click="stateOpen = true"
1802
+ >
1803
+ <Database :size="13" />
1804
+ {{ t("integration.editor.tool.state.label") }}
1805
+ <span v-if="stateCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ stateCount }}</span>
1806
+ </button>
1807
+ </IntegrationsEditorTip>
1808
+ <IntegrationsEditorTip :label="t('integration.editor.tool.settings.tooltip')" :shortcut="primaryShortcut('settings')">
1809
+ <button
1810
+ type="button"
1811
+ 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]"
1812
+ @click="emit('openSettings')"
1813
+ >
1814
+ <Settings2 :size="13" />
1815
+ {{ t("integration.editor.tool.settings.label") }}
1816
+ </button>
1817
+ </IntegrationsEditorTip>
1818
+ <!-- The overview, and the one control that has to be reachable without
1870
1819
  already knowing a key: it is where the other keys are learnt. Its
1871
1820
  tooltip carries `?` for the same reason — the pane teaches the key
1872
1821
  that teaches the rest. Icon-only, so it carries a name of its own
1873
1822
  (`specs/accessibility.md` AC-2). -->
1874
- <IntegrationsEditorTip :label="t('integration.canvas.shortcuts.dialog.title')" :shortcut="primaryShortcut('overview')">
1875
- <button
1876
- type="button"
1877
- :aria-label="t('integration.canvas.shortcuts.dialog.title')"
1878
- class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1879
- @click="shortcutsOpen = true"
1880
- >
1881
- <Keyboard :size="14" />
1882
- </button>
1883
- </IntegrationsEditorTip>
1823
+ <IntegrationsEditorTip :label="t('integration.canvas.shortcuts.dialog.title')" :shortcut="primaryShortcut('overview')">
1824
+ <button
1825
+ type="button"
1826
+ :aria-label="t('integration.canvas.shortcuts.dialog.title')"
1827
+ class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1828
+ @click="shortcutsOpen = true"
1829
+ >
1830
+ <Keyboard :size="14" />
1831
+ </button>
1832
+ </IntegrationsEditorTip>
1884
1833
 
1885
- <div class="mx-1 h-5 w-px bg-black/10" />
1834
+ <div class="mx-1 h-5 w-px bg-black/10" />
1886
1835
 
1887
- <IntegrationsEditorTip :label="t('integration.editor.tool.zoomOut')" :shortcut="primaryShortcut('zoomOut')">
1888
- <button
1889
- type="button"
1890
- :aria-label="t('integration.editor.tool.zoomOut')"
1891
- class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1892
- @click="flow.zoomOut({ duration: 150 })"
1893
- >
1894
- <ZoomOut :size="14" />
1895
- </button>
1896
- </IntegrationsEditorTip>
1897
- <!-- Tooltip sits on a wrapper span so its as-child merge never
1836
+ <IntegrationsEditorTip :label="t('integration.editor.tool.zoomOut')" :shortcut="primaryShortcut('zoomOut')">
1837
+ <button
1838
+ type="button"
1839
+ :aria-label="t('integration.editor.tool.zoomOut')"
1840
+ class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1841
+ @click="flow.zoomOut({ duration: 150 })"
1842
+ >
1843
+ <ZoomOut :size="14" />
1844
+ </button>
1845
+ </IntegrationsEditorTip>
1846
+ <!-- Tooltip sits on a wrapper span so its as-child merge never
1898
1847
  touches the dropdown trigger (merging both onto one button
1899
1848
  swallowed the trigger's pointerdown and the menu never opened). -->
1900
- <IntegrationsEditorTip :label="t('integration.editor.tool.zoomLevel.tooltip')" :shortcut="primaryShortcut('zoomReset')">
1901
- <span class="inline-flex">
1902
- <DropdownMenu v-model:open="zoomMenuOpen">
1903
- <DropdownMenuTrigger as-child>
1904
- <!-- The label carries the percentage because it replaces the
1849
+ <IntegrationsEditorTip :label="t('integration.editor.tool.zoomLevel.tooltip')" :shortcut="primaryShortcut('zoomReset')">
1850
+ <span class="inline-flex">
1851
+ <DropdownMenu v-model:open="zoomMenuOpen">
1852
+ <DropdownMenuTrigger as-child>
1853
+ <!-- The label carries the percentage because it replaces the
1905
1854
  button's text as its accessible name: a bare "Zoom level"
1906
1855
  would leave screen reader users with the one thing sighted
1907
1856
  users do not need told and without the value they do. -->
1908
- <button
1909
- type="button"
1910
- :aria-label="t('integration.editor.tool.zoomLevel.aria', { pct: zoomPct })"
1911
- 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"
1912
- >
1913
- {{ zoomPct }}
1914
- </button>
1915
- </DropdownMenuTrigger>
1916
- <DropdownMenuContent side="top" align="center" class="w-[4.75rem] min-w-0 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
1917
- <div class="px-0.5 pb-0.5">
1918
- <input
1919
- v-model="zoomInputText"
1920
- type="text"
1921
- inputmode="numeric"
1922
- :aria-label="t('integration.editor.tool.zoomLevel.inputAria')"
1923
- 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"
1924
- placeholder="%"
1925
- @keydown.stop
1926
- @keydown.enter.prevent="applyZoomInput"
1857
+ <button
1858
+ type="button"
1859
+ :aria-label="t('integration.editor.tool.zoomLevel.aria', { pct: zoomPct })"
1860
+ 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"
1861
+ >
1862
+ {{ zoomPct }}
1863
+ </button>
1864
+ </DropdownMenuTrigger>
1865
+ <DropdownMenuContent side="top" align="center" class="w-[4.75rem] min-w-0 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
1866
+ <div class="px-0.5 pb-0.5">
1867
+ <input
1868
+ v-model="zoomInputText"
1869
+ type="text"
1870
+ inputmode="numeric"
1871
+ :aria-label="t('integration.editor.tool.zoomLevel.inputAria')"
1872
+ 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"
1873
+ placeholder="%"
1874
+ @keydown.stop
1875
+ @keydown.enter.prevent="applyZoomInput"
1876
+ >
1877
+ </div>
1878
+ <DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
1879
+ <DropdownMenuItem
1880
+ v-for="p in ZOOM_PRESETS"
1881
+ :key="p"
1882
+ class="justify-center rounded-md px-1 py-0.5 text-sm tabular-nums"
1883
+ @select="setZoomPct(p)"
1884
+ >
1885
+ {{ p }}%
1886
+ </DropdownMenuItem>
1887
+ </DropdownMenuContent>
1888
+ </DropdownMenu>
1889
+ </span>
1890
+ </IntegrationsEditorTip>
1891
+ <IntegrationsEditorTip :label="t('integration.editor.tool.zoomIn')" :shortcut="primaryShortcut('zoomIn')">
1892
+ <button
1893
+ type="button"
1894
+ :aria-label="t('integration.editor.tool.zoomIn')"
1895
+ class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1896
+ @click="flow.zoomIn({ duration: 150 })"
1927
1897
  >
1928
- </div>
1929
- <DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
1930
- <DropdownMenuItem
1931
- v-for="p in ZOOM_PRESETS"
1932
- :key="p"
1933
- class="justify-center rounded-md px-1 py-0.5 text-sm tabular-nums"
1934
- @select="setZoomPct(p)"
1935
- >
1936
- {{ p }}%
1937
- </DropdownMenuItem>
1938
- </DropdownMenuContent>
1939
- </DropdownMenu>
1940
- </span>
1941
- </IntegrationsEditorTip>
1942
- <IntegrationsEditorTip :label="t('integration.editor.tool.zoomIn')" :shortcut="primaryShortcut('zoomIn')">
1943
- <button
1944
- type="button"
1945
- :aria-label="t('integration.editor.tool.zoomIn')"
1946
- class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1947
- @click="flow.zoomIn({ duration: 150 })"
1948
- >
1949
- <ZoomIn :size="14" />
1950
- </button>
1951
- </IntegrationsEditorTip>
1952
- </div>
1953
- </div>
1898
+ <ZoomIn :size="14" />
1899
+ </button>
1900
+ </IntegrationsEditorTip>
1901
+ </div>
1902
+ </div>
1954
1903
  </TooltipProvider>
1955
1904
  </div>
1956
1905
 
@@ -2011,9 +1960,9 @@ const edgeTypes = {
2011
1960
  form as a strip down the left of an empty workbench. -->
2012
1961
  <DialogContent
2013
1962
  :show-close-button="false"
2014
- @open-auto-focus="onConfigOpenAutoFocus"
2015
1963
  class="flex max-w-none flex-col gap-0 overflow-hidden rounded-lg p-0 sm:max-w-none"
2016
1964
  :class="selectionIsTrigger ? 'h-[min(520px,86vh)] w-[min(600px,94vw)]' : 'h-[min(680px,86vh)] w-[min(1100px,94vw)]'"
1965
+ @open-auto-focus="onConfigOpenAutoFocus"
2017
1966
  >
2018
1967
  <NodeInspector
2019
1968
  v-if="selection"
@@ -42,42 +42,8 @@ type __VLS_Props = {
42
42
  * id that is on the canvas is in the blob by construction.
43
43
  */
44
44
  declare function revealNode(id: string): void;
45
- /**
46
- * Point at a node without opening it — for a reader who has to look at the
47
- * card's *surroundings* rather than at its settings.
48
- *
49
- * The deliberate difference from {@link revealNode} is the missing selection,
50
- * and with it the missing settings lightbox. That is the whole reason there are
51
- * two functions: the validation banner sends someone to a field at fault, and
52
- * the dialog is where that field is. The error-path advice sends them to a port
53
- * with nothing on it, and the question they have to answer — where should this
54
- * failure go? — is answered by the graph around the card, which a dialog over
55
- * the canvas covers up.
56
- *
57
- * Marked with the same pulse a newly created card gets, because the job is the
58
- * same one: say which of thirty cards is the one meant.
59
- */
60
- declare function showNodeOnCanvas(id: string): void;
61
- /**
62
- * Hang a Stop-and-Error step off a node's error output and wire it up
63
- * (specs/workflow-advice.md AC-3).
64
- *
65
- * The advice strip can tell an author their error output leads nowhere; on its
66
- * own that is a reprimand without a remedy, because the repair — drop a node,
67
- * pick the right one, connect it, write an expression quoting the failing step's
68
- * message — is four steps of tedium they have to already know. Every template we
69
- * ship does exactly this, twenty-five times over, so the shape is settled and
70
- * worth one click.
71
- *
72
- * Leaves the result unsaved and unopened, like every other insertion here: the
73
- * new step is highlighted and the author decides whether to keep it. Deleting it
74
- * is the undo, because this editor has no undo stack to hook into.
75
- */
76
- declare function attachErrorStop(sourceNodeId: string, port: string): void;
77
45
  declare const _default: import("vue").DefineComponent<__VLS_Props, {
78
46
  revealNode: typeof revealNode;
79
- showNodeOnCanvas: typeof showNodeOnCanvas;
80
- attachErrorStop: typeof attachErrorStop;
81
47
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
82
48
  run: () => any;
83
49
  toggleActive: () => any;