@revenexx/studio-integrations 0.5.1 → 0.7.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 (171) hide show
  1. package/CHANGELOG.md +428 -0
  2. package/README.md +48 -4
  3. package/dist/module.d.mts +17 -1
  4. package/dist/module.json +1 -1
  5. package/dist/module.mjs +10 -2
  6. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +43 -41
  7. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +123 -50
  8. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +108 -71
  9. package/dist/runtime/components/Integrations/CredentialTypePicker.vue +12 -15
  10. package/dist/runtime/components/Integrations/DeadLettersPanel.vue +18 -17
  11. package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +1 -0
  12. package/dist/runtime/components/Integrations/EventTypeCombobox.vue +7 -4
  13. package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +1 -0
  14. package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +4 -0
  15. package/dist/runtime/components/Integrations/ExpressionInput.vue +67 -37
  16. package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +4 -0
  17. package/dist/runtime/components/Integrations/GroupFrameView.vue +30 -20
  18. package/dist/runtime/components/Integrations/Layout.vue +48 -4
  19. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +23 -23
  20. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +11 -13
  21. package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +2 -0
  22. package/dist/runtime/components/Integrations/NodeConfigField.vue +187 -93
  23. package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +2 -0
  24. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +6 -0
  25. package/dist/runtime/components/Integrations/NodeInspector.vue +1157 -376
  26. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +6 -0
  27. package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +12 -0
  28. package/dist/runtime/components/Integrations/NodeLogo.vue +2 -30
  29. package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +12 -0
  30. package/dist/runtime/components/Integrations/NodeMark.d.vue.ts +15 -0
  31. package/dist/runtime/components/Integrations/NodeMark.vue +79 -0
  32. package/dist/runtime/components/Integrations/NodeMark.vue.d.ts +15 -0
  33. package/dist/runtime/components/Integrations/NodeOutputSchema.d.vue.ts +21 -0
  34. package/dist/runtime/components/Integrations/NodeOutputSchema.vue +43 -0
  35. package/dist/runtime/components/Integrations/NodeOutputSchema.vue.d.ts +21 -0
  36. package/dist/runtime/components/Integrations/NodePalette.vue +122 -55
  37. package/dist/runtime/components/Integrations/QueryBuilder.vue +46 -38
  38. package/dist/runtime/components/Integrations/RunActionsMenu.vue +38 -12
  39. package/dist/runtime/components/Integrations/RunDetailPanel.vue +114 -62
  40. package/dist/runtime/components/Integrations/RunPayloadInput.vue +27 -13
  41. package/dist/runtime/components/Integrations/RunResponseModal.vue +11 -10
  42. package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +0 -1
  43. package/dist/runtime/components/Integrations/RunsTable.vue +38 -16
  44. package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +0 -1
  45. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +8 -7
  46. package/dist/runtime/components/Integrations/Sidebar.vue +14 -10
  47. package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +14 -4
  48. package/dist/runtime/components/Integrations/StickyNoteView.vue +38 -23
  49. package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +14 -4
  50. package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +18 -0
  51. package/dist/runtime/components/Integrations/TemplateCard.vue +119 -0
  52. package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +18 -0
  53. package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +2 -2
  54. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +16 -18
  55. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +2 -2
  56. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +34 -33
  57. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +52 -67
  58. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +8 -7
  59. package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +18 -4
  60. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +29 -11
  61. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +18 -4
  62. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +20 -1
  63. package/dist/runtime/components/Integrations/WorkflowEditor.vue +240 -116
  64. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +20 -1
  65. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +28 -43
  66. package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +19 -1
  67. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +17 -7
  68. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +19 -1
  69. package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +4 -0
  70. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +198 -56
  71. package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +4 -0
  72. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +56 -59
  73. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +28 -21
  74. package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +1 -0
  75. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +15 -9
  76. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +1 -0
  77. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +15 -10
  78. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +50 -36
  79. package/dist/runtime/components/Integrations/WorkflowsTable.vue +126 -48
  80. package/dist/runtime/composables/integrationsApi.js +4 -2
  81. package/dist/runtime/composables/useCredentialTypes.js +8 -6
  82. package/dist/runtime/composables/useIntegrationsAudit.js +4 -3
  83. package/dist/runtime/composables/useIntegrationsCredentials.js +10 -8
  84. package/dist/runtime/composables/useIntegrationsDeadLetters.js +7 -6
  85. package/dist/runtime/composables/useIntegrationsFocus.d.ts +12 -0
  86. package/dist/runtime/composables/useIntegrationsLocale.d.ts +26 -0
  87. package/dist/runtime/composables/useIntegrationsLocale.js +8 -0
  88. package/dist/runtime/composables/useIntegrationsNodes.js +7 -5
  89. package/dist/runtime/composables/useIntegrationsRealtime.d.ts +149 -0
  90. package/dist/runtime/composables/useIntegrationsRealtime.js +80 -0
  91. package/dist/runtime/composables/useIntegrationsRevisions.js +9 -7
  92. package/dist/runtime/composables/useIntegrationsRuns.d.ts +1 -1
  93. package/dist/runtime/composables/useIntegrationsRuns.js +14 -7
  94. package/dist/runtime/composables/useIntegrationsSchedules.d.ts +4 -0
  95. package/dist/runtime/composables/useIntegrationsSchedules.js +10 -3
  96. package/dist/runtime/composables/useIntegrationsSecrets.js +14 -9
  97. package/dist/runtime/composables/useIntegrationsT.d.ts +35 -0
  98. package/dist/runtime/composables/useIntegrationsT.js +9 -0
  99. package/dist/runtime/composables/useIntegrationsTemplates.d.ts +3 -3
  100. package/dist/runtime/composables/useIntegrationsTemplates.js +28 -16
  101. package/dist/runtime/composables/useIntegrationsTriggers.js +10 -8
  102. package/dist/runtime/composables/useIntegrationsWorkflows.js +13 -7
  103. package/dist/runtime/composables/useLoadGate.d.ts +17 -0
  104. package/dist/runtime/composables/useLoadGate.js +8 -0
  105. package/dist/runtime/composables/useNodeConfigResolve.js +2 -1
  106. package/dist/runtime/data/branchReorder.d.ts +37 -0
  107. package/dist/runtime/data/branchReorder.js +71 -0
  108. package/dist/runtime/data/handleId.d.ts +23 -0
  109. package/dist/runtime/data/handleId.js +6 -0
  110. package/dist/runtime/data/integrationNodeCatalog.d.ts +41 -0
  111. package/dist/runtime/data/integrationNodeCatalog.js +14 -0
  112. package/dist/runtime/data/integrationTemplates.d.ts +48 -0
  113. package/dist/runtime/data/integrationTemplates.js +21 -0
  114. package/dist/runtime/data/integrationWorkflowSamples.js +1 -1
  115. package/dist/runtime/data/nodeIcons.d.ts +2 -0
  116. package/dist/runtime/data/nodeIcons.js +73 -0
  117. package/dist/runtime/data/nodeInputData.d.ts +16 -4
  118. package/dist/runtime/data/nodeInputData.js +4 -0
  119. package/dist/runtime/data/payloadForm.d.ts +9 -2
  120. package/dist/runtime/data/payloadForm.js +29 -4
  121. package/dist/runtime/data/portTone.d.ts +17 -0
  122. package/dist/runtime/data/portTone.js +7 -0
  123. package/dist/runtime/data/webhookAuth.d.ts +18 -0
  124. package/dist/runtime/data/webhookAuth.js +19 -0
  125. package/dist/runtime/data/workflowIconCatalog.js +4 -6
  126. package/dist/runtime/i18n/messages/de.json +1207 -0
  127. package/dist/runtime/i18n/messages/en.json +1207 -0
  128. package/dist/runtime/i18n/translate.d.ts +83 -0
  129. package/dist/runtime/i18n/translate.js +41 -0
  130. package/dist/runtime/pages/integrations/audit.vue +140 -37
  131. package/dist/runtime/pages/integrations/credentials/oauth-complete.vue +9 -6
  132. package/dist/runtime/pages/integrations/credentials.vue +180 -64
  133. package/dist/runtime/pages/integrations/index.vue +91 -92
  134. package/dist/runtime/pages/integrations/runs.vue +33 -21
  135. package/dist/runtime/pages/integrations/schedules.vue +88 -23
  136. package/dist/runtime/pages/integrations/secrets.vue +57 -35
  137. package/dist/runtime/pages/integrations/templates/[slug].vue +119 -88
  138. package/dist/runtime/pages/integrations/templates/index.vue +82 -103
  139. package/dist/runtime/pages/integrations/workflows/[id].vue +344 -91
  140. package/dist/runtime/pages/integrations/workflows/index.vue +23 -5
  141. package/dist/runtime/plugins/locale.d.ts +10 -0
  142. package/dist/runtime/plugins/locale.js +6 -0
  143. package/dist/runtime/plugins/realtime.client.d.ts +32 -0
  144. package/dist/runtime/plugins/realtime.client.js +4 -0
  145. package/dist/runtime/types/integrations.d.ts +65 -6
  146. package/dist/runtime/utils/apiError.d.ts +47 -0
  147. package/dist/runtime/utils/apiError.js +32 -0
  148. package/dist/runtime/utils/cockpitSources.d.ts +153 -0
  149. package/dist/runtime/utils/cockpitSources.js +14 -0
  150. package/dist/runtime/utils/credentialState.d.ts +24 -1
  151. package/dist/runtime/utils/credentialState.js +26 -11
  152. package/dist/runtime/utils/loadGate.d.ts +61 -0
  153. package/dist/runtime/utils/loadGate.js +22 -0
  154. package/dist/runtime/utils/localize.d.ts +46 -4
  155. package/dist/runtime/utils/localize.js +34 -7
  156. package/dist/runtime/utils/notify.d.ts +59 -0
  157. package/dist/runtime/utils/notify.js +21 -0
  158. package/dist/runtime/utils/pollBudget.d.ts +72 -0
  159. package/dist/runtime/utils/pollBudget.js +17 -0
  160. package/dist/runtime/utils/revenexxCredentialSource.d.ts +26 -0
  161. package/dist/runtime/utils/revenexxCredentialSource.js +8 -0
  162. package/dist/runtime/utils/schedule.d.ts +116 -0
  163. package/dist/runtime/utils/schedule.js +219 -0
  164. package/dist/runtime/utils/statusStyle.d.ts +26 -2
  165. package/dist/runtime/utils/statusStyle.js +9 -1
  166. package/dist/runtime/utils/validationFindings.d.ts +101 -0
  167. package/dist/runtime/utils/validationFindings.js +54 -0
  168. package/dist/runtime/utils/whenIdle.d.ts +42 -0
  169. package/dist/runtime/utils/whenIdle.js +12 -0
  170. package/dist/types.d.mts +2 -6
  171. package/package.json +10 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,428 @@
