@revenexx/studio-integrations 2.0.0 → 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 (73) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/README.md +1 -0
  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 +7 -26
  21. package/dist/runtime/components/Integrations/StickyNoteView.vue +1 -0
  22. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +10 -6
  23. package/dist/runtime/components/Integrations/WorkflowEditor.vue +224 -225
  24. package/dist/runtime/components/Integrations/WorkflowExportDialog.d.vue.ts +6 -0
  25. package/dist/runtime/components/Integrations/WorkflowExportDialog.vue +71 -0
  26. package/dist/runtime/components/Integrations/WorkflowExportDialog.vue.d.ts +6 -0
  27. package/dist/runtime/components/Integrations/WorkflowImportFileStep.d.vue.ts +8 -0
  28. package/dist/runtime/components/Integrations/WorkflowImportFileStep.vue +95 -0
  29. package/dist/runtime/components/Integrations/WorkflowImportFileStep.vue.d.ts +8 -0
  30. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +5 -4
  31. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +0 -7
  32. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +2 -2
  33. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +2 -3
  34. package/dist/runtime/components/Integrations/WorkflowsTable.vue +14 -8
  35. package/dist/runtime/composables/useIntegrationsNodes.js +4 -1
  36. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +3 -1
  37. package/dist/runtime/composables/useIntegrationsWorkflows.js +5 -22
  38. package/dist/runtime/composables/useNow.d.ts +17 -0
  39. package/dist/runtime/composables/useNow.js +16 -0
  40. package/dist/runtime/composables/useWorkflowFile.d.ts +21 -0
  41. package/dist/runtime/composables/useWorkflowFile.js +63 -0
  42. package/dist/runtime/data/expressionEditor.d.ts +11 -1
  43. package/dist/runtime/data/expressionEditor.js +2 -1
  44. package/dist/runtime/data/nodeIcons.d.ts +14 -0
  45. package/dist/runtime/data/nodeIcons.js +57 -14
  46. package/dist/runtime/data/nodeRunData.js +1 -1
  47. package/dist/runtime/data/payloadForm.js +1 -1
  48. package/dist/runtime/i18n/messages/de.json +42 -0
  49. package/dist/runtime/i18n/messages/en.json +42 -0
  50. package/dist/runtime/pages/integrations/audit.vue +3 -3
  51. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +2 -2
  52. package/dist/runtime/pages/integrations/index.vue +5 -10
  53. package/dist/runtime/pages/integrations/schedules.vue +13 -1
  54. package/dist/runtime/pages/integrations/templates/index.vue +40 -40
  55. package/dist/runtime/pages/integrations/workflows/[id].vue +324 -244
  56. package/dist/runtime/types/integrations.d.ts +13 -0
  57. package/dist/runtime/utils/credentialState.d.ts +0 -7
  58. package/dist/runtime/utils/credentialState.js +0 -12
  59. package/dist/runtime/utils/localize.d.ts +12 -0
  60. package/dist/runtime/utils/localize.js +1 -1
  61. package/dist/runtime/utils/paletteOrder.d.ts +29 -0
  62. package/dist/runtime/utils/paletteOrder.js +46 -0
  63. package/dist/runtime/utils/relativeTime.d.ts +29 -0
  64. package/dist/runtime/utils/relativeTime.js +23 -0
  65. package/dist/runtime/utils/runFeedQuery.d.ts +12 -6
  66. package/dist/runtime/utils/runFeedQuery.js +3 -18
  67. package/dist/runtime/utils/settingValue.d.ts +33 -0
  68. package/dist/runtime/utils/settingValue.js +8 -0
  69. package/dist/runtime/utils/statusStyle.d.ts +8 -8
  70. package/dist/runtime/utils/statusStyle.js +4 -1
  71. package/dist/runtime/utils/workflowFile.d.ts +119 -0
  72. package/dist/runtime/utils/workflowFile.js +192 -0
  73. package/package.json +8 -3
@@ -58,9 +58,8 @@ import { inputHandleId, outputHandleId, portFromHandleId } from "../../data/hand
58
58
  import { OPPOSITE_SIDE } from "./canvasSides";
59
59
  import { deepClone } from "../../utils/deepClone";
60
60
  import { copyToClipboard } from "../../utils/clipboard";
