@revenexx/studio-integrations 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @revenexx/studio-integrations
2
2
 
3
+ ## 2.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 22eb158: Run in the workflow editor no longer starts the saved version while the canvas shows unsaved changes (PO-567). A changed setting used to be run as its old value, with nothing on screen to say so. Run now stands in the header beside Save rather than in the canvas's control pane, and while there is unsaved work it reads **Save & run** (German **Speichern & Run**): the press saves first and runs the workflow as it then stands. A save that is refused, or waits on the question whether to keep the work as paused, starts no run. The canvas's right-click menu names the save the same way. The header's dispatch badge now shows the mode as entered in the settings, as the name beside it already did, because that is the mode the next run goes out in. `WorkflowEditor` loses its `runWaiting` prop and gains `runSaves`.
8
+
3
9
  ## 2.3.0
4
10
 
5
11
  ### Minor Changes
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revenexx/studio-integrations",
3
3
  "configKey": "studioIntegrations",
4
- "version": "2.3.0",
4
+ "version": "2.4.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -11,6 +11,8 @@ type __VLS_Props = {
11
11
  targetIsTrigger?: boolean;
12
12
  /** Busy flag for the Run item. */
13
13
  runBusy?: boolean;
14
+ /** The workflow has unsaved work, so the Run item saves it first — and says so. */
15
+ runSaves?: boolean;
14
16
  };
15
17
  declare var __VLS_14: {};