1
+ # @revenexx/studio-integrations
2
+
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f7ddd90: The run surfaces now update from pushed platform events, not only from their own
8
+ refresh.
9
+
10
+ Where a host has Talkback configured, the Runs list, a workflow's Runs tab, a run's
11
+ record and the replay canvas react to the run and workflow lifecycle the integrations
12
+ service publishes — so a run that finishes is on screen in about a second instead of
13
+ up to three, and a run started from the API, a schedule or an MCP tool appears on an
14
+ idle list that previously had no reason to ask. `workflow.deleted` also takes its runs
15
+ off the list, which no `run.deleted` event exists to do.
16
+
17
+ A run's record follows the same reports, and it now re-reads everything it shows when
18
+ the run it is open on ends — the status it displays comes from a different request
19
+ than the one an ending run used to trigger, so a record left open on a finishing run
20
+ could keep showing the state before it ended. That is fixed for the reported route;
21
+ the three-second poll behind it is unchanged.
22
+
23
+ While events are actually arriving, each surface's own refresh slows to every 30
24
+ seconds and stays as the backstop rather than stopping — the connection signal
25
+ available today cannot tell a quiet socket from a dead one, so a list that stopped
26
+ converging was not a trade worth making. **A connection that reports nothing is
27
+ treated as no connection**: the slower refresh is taken only once something has been
28
+ delivered, so a tenant whose event catalogue does not release these topics to browsers
29
+ keeps the cadence it always had rather than becoming the slowest configuration of the
30
+ three. If the connection drops and comes back having missed more than the platform can
31
+ replay, every surface reads again instead of waiting for a report that no longer
32
+ exists.
33
+
34
+ With no Talkback host configured — the default — nothing is subscribed, neither
35
+ `@revenexx/talkback-js` nor `centrifuge` is loaded, and every surface polls exactly as
36
+ before.
37
+
38
+ **This raises the floor for `@revenexx/studio-shared` to `^0.3.0`**, which is the
39
+ release that carries the realtime seam. A host installing this module gets the newer
40
+ one automatically; a host that pins studio-shared itself has to move with it, or it
41
+ will end up with two copies and no realtime at all.
42
+
43
+ Realtime needs `@revenexx/talkback-js` and `centrifuge` in the host application —
44
+ they are optional peers of studio-shared, so a host that never sets a Talkback host
45
+ installs neither.
46
+
47
+ - 9ef81b8: The studio reads in the language your application chose, both halves of the
48
+ screen at once.
49
+
50
+ Until now every surface was English. Two things change that. The text the node
51
+ packages publish — a node's name, what it is for, the label and example of every
52
+ setting it offers, a credential type's fields, a template's whole description —
53
+ has been shipping in German as well as English all along, and was being discarded
54
+ on the way to the screen; it is now resolved in the reader's language. And the
55
+ studio's own chrome — headings, buttons, tabs, table headers, empty states,
56
+ toasts, confirmations — is read from a message catalogue the module carries
57
+ itself, in English and German.
58
+
59
+ You do not need an i18n library installed, and the module never reads your
60
+ catalogue, a cookie or a browser setting: the language comes from you. Wire one
61
+ of two ways.
62
+
63
+ A language that does not change, in `nuxt.config.ts`:
64
+
65
+ ```ts
66
+ export default defineNuxtConfig({
67
+ modules: ["@revenexx/studio-integrations"],
68
+ studioIntegrations: { locale: "de" },
69
+ });
70
+ ```
71
+
72
+ Deployments can override that with `NUXT_PUBLIC_STUDIO_INTEGRATIONS_LOCALE`
73
+ without a rebuild.
74
+
75
+ A language the user switches while the app is running, from a plugin of yours:
76
+
77
+ ```ts
78
+ const { setLocale } = useIntegrationsLocale();
79
+ watch(hostLocale, (l) => setLocale(l), { immediate: true });
80
+ ```
81
+
82
+ `useIntegrationsLocale()` is auto-imported like every other composable here, so
83
+ you import nothing. A switch reaches surfaces that are already open — no reload,
84
+ no refetch, no reopening a dialog. Both routes may be left alone: with neither
85
+ wired the studio is English, exactly as before, so upgrading changes nothing
86
+ until you ask it to.
87
+
88
+ A tag is BCP-47 and a region falls back to its language, so `de-CH` reads German
89
+ rather than English; anything the catalogue does not carry reads English.
90
+
91
+ **This expects a client-rendered host.** The language is held once per module
92
+ instance, which is once per browser tab on the client and once per process on a
93
+ server. If you render the studio server-side _and_ give each visitor their own
94
+ language, one visitor's choice can reach another's page — a page in the wrong
95
+ language, nothing more, since the value only chooses between the translations of a
96
+ string. A single fixed language is unaffected either way. Both routes above are
97
+ safe in a client-rendered app, which is what the known consumers are.
98
+
99
+ Four English strings changed spelling, to British. One of them is an accessible
100
+ name: the grey swatch in the sticky-note and group-frame colour pickers is
101
+ `Grey`, not `Gray` — update it if a test of yours addresses it by name. The other
102
+ three are wording only: two tooltips on the sample-payload button say `catalogue`,
103
+ and the workflow settings dialog says `Execution behaviour`.
104
+
105
+ - 7cb9f1c: The studio's lists narrow for real.
106
+
107
+ Credentials, runs, workflows and schedules each carried a filter button whose menu
108
+ read "No filters defined." — it could never filter anything. Each now narrows by the
109
+ dimensions its own rows already show: a credential's state and system, a run's
110
+ outcome and what started it, a workflow's position, build and dispatch mode, and
111
+ whether a schedule is live plus the timezone it fires in. Two choices narrow
112
+ together, each has a way back to everything, and the words on the menu are the words
113
+ on the rows.
114
+
115
+ Secrets loses the button instead: a key and two dates have nothing categorical to
116
+ narrow by, so the honest outcome there is not offering it.
117
+
118
+ A narrowing applies to the rows the page has loaded. On the lists collected per
119
+ workflow that is as far back as the page has read.
120
+
121
+ - 8f332af: Deleting is drawn with one bin everywhere, duplicating with one pair of sheets, and a
122
+ trigger's delete button says what it deletes.
123
+
124
+ The studio drew the delete action with three different bin glyphs, two of them one
125
+ click apart on the same node: a node's hover toolbar used Lucide's bin while that
126
+ node's own inspector used Solar's, and the credential surfaces used a third, fuller
127
+ Solar bin. Nothing was broken — what it cost is recognition, on the one action that
128
+ cannot be undone. Every delete and remove in the studio now draws Solar's
129
+ `TrashBinMinimalistic`.
130
+
131
+ The copy sheets sat one button along and are folded in: a node's hover toolbar drew
132
+ Lucide's glyph for **Duplicate** while that same node's inspector drew Solar's for
133
+ **Copy**. Both are Solar's now.
134
+
135
+ Two things a host or a test may notice:
136
+
137
+ - **The trigger card's delete button has an accessible name**, `Delete trigger`,
138
+ where it previously fell back to its `Delete` tooltip. That is what the node card's
139
+ `Delete node` already did, so the two can now be addressed and heard the same way.
140
+ - **No delete icon is imported under another library's name any more.** The alias
141
+ `TrashBinMinimalistic as Trash2` is what hid the divergence — two components read
142
+ identically in the markup and differed only in an import line. The bin travels as
143
+ `TrashBin`.
144
+
145
+ Why Solar, what `lucide-vue-next` is still for, and why the glyph itself is not
146
+ promised in a spec: `docs/icons.md`. It also names what is left — five shapes the
147
+ studio still draws from both libraries — and the ticket that carries them.
148
+
149
+ - fa0ef1a: The studio navigation stays when a workflow is opened, and the canvas gets its
150
+ width only when the operator asks.
151
+
152
+ Until now, opening a workflow — or one of its runs — folded the navigation away on
153
+ mount, and nothing anywhere brought it back: the way on was the breadcrumb or the
154
+ browser's back button. That fold is gone. The rail now stands on every surface,
155
+ including a workflow's canvas, and a control at the head of the workflow page,
156
+ reading **Hide navigation**, hands its width to the canvas when the operator wants
157
+ it. The same control then reads **Show navigation** and gives it back, without
158
+ leaving the surface. The choice is remembered per browser, so it is stated once
159
+ rather than on every visit, and it applies to the canvas alone — a run's record,
160
+ the run lists and the earlier versions keep the navigation either way.
161
+
162
+ Two things to know if you host this module:
163
+
164
+ - **A folded rail is now `inert` and `aria-hidden`.** It used to be clipped to zero
165
+ width with its links still focusable, so a keyboard walked into a navigation
166
+ nobody could see. If a test of yours reaches into the collapsed sidebar, it will
167
+ no longer find it in the accessibility tree — which is the point.
168
+ - **The fold is still driven by `useIntegrationsFocus()`**, unchanged, so a host
169
+ shell that reads `focused` keeps working as before. What changed is who turns it
170
+ on: nothing does it by itself any more. If your own shell collapses the sidebar
171
+ on that state, make sure the surface doing so also offers the way back — the
172
+ control belongs to the page, because a host shell can be replaced and the module's
173
+ cannot reach into it.
174
+
175
+ Two new accessible names, in English and German: `Hide navigation` /
176
+ `Show navigation`.
177
+
178
+ ### Patch Changes
179
+
180
+ - 30f5bda: A group frame now tells the truth about what it holds as soon as a member is deleted.
181
+
182
+ Deleting a node out of a group left the frame printing the count it had before —
183
+ `Invoice sync · 3 nodes` over two remaining nodes — until the page was reloaded. The
184
+ saved workflow was correct throughout; it was the caption that lagged. The larger
185
+ half of the same defect: deleting a frame's last member left the frame drawn on the
186
+ canvas over a workflow that no longer contained it, so the editor showed a group
187
+ that had already ceased to exist.
188
+
189
+ Both follow the deletion at once now. An empty frame you drew yourself still stays —
190
+ it counts down to `0 nodes` and waits to be filled.
191
+
192
+ The cause was timing rather than arithmetic: the canvas rebuild ran inside Vue Flow's
193
+ own change pass, which left the rendered graph on the array from before the deletion.
194
+ It is deferred by one tick. Nothing about the API, the saved blob or the keyboard
195
+ changes.
196
+
197
+ - d690841: A note on a read-only canvas no longer offers controls that do nothing.
198
+
199
+ On the run replay and the revision preview, a sticky note used to be drawn with
200
+ the toolbar it has in the editor — a bin, seven colours, a corner handle — and
201
+ could still be double-clicked into a text field. None of it had any effect: the
202
+ note survived its own Delete, typing into it changed nothing, and the workflow
203
+ never became dirty. A reader could not tell "not allowed here" from "broken".
204
+
205
+ The note is now read-only in the way a node card and a group frame already were:
206
+ no toolbar, no resize handle, and a double-click opens nothing. An empty note is
207
+ quiet too: the _Double-click to edit_ hint belongs to the editor, and on these
208
+ canvases it named the one gesture the note refuses. Everything the note says stays
209
+ exactly as readable, formatting and all — this removes affordances, not content.
210
+
211
+ A double-click on a read-only note now reaches the canvas instead of being
212
+ swallowed, so it zooms there like a double-click on a node card or on empty space.
213
+ A group frame's label was swallowing it the same way, and no longer does — the note
214
+ and that label were the two places on those canvases where the gesture did nothing
215
+ at all, and a dead spot reads as broken. The zoom itself is how these canvases
216
+ behave rather than something promised: what is promised is that nothing opens to be
217
+ edited.
218
+
219
+ If you render `StickyNoteView` yourself: the read-only state _is_ the absence of
220
+ its callbacks. `onUpdate` and `onRemove` in `StickyNodeData` are now optional, and
221
+ a note handed neither draws no tools — the pattern `GroupFrameView` already used
222
+ for its own toolbar. Passing no-ops, which is what the two read-only panels did,
223
+ now means "editable, and nothing happens".
224
+
225
+ A host addressing the note's `Delete note` button or its colour swatches on a
226
+ replay or preview canvas will no longer find them. That is the fix, which is why
227
+ this is a patch: nothing here is additive, and what a host can observe is the
228
+ defect being gone.
229
+
230
+ ## 0.6.0
231
+
232
+ ### Minor Changes
233
+
234
+ - 3686d9e: The node inspector's controls report honest states — a field that is loading,
235
+ empty or unavailable says which, instead of all three looking alike — and the
236
+ trigger surface gains a payload form built from the trigger's own shape rather
237
+ than a free-text box. Rows keep stable keys, so editing one no longer re-orders
238
+ the rest.
239
+ - 3686d9e: Activating a workflow the platform refuses now says why. The validation feedback
240
+ the service returns is surfaced in the editor instead of the activation failing
241
+ silently, and a refusal that carries no readable detail falls back to wording
242
+ that still tells the user what happened.
243
+ - 3686d9e: Placing a node no longer forces its configuration open. Dropping a node onto the
244
+ canvas just places it; you open the settings when you want them, through an edit
245
+ affordance that now stays visible on the node card rather than appearing only on
246
+ hover. The affordance is shown only for nodes that actually have something to
247
+ configure.
248
+ - 3686d9e: A node that ships in several versions lets you choose which one you place,
249
+ instead of the palette silently picking one. The chosen version is shown on the
250
+ node card, so a workflow built against an older version is readable on the
251
+ canvas.
252
+ - 3686d9e: A node is now marked by its own symbol combined with a vendor badge, so two
253
+ nodes from the same vendor stay distinguishable while still reading as that
254
+ vendor's. The badge is drawn only where it adds something — including search
255
+ hits in the palette — and a node with no symbol of its own is marked honestly
256
+ rather than given a stand-in.
257
+
258
+ The component behind this is renamed `NodeLogo` → `NodeMark`. `NodeLogo` remains
259
+ as a shim so hosts that reference it keep working.
260
+
261
+ - 3686d9e: The canvas says which output path leads the eye. Edges now carry importance
262
+ rather than colour, so the main path reads as the main path, and colour is
263
+ reserved for the two states where it means something — green for the ordinary
264
+ output, rose for the error output. The inspector's port dots use the same neutral
265
+ tone as the canvas, and an error port's label stays legible instead of being
266
+ tinted into the background.
267
+ - 3686d9e: The mouse wheel zooms the canvas instead of panning it vertically — on the
268
+ editor and on the read-only canvases alike. The zoom readout announces its
269
+ percentage again, so the level is available to screen readers and not only to
270
+ the eye.
271
+ - 3686d9e: Node cards drop the generated subtitle and surface the node's note instead. The
272
+ note is marked in the card's corner and its panel flips open from there, so a
273
+ card carries what somebody wrote about the node rather than a line derived from
274
+ its configuration. The marker is reachable from the keyboard, and the note's
275
+ description stays in the workflow tree rather than living only in the panel.
276
+ - 3686d9e: A node can say what it is for. The description from the node's manifest is shown
277
+ in the inspector, and the note moves out of the settings into its own place, so
278
+ the text the vendor wrote and the text you wrote are no longer mixed into one
279
+ field. The explanation can be reached by pointer as well as keyboard, and Escape
280
+ closes the note without closing anything behind it. Whitespace alone no longer
281
+ counts as a note.
282
+ - 3686d9e: A workflow variable has a value, not a default. The field is named for what it
283
+ actually is — the value the workflow runs with — which removes the suggestion
284
+ that something else would override it, and a variable can be marked as required
285
+ so an incomplete workflow is caught before it runs.
286
+ - 3686d9e: Configuring a node is simpler. The configuration itself gets the dialog, with
287
+ mapping and testing available on call rather than competing for the same space,
288
+ and the separate outputs strip is gone — the third tab is **Output**. A setting
289
+ the last save refused is marked as such, so a rejected save says which field it
290
+ was about, and the dialog is sized to its content instead of leaving the fold to
291
+ hide a control. Switching away from a tab the bar no longer draws leaves nothing
292
+ selected behind.
293
+ - 3686d9e: You can say when a workflow runs without writing cron. The schedules page offers
294
+ the choice directly, a schedule can fire several times a day, and every schedule
295
+ states when it next fires instead of showing an expression and leaving you to read
296
+ it.
297
+
298
+ The reading is exact rather than approximate: a step that does not divide its
299
+ field is no longer described as a plain interval, and a named weekday no longer
300
+ widens a schedule to the whole week.
301
+
302
+ - 3686d9e: A webhook trigger can say who is allowed to call it. Protection profiles are
303
+ offered on the trigger, the secret behind one can be rotated, and exposing an
304
+ endpoint persists the default profile rather than leaving the webhook open until
305
+ somebody sets one.
306
+
307
+ ### Patch Changes
308
+
309
+ - 3686d9e: The node inspector's credential picker separates the credential's name from its
310
+ state, and reports the state it was actually told rather than deriving one. A
311
+ deleted credential no longer reads as though none had been chosen — the dead
312
+ reference is announced, not only drawn — and a failed test keeps a readable
313
+ reason.
314
+ - 3686d9e: **Tidy up** no longer leaves a node's outputs crossing each other. After an
315
+ auto-layout the out and error branches keep to their own lanes, and where one
316
+ output fans out to several nodes the edge bends are staggered — mirrored on both
317
+ sides — so sibling branches read alike instead of overlapping into a single
318
+ line.
319
+ - 3686d9e: The hover toolbar no longer appears on canvases you cannot edit. On a run replay
320
+ or any other read-only canvas, **Duplicate** and **Delete** used to show up on
321
+ hover and do nothing; they are now absent instead of dead.
322
+ - 3686d9e: **Tidy up** no longer routes a node's ordinary output and its error output across
323
+ each other. Branches are re-slotted so the two leave the spine without crossing,
324
+ and where the current order gives no answer the target node's id breaks the tie —
325
+ so the same workflow tidies up the same way every time.
326
+ - 3686d9e: A node that ends the run no longer draws an output port. Stop and error nodes
327
+ declare no outputs, so the canvas gave them a port that could never lead
328
+ anywhere; that port is gone, and nothing can be connected to it by accident.
329
+ - 3686d9e: The actions button on a workflow row has an accessible name. It is icon-only and
330
+ sits in a row that offers nothing else to address it by, so it now names the
331
+ workflow it acts on — `Actions for <workflow>` — instead of being unreachable to
332
+ anyone not using a pointer.
333
+ - 3686d9e: Every form field and icon-only control in the studio has an accessible name, and
334
+ each page declares its main content landmark. The skip link now moves focus
335
+ rather than only changing the address, so it actually skips something, and the
336
+ icon picker keeps the name of the popover it opens from.
337
+ - 3686d9e: **Delete** and **Backspace** no longer delete nodes while a dialog or panel
338
+ covers the canvas. The canvas stands its keyboard handling down for as long as an
339
+ overlay is open, so a keystroke meant for the text field in front of you cannot
340
+ remove a node behind it.
341
+ - 3686d9e: The validation banner is summarised per node, and the nodes it names are marked
342
+ on the canvas — so a workflow that will not activate points at where the problem
343
+ is instead of listing findings you have to match up yourself. A finding the
344
+ platform attributes to no node carries only its message rather than being pinned
345
+ to an arbitrary one. Marks are held back while a node is being dragged, so the
346
+ re-sync no longer interrupts the drag.
347
+ - 3686d9e: Running an incomplete workflow names the settings that are missing, the way the
348
+ activation gate already did — instead of a refusal you have to go looking for the
349
+ cause of. A finding the platform attributes to no node is named rather than only
350
+ counted, and a refusal that arrives with nothing readable in it no longer leaves
351
+ the user with no message at all.
352
+ - 3686d9e: A credential's test result is no longer wiped by the reload that follows it. The
353
+ result you just produced stays on screen instead of disappearing the moment the
354
+ list refreshes, and a prototype key is no longer offered as though it were a
355
+ credential type.
356
+ - 3686d9e: The template gallery says which kind of nothing it is showing — no templates at
357
+ all reads differently from nothing matching your search. A template's vendor can
358
+ be searched for, and three names that the detail page relied on but never put on
359
+ screen are now there, so what a template arrives wired with is readable before
360
+ you install it.
361
+ - 3686d9e: The runs and schedules pages stop reporting figures they did not have. Missed
362
+ runs were collected before the workflows they belong to had arrived, so they came
363
+ out short; they are now collected once the list is in. Leaving the page no longer
364
+ takes the count with it, so coming back shows what ran, what runs next and what
365
+ never got to run rather than an empty summary.
366
+ - 3686d9e: Two controls on the workflows list no longer claim a name derived from data that
367
+ had not loaded, so they are addressable as soon as the page is — including the
368
+ route to the create-workflow dialog, which sits behind two doors and had nothing
369
+ stable to reach it by.
370
+ - 3686d9e: The audit log pages properly and closes properly. A second pager sat behind the
371
+ first and could disagree with it; there is now one. Searching for a person finds
372
+ them through the actor column rather than depending on the resource column
373
+ repeating the same value, and the one way of closing an opened record that never
374
+ worked now does.
375
+ - 3686d9e: Credential dialogs no longer come up empty for a managed type. The managed types
376
+ ask whether the cockpit is actually there instead of assuming it, so the fields
377
+ are rendered when the source can answer and the dialog says what it cannot offer
378
+ when it cannot.
379
+ - 3686d9e: The **Created** and **Updated** columns on the secrets list show something. They
380
+ were always empty; they now carry the dates the service records, a cell with no
381
+ date claims no timestamp rather than rendering an epoch, and the dates stay out
382
+ of the search box so filtering by name is not confused by them.
383
+ - 3686d9e: Text that arrives from the API is localised by one resolver instead of fifteen
384
+ private copies, so a label resolves the same way wherever it is shown and a
385
+ missing translation falls back consistently rather than differently per page.
386
+ - 3686d9e: Pages no longer render empty data while their load is still in flight. The ten
387
+ composables that each guarded their own load now share one guard that waits for
388
+ the load already running instead of handing back an empty result, so a second
389
+ caller is answered once the data is there. The workflows list stops claiming
390
+ "Never run" when the runs simply had not arrived yet.
391
+ - 3686d9e: The dashboard's figures are there on the first visit rather than after a reload.
392
+ **Needs attention** reports the state now instead of every failure the workflow
393
+ has ever had, each figure that answers a click is a link and says its number, and
394
+ a request that is counted is recounted when it changes rather than going quiet.
395
+ - 3686d9e: A failed run keeps the reason the platform recorded, and shows the moment the
396
+ failure happened rather than the moment the run started. Where the platform left
397
+ the reason empty the run says so, instead of presenting the blank as though
398
+ nothing had gone wrong.
399
+ - 3686d9e: The dashboard's templates panel is named for what it offers rather than for a
400
+ ranking it never measured. It was headed "Popular templates" while showing no
401
+ measure of popularity; it now reads **Start from a template**.
402
+ - 3686d9e: A connection can only leave an output and arrive at an input. Two input ports
403
+ could be joined to each other and the result saved, producing a workflow the
404
+ platform would never run; that is refused now. Connections already saved the wrong
405
+ way round are not repaired by this change — they have to be redrawn.
406
+ - 3686d9e: Template categories are named from one source, so the filter and the cards cannot
407
+ disagree, and the category list names the two categories that actually ship rather
408
+ than one nobody could select. A template is presented by a single card component in
409
+ two densities instead of two implementations that had drifted apart, and what
410
+ choosing a card does is defined once.
411
+ - 3686d9e: An audit entry opened from a row holds the keyboard and gives it back when it
412
+ closes. The panel used to leave focus in the row behind it, so the keyboard was
413
+ somewhere the user could no longer see.
414
+ - 3686d9e: The template gallery says it is loading in words, not only as a shimmer, so the
415
+ wait is announced rather than being visible only to the eye — and a loading
416
+ gallery no longer looks exactly like an empty one. The announcement and the
417
+ placeholders are derived from the same condition, and the wait ends in words too.
418
+ - 3686d9e: A port name comes back whole. A name containing `__` was cut at the separator, and
419
+ a handle id with no port name in it produced an empty string that read as a real
420
+ name; the first round-trips intact now and the second answers with nothing at all.
421
+ - 3686d9e: The webhook and event trigger copy spells it **catalogue**, matching the rest of
422
+ the studio's British English — in the loading message, the payload hint and the
423
+ line shown when a topic has no sample payload.
424
+ - 3686d9e: Saving a credential says what happened. A save that is refused states its reason
425
+ — in the dialog and at the field it concerns, where a scrolling form cannot hide
426
+ it — rather than the service's count of the other fields, and it reaches every
427
+ field it names. A save that lands closes the dialog. A refused edit says why too,
428
+ instead of failing silently.
package/README.md CHANGED
@@ -30,6 +30,33 @@ supply a token/tenant through the platform provider (override
30
30
  `usePlatformAuth` / `usePlatformTenant`, or the standalone defaults read
31
31
  `NUXT_PUBLIC_DEV_TOKEN` / `NUXT_PUBLIC_DEV_TENANT`).