61
- import { groupFindingsByNode } from "../../utils/validationFindings";
61
+ import { groupFindingsByNode, findingsByNodeField } from "../../utils/validationFindings";
62
62
  import { reorderBranchSubtrees } from "../../data/branchReorder";
63
- import { findingsByNodeField } from "../../utils/validationFindings";
64
63
  import { resolveDisplayName, resolveLocalized } from "../../utils/localize";
65
64
  const props = defineProps({
66
65
  workflow: { type: Object, required: true },
@@ -1556,41 +1555,41 @@ const edgeTypes = {
1556
1555
  :style="palette.mode.value === 'floating' ? palette.floatingStyle.value : void 0"
1557
1556
  >
1558
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">
1559
- <div
1560
- 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"
1561
- :class="palette.dragging.value ? 'cursor-grabbing' : 'cursor-grab'"
1562
- @pointerdown="palette.startDrag($event, paletteShellRef)"
1563
- >
1564
- <GripVertical :size="14" class="shrink-0 text-muted-foreground/60" />
1565
- <span class="min-w-0 flex-1 truncate text-sm font-medium">{{ t("integration.editor.palette.title") }}</span>
1566
- <DropdownMenu>
1567
- <DropdownMenuTrigger as-child>
1568
- <button
1569
- type="button"
1570
- class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
1571
- :title="t('integration.editor.palette.action.placement.title')"
1572
- >
1573
- <EllipsisVertical :size="14" />
1574
- </button>
1575
- </DropdownMenuTrigger>
1576
- <DropdownMenuContent align="end" class="w-36 rounded-lg border border-black/[0.06] bg-white p-1 shadow-lg">
1577
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('left')">{{ t("integration.editor.palette.action.dockLeft") }}</DropdownMenuItem>
1578
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.dock('right')">{{ t("integration.editor.palette.action.dockRight") }}</DropdownMenuItem>
1579
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.floatHere(paletteShellRef)">{{ t("integration.editor.panel.action.floatHere") }}</DropdownMenuItem>
1580
- <DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
1581
- <DropdownMenuItem class="rounded-md text-sm" @select="palette.setCollapsed(true)">{{ t("integration.editor.panel.action.collapse") }}</DropdownMenuItem>
1582
- </DropdownMenuContent>
1583
- </DropdownMenu>
1584
- <button
1585
- type="button"
1586
- class="flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted/50 hover:text-foreground"
1587
- :title="t('integration.editor.palette.action.collapse.title')"
1588
- @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)"
1589
1562
  >
1590
- <Minus :size="14" />
1591
- </button>
1592
- </div>
1593
- <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" />
1594
1593
  </div>
1595
1594
  </div>
1596
1595
  </Transition>
@@ -1694,213 +1693,213 @@ const edgeTypes = {
1694
1693
  <!-- Floating control pane — bottom center. -->
1695
1694
  <div ref="controlBar" class="absolute bottom-3 left-1/2 z-10 -translate-x-1/2">
1696
1695
  <TooltipProvider :delay-duration="250">
1697
- <div class="rounded-lg border border-gray-400/10 outline-1 outline-white/30 backdrop-blur-sm">
1698
- <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">
1699
- <!-- 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
1700
1699
  consumes trigger.payload, or leaves the mode to the caller, opens
1701
1700
  the run dialog; one that asks neither fires immediately. One
1702
1701
  button either way. -->
1703
- <IntegrationsEditorTip :label="runWaiting ? t('integration.editor.tool.run.tooltipWaiting') : t('integration.editor.tool.run.tooltip')" :shortcut="primaryShortcut('run')">
1704
- <button
1705
- type="button"
1706
- 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"
1707
- :disabled="runBusy"
1708
- @click="emit('run')"
1709
- >
1710
- <Loader2 v-if="runBusy" :size="14" class="animate-spin" />
1711
- <Play v-else :size="14" />
1712
- {{ runWaiting ? t("integration.editor.run.waitingForBuild") : t("integration.editor.run.button") }}
1713
- </button>
1714
- </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>
1715
1714
 
1716
- <div class="mx-1 h-5 w-px bg-black/10" />
1715
+ <div class="mx-1 h-5 w-px bg-black/10" />
1717
1716
 
1718
- <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)]">
1719
- <!-- 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
1720
1719
  says what it does itself, and whether it is the active mode. -->
1721
- <IntegrationsEditorTip :label="t('integration.editor.tool.select')" :shortcut="primaryShortcut('pointerSelect')">
1722
- <button
1723
- type="button"
1724
- :aria-label="t('integration.editor.tool.select')"
1725
- :aria-pressed="pointerMode === 'select'"
1726
- class="flex size-7 items-center justify-center rounded-lg transition"
1727
- :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'"
1728
- @click="setPointerMode('select')"
1729
- >
1730
- <MousePointer2 :size="14" />
1731
- </button>
1732
- </IntegrationsEditorTip>
1733
- <IntegrationsEditorTip :label="t('integration.editor.tool.hand')" :shortcut="primaryShortcut('pointerHand')">
1734
- <button
1735
- type="button"
1736
- :aria-label="t('integration.editor.tool.hand')"
1737
- :aria-pressed="pointerMode === 'hand'"
1738
- class="flex size-7 items-center justify-center rounded-lg transition"
1739
- :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'"
1740
- @click="setPointerMode('hand')"
1741
- >
1742
- <Hand :size="14" />
1743
- </button>
1744
- </IntegrationsEditorTip>
1745
- </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>
1746
1745
 
1747
- <IntegrationsEditorTip :label="t('integration.editor.tool.tidyUp')" :shortcut="primaryShortcut('tidyUp')">
1748
- <button
1749
- type="button"
1750
- 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]"
1751
- @click="tidyUp"
1752
- >
1753
- <LayoutGrid :size="13" />
1754
- {{ t("integration.editor.tool.tidyUp") }}
1755
- </button>
1756
- </IntegrationsEditorTip>
1757
- <IntegrationsEditorTip :label="t('integration.editor.tool.fitView.tooltip')" :shortcut="primaryShortcut('fitView')">
1758
- <button
1759
- type="button"
1760
- 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]"
1761
- @click="flow.fitView({ padding: 0.2, duration: 400, maxZoom: 1 })"
1762
- >
1763
- <Sparkles :size="13" />
1764
- {{ t("integration.editor.tool.fitView.label") }}
1765
- </button>
1766
- </IntegrationsEditorTip>
1767
- <IntegrationsEditorTip :label="t('integration.editor.tool.addSticky.tooltip')" :shortcut="primaryShortcut('addSticky')">
1768
- <button
1769
- type="button"
1770
- 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]"
1771
- @click="addSticky()"
1772
- >
1773
- <StickyNote :size="13" />
1774
- {{ t("integration.editor.tool.addSticky.label") }}
1775
- </button>
1776
- </IntegrationsEditorTip>
1777
- <IntegrationsEditorTip :label="t('integration.editor.tool.addGroup.tooltip')">
1778
- <button
1779
- type="button"
1780
- 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]"
1781
- @click="addGroupFrame"
1782
- >
1783
- <GroupIcon :size="13" />
1784
- {{ t("integration.editor.tool.addGroup.label") }}
1785
- </button>
1786
- </IntegrationsEditorTip>
1787
- <IntegrationsEditorTip :label="t('integration.editor.tool.variables.tooltip')" :shortcut="primaryShortcut('variables')">
1788
- <button
1789
- type="button"
1790
- 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]"
1791
- @click="variablesOpen = true"
1792
- >
1793
- <Braces :size="13" />
1794
- {{ t("integration.editor.tool.variables.label") }}
1795
- <span v-if="variableCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ variableCount }}</span>
1796
- </button>
1797
- </IntegrationsEditorTip>
1798
- <IntegrationsEditorTip :label="t('integration.editor.tool.state.tooltip')" :shortcut="primaryShortcut('state')">
1799
- <button
1800
- type="button"
1801
- 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]"
1802
- @click="stateOpen = true"
1803
- >
1804
- <Database :size="13" />
1805
- {{ t("integration.editor.tool.state.label") }}
1806
- <span v-if="stateCount" class="rounded-lg bg-muted px-1.5 text-xs tabular-nums text-muted-foreground">{{ stateCount }}</span>
1807
- </button>
1808
- </IntegrationsEditorTip>
1809
- <IntegrationsEditorTip :label="t('integration.editor.tool.settings.tooltip')" :shortcut="primaryShortcut('settings')">
1810
- <button
1811
- type="button"
1812
- 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]"
1813
- @click="emit('openSettings')"
1814
- >
1815
- <Settings2 :size="13" />
1816
- {{ t("integration.editor.tool.settings.label") }}
1817
- </button>
1818
- </IntegrationsEditorTip>
1819
- <!-- 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
1820
1819
  already knowing a key: it is where the other keys are learnt. Its