16
18
  type __VLS_Slots = {} & {
@@ -16,7 +16,8 @@ const props = defineProps({
16
16
  selectionCount: { type: Number, required: true },
17
17
  totalNodes: { type: Number, required: true },
18
18
  targetIsTrigger: { type: Boolean, required: false },
19
- runBusy: { type: Boolean, required: false }
19
+ runBusy: { type: Boolean, required: false },
20
+ runSaves: { type: Boolean, required: false }
20
21
  });
21
22
  const emit = defineEmits(["action", "update:open"]);
22
23
  const t = useIntegrationsT();
@@ -33,7 +34,7 @@ const sections = computed(() => {
33
34
  { id: "duplicate", label: t("integration.canvas.contextMenu.item.duplicate"), shortcut: primaryShortcut("duplicate"), disabled: props.targetIsTrigger }
34
35
  ],
35
36
  [
36
- { id: "run", label: t("integration.canvas.contextMenu.item.runWorkflow"), shortcut: primaryShortcut("run"), disabled: props.runBusy },
37
+ { id: "run", label: t(props.runSaves ? "integration.canvas.contextMenu.item.saveAndRunWorkflow" : "integration.canvas.contextMenu.item.runWorkflow"), shortcut: primaryShortcut("run"), disabled: props.runBusy },
37
38
  { id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: primaryShortcut("tidyUp") }
38
39
  ],
39
40
  [
@@ -11,6 +11,8 @@ type __VLS_Props = {
11
11
  targetIsTrigger?: boolean;
12
12
  /** Busy flag for the Run item. */
13
13
  runBusy?: boolean;
14
+ /** The workflow has unsaved work, so the Run item saves it first — and says so. */
15
+ runSaves?: boolean;
14
16
  };
15
17
  declare var __VLS_14: {};
16
18
  type __VLS_Slots = {} & {
@@ -20,10 +20,13 @@ type __VLS_Props = {
20
20
  * keystrokes.
21
21
  */
22
22
  draft?: WorkflowBlob | null;
23
- /** Busy flag for the control-pane Play button (a run is being triggered). */
23
+ /** A run is being triggered — the context menu's Run item and the shortcut stand down. */
24
24
  runBusy?: boolean;
25
- /** The run is queued behind a bundle build — busy, but worth saying why. */
26
- runWaiting?: boolean;
25
+ /**
26
+ * The workflow has unsaved work, so Run saves it first. Only the context menu
27
+ * reads it, to say so: Run itself stands in the page header, beside Save.
28
+ */
29
+ runSaves?: boolean;
27
30
  /**
28
31
  * One of the page's own lightboxes is open above the editor (settings,
29
32
  * duplicate, run payload, the sync response, the deactivation prompt). The
@@ -13,10 +13,8 @@ import { MiniMap } from "@vue-flow/minimap";
13
13
  import {
14
14
  Widget as LayoutGrid,
15
15
  MagicStick as Sparkles,
16
- Play,
17
16
  MinusCircle as ZoomOut,
18
- AddCircle as ZoomIn,
19
- RefreshCircle as Loader2
17
+ AddCircle as ZoomIn
20
18
  } from "#solar-icons";
21
19
  import {
22
20
  Dialog,
@@ -65,7 +63,7 @@ const props = defineProps({
65
63
  workflow: { type: Object, required: true },
66
64
  draft: { type: [Object, null], required: false },
67
65
  runBusy: { type: Boolean, required: false },
68
- runWaiting: { type: Boolean, required: false },
66
+ runSaves: { type: Boolean, required: false },
69
67
  overlayOpen: { type: Boolean, required: false }
70
68
  });
71
69
  const emit = defineEmits(["change", "run", "toggleActive", "openSettings"]);
@@ -1420,6 +1418,7 @@ const edgeTypes = {
1420
1418
  :total-nodes="vfNodes.length"
1421
1419
  :target-is-trigger="!!menu.targetId && blob.triggers.some((t2) => t2.id === menu.targetId)"
1422
1420
  :run-busy="runBusy"
1421
+ :run-saves="runSaves"
1423
1422
  @action="onMenuAction"
1424
1423
  >
1425
1424
  <!-- `pan-activation-key-code` is null rather than left at its default, which
@@ -1695,25 +1694,6 @@ const edgeTypes = {
1695
1694
  <TooltipProvider :delay-duration="250">
1696
1695
  <div class="rounded-lg border border-gray-400/10 outline-1 outline-white/30 backdrop-blur-sm">
1697
1696
  <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
1699
- consumes trigger.payload, or leaves the mode to the caller, opens
1700
- the run dialog; one that asks neither fires immediately. One
1701
- button either way. -->
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>
1714
-
1715
- <div class="mx-1 h-5 w-px bg-black/10" />
1716
-
1717
1697
  <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
1698
  <!-- Icon-only, and the tooltip is not an accessible name: each one
1719
1699
  says what it does itself, and whether it is the active mode. -->
@@ -20,10 +20,13 @@ type __VLS_Props = {
20
20
  * keystrokes.
21
21
  */
22
22
  draft?: WorkflowBlob | null;
23
- /** Busy flag for the control-pane Play button (a run is being triggered). */
23
+ /** A run is being triggered — the context menu's Run item and the shortcut stand down. */
24
24
  runBusy?: boolean;
25
- /** The run is queued behind a bundle build — busy, but worth saying why. */
26
- runWaiting?: boolean;
25
+ /**
26
+ * The workflow has unsaved work, so Run saves it first. Only the context menu
27
+ * reads it, to say so: Run itself stands in the page header, beside Save.
28
+ */
29
+ runSaves?: boolean;
27
30
  /**
28
31
  * One of the page's own lightboxes is open above the editor (settings,
29
32
  * duplicate, run payload, the sync response, the deactivation prompt). The
@@ -53,6 +53,7 @@
53
53
  "integration.canvas.contextMenu.item.paste": "Einfügen",
54
54
  "integration.canvas.contextMenu.item.rename": "Umbenennen",
55
55
  "integration.canvas.contextMenu.item.runWorkflow": "Workflow ausführen",
56
+ "integration.canvas.contextMenu.item.saveAndRunWorkflow": "Speichern & Workflow ausführen",
56
57
  "integration.canvas.contextMenu.item.selectAll": "Alle auswählen",
57
58
  "integration.canvas.contextMenu.item.tidyUp": "Aufräumen",
58
59
  "integration.canvas.edge.action.delete": "Verbindung löschen",
@@ -439,6 +440,7 @@
439
440
  "integration.editor.run.error.syncFailed": "Synchroner Run fehlgeschlagen",
440
441
  "integration.editor.run.error.syncTimeout": "Synchroner Run: Zeitüberschreitung",
441
442
  "integration.editor.run.loading": "Run wird geladen…",
443
+ "integration.editor.run.saveAndRun": "Speichern & Run",
442
444
  "integration.editor.run.waitingForBuild": "Warten auf Build…",
443
445
  "integration.editor.runPayload.action.latestEvent": "Letztes reales Event",
444
446
  "integration.editor.runPayload.action.latestEvent.title": "Letztes empfangenes Event dieses Topics einfügen",
@@ -53,6 +53,7 @@
53
53
  "integration.canvas.contextMenu.item.paste": "Paste",
54
54
  "integration.canvas.contextMenu.item.rename": "Rename",
55
55
  "integration.canvas.contextMenu.item.runWorkflow": "Run workflow",
56
+ "integration.canvas.contextMenu.item.saveAndRunWorkflow": "Save & run workflow",
56
57
  "integration.canvas.contextMenu.item.selectAll": "Select all",
57
58
  "integration.canvas.contextMenu.item.tidyUp": "Tidy up",
58
59
  "integration.canvas.edge.action.delete": "Delete this connection",
@@ -439,6 +440,7 @@
439
440
  "integration.editor.run.error.syncFailed": "Sync run failed",
440
441
  "integration.editor.run.error.syncTimeout": "Sync run timed out",
441
442
  "integration.editor.run.loading": "Loading run…",
443
+ "integration.editor.run.saveAndRun": "Save & run",
442
444
  "integration.editor.run.waitingForBuild": "Waiting for build…",
443
445
  "integration.editor.runPayload.action.latestEvent": "Latest real event",
444
446
  "integration.editor.runPayload.action.latestEvent.title": "Insert the latest received event of this topic",
@@ -400,7 +400,11 @@ async function run(mode) {
400
400
  }
401
401
  const payloadDriven = computed(() => payloadFieldsFromBlob(shownBlob.value ?? { nodes: [] }).length > 0);
402
402
  async function runFromEditor() {
403
- if (!workflow.value) return;
403
+ if (!workflow.value || saving.value) return;
404
+ if (isDirty.value) {
405
+ await save();
406
+ if (isDirty.value) return;
407
+ }
404
408
  if (payloadDriven.value || workflow.value.execution_mode === "caller_decides") {
405
409
  runPayloadOpen.value = true;
406
410
  return;
@@ -817,14 +821,12 @@ async function confirmDeactivate() {
817
821
  <WorkflowIconView :icon="shownIcon" :size="16" />
818
822
  </span>
819
823
  <span class="truncate" :title="shownName">{{ shownName }}</span>
820
- <!-- The SAVED mode, beside a drafted name, and the difference is not an
821
- oversight. A name changes nothing about what happens when Run is
822
- pressed, so the header may show what the operator just entered. The
823
- dispatch mode decides what a run does — and a run goes to the
824
- workflow the platform holds, not to the draft. A badge following the
825
- draft would read SYNC over a button that fires an async run. -->
826
- <Badge variant="outline" class="ml-1.5 shrink-0 border text-xs font-medium uppercase text-muted-foreground" :title="t('integration.editor.header.executionMode.title', { mode: modeLabel(workflow.execution_mode) })">
827
- {{ modeLabel(workflow.execution_mode) }}
824
+ <!-- The DRAFTED mode, like the name beside it: Run over a draft saves
825
+ it first (PO-567), so this is the mode the next run goes out in. The
826
+ saved one would read ASYNC over a button about to fire a sync run;
827
+ the unsaved marker says the platform does not have it yet. -->
828
+ <Badge variant="outline" class="ml-1.5 shrink-0 border text-xs font-medium uppercase text-muted-foreground" :title="t('integration.editor.header.executionMode.title', { mode: modeLabel(draftedMode) })">
829
+ {{ modeLabel(draftedMode) }}
828
830
  </Badge>
829
831
  </template>
830
832
  </span>
@@ -927,8 +929,23 @@ async function confirmDeactivate() {
927
929
  <Save v-else :size="12" />
928
930
  {{ saving ? t("integration.editor.action.save.saving") : t("integration.editor.action.save.label") }}
929
931
  </Button>
932
+ <!-- Run stands beside Save because a run goes to the workflow the
933
+ platform holds, not to the draft on the canvas: with the two at
934
+ opposite ends of the screen, a changed value was run as the old one
935
+ and nothing said so. While there is a draft the press saves it first,
936
+ and the name says that it will (PO-567). -->
937
+ <Button
938
+ class="h-7 bg-emerald-600 text-sm text-white hover:bg-emerald-700"
939
+ :disabled="saving || running || syncLoading || waitingForBuild"
940
+ :title="waitingForBuild ? t('integration.editor.tool.run.tooltipWaiting') : t('integration.editor.tool.run.tooltip')"
941
+ @click="runFromEditor"
942
+ >
943
+ <Loader2 v-if="running || syncLoading || waitingForBuild" :size="12" class="animate-spin" />
944
+ <Play v-else :size="12" />
945
+ {{ waitingForBuild ? t("integration.editor.run.waitingForBuild") : isDirty ? t("integration.editor.run.saveAndRun") : t("integration.editor.run.button") }}
946
+ </Button>
930
947
  <!-- Activation is a standing state, not a transport control. The play
931
- symbol belongs to Run, 600 px away in the canvas's control pane,
948
+ symbol belongs to Run, the button just before this one,
932
949
  where pressing it makes one thing happen now; arming a workflow's
933
950
  triggers looks like nothing happening at all. So this states the
934
951
  state in the words the workflows list uses — Active, Paused — and
@@ -1064,8 +1081,8 @@ async function confirmDeactivate() {
1064
1081
  ref="editorRef"
1065
1082
  :workflow="workflow"
1066
1083
  :draft="draft?.blob ?? null"
1067
- :run-busy="running || syncLoading || waitingForBuild"
1068
- :run-waiting="waitingForBuild"
1084
+ :run-busy="saving || running || syncLoading || waitingForBuild"
1085
+ :run-saves="isDirty"
1069
1086
  :overlay-open="settingsOpen || duplicateOpen || importOpen || !!exporter?.open || deleteOpen || syncOpen || runPayloadOpen || deactivationOpen || leaveOpen"
1070
1087
  @change="onChange"
1071
1088
  @run="runFromEditor"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revenexx/studio-integrations",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "description": "Integration Studio (core: workflows, node editor, runs, schedules, credentials, secrets, audit) as a standalone, publishable Nuxt module.",
6
6
  "repository": {