32
32
 
33
+ ### Language
34
+
35
+ The studio speaks English and German. That covers both halves of a screen: the
36
+ text node packages publish (names, descriptions, settings) **and** the studio's
37
+ own chrome — headings, buttons, tabs, empty states, toasts. Both fall back to
38
+ English. The studio has no switcher of its own, so hand the choice over one of
39
+ two ways:
40
+
41
+ ```ts
42
+ // nuxt.config.ts — a language that does not change
43
+ export default defineNuxtConfig({
44
+ studioIntegrations: { locale: 'de' }, // or NUXT_PUBLIC_STUDIO_INTEGRATIONS_LOCALE
45
+ })
46
+ ```
47
+
48
+ ```ts
49
+ // a host plugin — a language the user switches while the app is running
50
+ const { setLocale } = useIntegrationsLocale()
51
+ watch(hostLocale, l => setLocale(l), { immediate: true })
52
+ ```
53
+
54
+ A switch reaches surfaces that are already open; nothing is refetched. With
55
+ neither wired, the studio is English. The module carries its own message
56
+ catalogue (`runtime/i18n/messages/*.json`) — it never reads the host's, and needs
57
+ no i18n library installed in the host. See
58
+ [`docs/interface-language.md`](docs/interface-language.md).
59
+
33
60
  ## ⚠️ Tailwind: you MUST scan this package