1821
1820
  tooltip carries `?` for the same reason — the pane teaches the key
1822
1821
  that teaches the rest. Icon-only, so it carries a name of its own
1823
1822
  (`specs/accessibility.md` AC-2). -->
1824
- <IntegrationsEditorTip :label="t('integration.canvas.shortcuts.dialog.title')" :shortcut="primaryShortcut('overview')">
1825
- <button
1826
- type="button"
1827
- :aria-label="t('integration.canvas.shortcuts.dialog.title')"
1828
- class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1829
- @click="shortcutsOpen = true"
1830
- >
1831
- <Keyboard :size="14" />
1832
- </button>
1833
- </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>
1834
1833
 
1835
- <div class="mx-1 h-5 w-px bg-black/10" />
1834
+ <div class="mx-1 h-5 w-px bg-black/10" />
1836
1835
 
1837
- <IntegrationsEditorTip :label="t('integration.editor.tool.zoomOut')" :shortcut="primaryShortcut('zoomOut')">
1838
- <button
1839
- type="button"
1840
- :aria-label="t('integration.editor.tool.zoomOut')"
1841
- class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1842
- @click="flow.zoomOut({ duration: 150 })"
1843
- >
1844
- <ZoomOut :size="14" />
1845
- </button>
1846
- </IntegrationsEditorTip>
1847
- <!-- 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
1848
1847
  touches the dropdown trigger (merging both onto one button
1849
1848
  swallowed the trigger's pointerdown and the menu never opened). -->
1850
- <IntegrationsEditorTip :label="t('integration.editor.tool.zoomLevel.tooltip')" :shortcut="primaryShortcut('zoomReset')">
1851
- <span class="inline-flex">
1852
- <DropdownMenu v-model:open="zoomMenuOpen">
1853
- <DropdownMenuTrigger as-child>
1854
- <!-- 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
1855
1854
  button's text as its accessible name: a bare "Zoom level"
1856
1855
  would leave screen reader users with the one thing sighted
1857
1856
  users do not need told and without the value they do. -->
1858
- <button
1859
- type="button"
1860
- :aria-label="t('integration.editor.tool.zoomLevel.aria', { pct: zoomPct })"
1861
- 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"
1862
- >
1863
- {{ zoomPct }}
1864
- </button>
1865
- </DropdownMenuTrigger>
1866
- <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">
1867
- <div class="px-0.5 pb-0.5">
1868
- <input
1869
- v-model="zoomInputText"
1870
- type="text"
1871
- inputmode="numeric"
1872
- :aria-label="t('integration.editor.tool.zoomLevel.inputAria')"
1873
- 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"
1874
- placeholder="%"
1875
- @keydown.stop
1876
- @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 })"
1877
1897
  >