34
61
 
35
62
  The module ships its runtime as source-ish `.vue`; Tailwind generates the
@@ -60,8 +87,13 @@ cockpit monorepo under `packages/studio-integrations`).
60
87
  pnpm install # runs the module `prepare` (nuxt-module-build)
61
88
  pnpm test # vitest
62
89
  pnpm build # dist/
90
+ pnpm changeset # record what your change does to the version
63
91
  ```
64
92
 
93
+ Branch off `main` as `feature/<desc>`, `fix/<desc>` or `chore/<desc>` — GitHub refuses
94
+ the first push of any other name, and `main` itself takes a reviewed pull request with
95
+ CI green: [`docs/branch-protection.md`](docs/branch-protection.md).
96
+
65
97
  Hosts to develop against:
66
98
 
67
99
  * **Playground** (`playground/`, in this repo): thin standalone Nuxt host that
@@ -77,7 +109,19 @@ Hosts to develop against:
77
109
 
78
110
  ## Release
79
111
 
80
- Bump `version` in `package.json` and push to `main` — CI
81
- (`.github/workflows/publish.yml`) publishes any version that isn't on npm
82
- yet, via npm Trusted Publishing (the trusted publisher on npmjs is bound to
83
- this repo + workflow file).
112
+ Versioning is driven by [Changesets](https://github.com/changesets/changesets),
113
+ so `version` in `package.json` is never edited by hand. Every pull request
114
+ records what it does to the version — CI fails without it:
115
+
116
+ ```bash
117
+ pnpm changeset # pick patch/minor/major, then write the note
118
+ pnpm changeset --empty # …for a PR that intentionally releases nothing
119
+ ```
120
+
121
+ Releasing is then merging a pull request. Pushes to `main` that carry changesets
122
+ make CI open a **"Version Packages"** PR with the bump and `CHANGELOG.md`
123
+ applied; merging that PR publishes to npm (Trusted Publishing over OIDC, no
124
+ token) and notifies the cockpit.
125
+
126
+ Full policy, the SemVer contract, and why publishing is deliberately not
127
+ `changeset publish`: [`docs/versioning.md`](docs/versioning.md).
package/dist/module.d.mts CHANGED
@@ -1,6 +1,22 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
  export * from '../dist/runtime/types/integrations.js';
3
3
 
4
- declare const _default: _nuxt_schema.NuxtModule<_nuxt_schema.ModuleOptions, _nuxt_schema.ModuleOptions, false>;
4
+ interface ModuleOptions {
5
+ /**
6
+ * The language the studio renders in, as a BCP-47 tag (`en`, `de`, `de-CH`).
7
+ *
8
+ * This is the *initial* language and the whole of the contract for a host
9
+ * whose language never changes. A host with a switcher leaves it alone and
10
+ * calls `useIntegrationsLocale().setLocale()` instead — its plugins run after
11
+ * this module's, so the two do not fight over the value.
12
+ *
13
+ * It reaches the runtime through `runtimeConfig.public.studioIntegrations`,
14
+ * so a deployment can override it with
15
+ * `NUXT_PUBLIC_STUDIO_INTEGRATIONS_LOCALE` without a rebuild.
16
+ */
17
+ locale?: string;
18
+ }
19
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
5
20
 
6
21
  export { _default as default };
22
+ export type { ModuleOptions };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@revenexx/studio-integrations",
3
3
  "configKey": "studioIntegrations",
4
- "version": "0.5.1",
4
+ "version": "0.7.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { defineNuxtModule, createResolver, hasNuxtModule, installModule, addComponentsDir, addImportsDir, extendPages } from '@nuxt/kit';
3
+ import { defineNuxtModule, createResolver, addPlugin, hasNuxtModule, installModule, addComponentsDir, addImportsDir, extendPages } from '@nuxt/kit';
4
4
 
5
5
  function toSegment(name) {
6
6
  if (name.startsWith("[...")) return `:${name.slice(4, -1)}(.*)*`;
@@ -27,9 +27,16 @@ const module$1 = defineNuxtModule({
27
27
  name: "@revenexx/studio-integrations",
28
28
  configKey: "studioIntegrations"
29
29
  },
30
- async setup(_options, nuxt) {
30
+ defaults: {
31
+ locale: "en"
32
+ },
33
+ async setup(options, nuxt) {
31
34
  const { resolve } = createResolver(import.meta.url);
32
35
  const runtime = resolve("./runtime");
36
+ const publicConfig = nuxt.options.runtimeConfig.public;
37
+ const configured = publicConfig.studioIntegrations ?? {};
38
+ publicConfig.studioIntegrations = { ...configured, locale: configured.locale ?? options.locale };
39
+ addPlugin(resolve("./runtime/plugins/locale"));
33
40
  if (!hasNuxtModule("@solar-icons/nuxt", nuxt)) {
34
41
  await installModule("@solar-icons/nuxt", {
35
42
  namePrefix: "Solar",
@@ -52,6 +59,7 @@ const module$1 = defineNuxtModule({
52
59
  nuxt.options.build.transpile.push(runtime);
53
60
  addComponentsDir({ path: join(runtime, "components"), pathPrefix: true });
54
61
  addImportsDir(join(runtime, "composables"));
62
+ addPlugin(join(runtime, "plugins", "realtime.client"));
55
63
  extendPages((pages) => {
56
64
  const pagesDir = join(runtime, "pages", "integrations");
57
65
  if (!existsSync(pagesDir)) return;