1878
- </div>
1879
- <DropdownMenuSeparator class="my-1 h-px bg-black/[0.06]" />
1880
- <DropdownMenuItem
1881
- v-for="p in ZOOM_PRESETS"
1882
- :key="p"
1883
- class="justify-center rounded-md px-1 py-0.5 text-sm tabular-nums"
1884
- @select="setZoomPct(p)"
1885
- >
1886
- {{ p }}%
1887
- </DropdownMenuItem>
1888
- </DropdownMenuContent>
1889
- </DropdownMenu>
1890
- </span>
1891
- </IntegrationsEditorTip>
1892
- <IntegrationsEditorTip :label="t('integration.editor.tool.zoomIn')" :shortcut="primaryShortcut('zoomIn')">
1893
- <button
1894
- type="button"
1895
- :aria-label="t('integration.editor.tool.zoomIn')"
1896
- class="flex size-8 items-center justify-center rounded-lg text-foreground transition hover:bg-black/[0.04]"
1897
- @click="flow.zoomIn({ duration: 150 })"
1898
- >
1899
- <ZoomIn :size="14" />
1900
- </button>
1901
- </IntegrationsEditorTip>
1902
- </div>
1903
- </div>
1898
+ <ZoomIn :size="14" />
1899
+ </button>
1900
+ </IntegrationsEditorTip>
1901
+ </div>
1902
+ </div>
1904
1903
  </TooltipProvider>
1905
1904
  </div>
1906
1905
 
@@ -1961,9 +1960,9 @@ const edgeTypes = {
1961
1960
  form as a strip down the left of an empty workbench. -->
1962
1961
  <DialogContent
1963
1962
  :show-close-button="false"
1964
- @open-auto-focus="onConfigOpenAutoFocus"
1965
1963
  class="flex max-w-none flex-col gap-0 overflow-hidden rounded-lg p-0 sm:max-w-none"
1966
1964
  :class="selectionIsTrigger ? 'h-[min(520px,86vh)] w-[min(600px,94vw)]' : 'h-[min(680px,86vh)] w-[min(1100px,94vw)]'"
1965
+ @open-auto-focus="onConfigOpenAutoFocus"
1967
1966
  >
1968
1967
  <NodeInspector
1969
1968
  v-if="selection"
@@ -0,0 +1,6 @@
1
+ declare function start(id: number): Promise<void>;
2
+ declare const _default: import("vue").DefineComponent<{}, {
3
+ start: typeof start;
4
+ open: import("vue").Ref<boolean, boolean>;
5
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ export default _default;
@@ -0,0 +1,71 @@
1
+ <script setup>
2
+ import { ref } from "vue";
3
+ import { toast } from "vue-sonner";
4
+ import {
5
+ AlertDialog,
6
+ AlertDialogCancel,
7
+ AlertDialogContent,
8
+ AlertDialogDescription,
9
+ AlertDialogFooter,
10
+ AlertDialogHeader,
11
+ AlertDialogTitle,
12
+ Button
13
+ } from "@revenexx/studio";
14
+ import { useWorkflowFile } from "../../composables/useWorkflowFile";
15
+ import { hasVariableValues } from "../../utils/workflowFile";
16
+ import { errorReason } from "../../utils/apiError";
17
+ const t = useIntegrationsT();
18
+ const { fileFor, download } = useWorkflowFile();
19
+ const open = ref(false);
20
+ const pending = ref(null);
21
+ async function start(id) {
22
+ let file;
23
+ try {
24
+ file = (await fileFor(id)).file;
25
+ } catch (e) {
26
+ toast.error(errorReason(e, t("integration.workflowFile.export.error")));
27
+ return;
28
+ }
29
+ if (hasVariableValues(file.workflow.blob)) {
30
+ pending.value = file;
31
+ open.value = true;
32
+ return;
33
+ }
34
+ finish(file);
35
+ }
36
+ function finish(file) {
37
+ download(file);
38
+ toast.success(t("integration.workflowFile.export.toast", { name: file.workflow.name }));
39
+ }
40
+ function confirm() {
41
+ const file = pending.value;
42
+ open.value = false;
43
+ pending.value = null;
44
+ if (file) finish(file);
45
+ }
46
+ defineExpose({ start, open });
47
+ </script>
48
+
49
+ <template>
50
+ <AlertDialog :open="open" @update:open="(v) => {
51
+ if (!v) {
52
+ open = false;
53
+ pending = null;
54
+ }
55
+ }">
56
+ <AlertDialogContent>
57
+ <AlertDialogHeader>
58
+ <AlertDialogTitle>{{ t("integration.workflowFile.export.notice.title") }}</AlertDialogTitle>
59
+ <AlertDialogDescription>
60
+ {{ t("integration.workflowFile.export.notice.description") }}
61
+ </AlertDialogDescription>
62
+ </AlertDialogHeader>
63
+ <AlertDialogFooter>
64
+ <AlertDialogCancel as-child>
65
+ <Button variant="outline">{{ t("integration.common.action.cancel") }}</Button>
66
+ </AlertDialogCancel>
67
+ <Button @click="confirm">{{ t("integration.workflowFile.export.notice.confirm") }}</Button>
68
+ </AlertDialogFooter>
69
+ </AlertDialogContent>
70
+ </AlertDialog>
71
+ </template>
@@ -0,0 +1,6 @@
1
+ declare function start(id: number): Promise<void>;
2
+ declare const _default: import("vue").DefineComponent<{}, {
3
+ start: typeof start;
4
+ open: import("vue").Ref<boolean, boolean>;
5
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ export default _default;
@@ -0,0 +1,8 @@
1
+ declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
2
+ created: (id: number) => any;
3
+ cancel: () => any;
4
+ }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
5
+ onCreated?: ((id: number) => any) | undefined;
6
+ onCancel?: (() => any) | undefined;
7
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
8
+ export default _default;