@revenexx/studio-integrations 1.3.0 → 1.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +426 -0
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/components/Integrations/CanvasShortcutsDialog.d.vue.ts +9 -0
  4. package/dist/runtime/components/Integrations/CanvasShortcutsDialog.vue +53 -0
  5. package/dist/runtime/components/Integrations/CanvasShortcutsDialog.vue.d.ts +9 -0
  6. package/dist/runtime/components/Integrations/EditorTip.vue +2 -15
  7. package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +10 -0
  8. package/dist/runtime/components/Integrations/NodeConfigField.vue +94 -49
  9. package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +10 -0
  10. package/dist/runtime/components/Integrations/NodeInspector.vue +37 -60
  11. package/dist/runtime/components/Integrations/RunDetailPanel.vue +80 -33
  12. package/dist/runtime/components/Integrations/TemplateCard.d.vue.ts +0 -4
  13. package/dist/runtime/components/Integrations/TemplateCard.vue +3 -6
  14. package/dist/runtime/components/Integrations/TemplateCard.vue.d.ts +0 -4
  15. package/dist/runtime/components/Integrations/TemplateInstallStep.d.vue.ts +25 -0
  16. package/dist/runtime/components/Integrations/TemplateInstallStep.vue +198 -0
  17. package/dist/runtime/components/Integrations/TemplateInstallStep.vue.d.ts +25 -0
  18. package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +2 -2
  19. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +22 -40
  20. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +2 -2
  21. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +94 -65
  22. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +36 -2
  23. package/dist/runtime/components/Integrations/WorkflowEditor.vue +165 -56
  24. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +36 -2
  25. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +6 -2
  26. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +4 -106
  27. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +6 -2
  28. package/dist/runtime/components/Integrations/WorkflowsTable.vue +22 -11
  29. package/dist/runtime/composables/useCanvasShortcuts.d.ts +34 -0
  30. package/dist/runtime/composables/useCanvasShortcuts.js +11 -8
  31. package/dist/runtime/composables/useCredentialTypes.js +3 -9
  32. package/dist/runtime/composables/useIntegrationsCredentials.js +6 -14
  33. package/dist/runtime/composables/useIntegrationsNodes.js +2 -2
  34. package/dist/runtime/composables/useIntegrationsRuns.js +8 -13
  35. package/dist/runtime/composables/useIntegrationsSecrets.js +5 -5
  36. package/dist/runtime/composables/useIntegrationsTemplates.js +5 -8
  37. package/dist/runtime/composables/useIntegrationsTriggers.js +1 -4
  38. package/dist/runtime/composables/useIntegrationsWorkflows.js +5 -7
  39. package/dist/runtime/data/canvasShortcuts.d.ts +208 -0
  40. package/dist/runtime/data/canvasShortcuts.js +68 -0
  41. package/dist/runtime/i18n/messages/de.json +30 -16
  42. package/dist/runtime/i18n/messages/en.json +30 -16
  43. package/dist/runtime/pages/integrations/templates/[slug].vue +22 -132
  44. package/dist/runtime/pages/integrations/workflows/[id].vue +128 -33
  45. package/dist/runtime/utils/apiEnvelope.d.ts +27 -0
  46. package/dist/runtime/utils/apiEnvelope.js +20 -0
  47. package/dist/runtime/utils/configDefaults.d.ts +18 -0
  48. package/dist/runtime/utils/configDefaults.js +7 -0
  49. package/dist/runtime/utils/errorPathAdvice.d.ts +60 -0
  50. package/dist/runtime/utils/errorPathAdvice.js +19 -0
  51. package/dist/runtime/utils/notify.d.ts +2 -2
  52. package/dist/runtime/utils/runEnvelope.d.ts +12 -0
  53. package/dist/runtime/utils/runEnvelope.js +2 -1
  54. package/dist/runtime/utils/selectOptions.d.ts +48 -0
  55. package/dist/runtime/utils/selectOptions.js +19 -0
  56. package/dist/runtime/utils/shortcutLabel.d.ts +40 -0
  57. package/dist/runtime/utils/shortcutLabel.js +48 -0
  58. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,431 @@
1
1
  # @revenexx/studio-integrations
2
2
 
3
+ ## 1.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 388babd: Installing a template takes the same step from both routes into it (PO-244).
8
+
9
+ A template could be installed from two places, and the two gave the user different
10
+ workflows. From the "New workflow" dialog one press of a card installed it there and
11
+ then: the copy arrived under the template's own name, bound to none of the tenant's
12
+ credentials, with nothing said about either — and a refusal came back as a message
13
+ that disappeared. From the template's own page the same act named the copy first,
14
+ offered a credential per place that needs one, listed the secrets it references as
15
+ stored or missing, and kept a refusal on screen with what had been entered. Nobody
16
+ had decided that; the richer route simply arrived later.
17
+
18
+ The dialog's card now opens that same step in place of the list of templates, with
19
+ the way back to the list beside the name. Both routes therefore create the same
20
+ workflow, and it is created when the user confirms rather than when they press a
21
+ card. The step is one component reached from both places, so what it offers cannot
22
+ drift apart again, and while it works out what the template needs it says so —
23
+ "needs no credentials" and "not asked yet" used to look the same.
24
+
25
+ Two things a host should know. **Pressing a card no longer creates anything**, so a
26
+ test or a script that pressed one and waited for the editor now has to confirm the
27
+ step. And the template card has lost its `busy` and `disabled` props, which existed
28
+ for the install that used to start on the card itself; nothing acts on a card any
29
+ more.
30
+
31
+ - 79f42db: The run replay replays the run it was handed, and nothing else (PO-350).
32
+
33
+ The replay carried a second mode, switched by a `single` prop: a 320 px sidebar
34
+ listing the workflow's runs to browse, beside the canvas. No route in the studio
35
+ ever mounted it — the run record took the replay over and passed `single` from the
36
+ day it did — so what shipped was a surface with a defect nobody could reach: asked
37
+ for a run it did not find among the most recent ones, it replayed the newest run
38
+ instead, under a label naming the newest run. A host mounting the component the way
39
+ its own prop documented got that, quietly.
40
+
41
+ The mode is gone. The replay now walks the run it is given, whatever else the
42
+ workflow has run, and with no run given it says so rather than choosing one. Runs
43
+ are browsed on the studio's own runs table, which filters, pages and offers the row
44
+ actions the sidebar never had — and the replay no longer asks for a run list it had
45
+ no use for.
46
+
47
+ `single` is still accepted and now ignored, so a host passing it keeps working; it
48
+ is deprecated and will be dropped at the next major. The four `runsList` strings
49
+ and the run-list failure message leave with the mode.
50
+
51
+ Two more strings stay but stop pointing at it: a panel given no run to replay used
52
+ to answer _Pick a run on the right to replay it._, where the right-hand side was
53
+ that sidebar. It now says _No run to replay._ — the state, rather than an
54
+ instruction with nothing to follow it.
55
+
56
+ - 3ddba20: A workflow that leaves the run mode to its caller is asked which one, instead of
57
+ being run asynchronously (PO-428).
58
+
59
+ Pressing Run in the editor opened a dialog only where the workflow read something
60
+ from its trigger payload. A workflow whose execution mode is **Caller decides** and
61
+ whose nodes read no payload therefore never reached the offer at all: it was started
62
+ in the mode that does not wait, and a run whose result the author wanted to wait for
63
+ could not be started by hand. It now opens the same dialog, with both modes on it.
64
+ `mod+enter` follows the button, as it always has.
65
+
66
+ The dialog is named after the run rather than after the payload: **Run workflow**,
67
+ "Starts a manual run of this workflow. What it returns is shown here when it ends."
68
+ The payload is a field on it and names itself. The old description also still
69
+ promised the result on the Runs tab, which stopped being where it appears when the
70
+ dialog began showing what the run returned.
71
+
72
+ Where both modes are offered, the one that waits for the result is now the suggested
73
+ one, on the right, and it stays the suggested one however the last run was started —
74
+ the offer follows the workflow, not the last press. The mode words on the buttons are
75
+ unchanged.
76
+
77
+ Promised behaviour: `specs/run-from-editor.md` AC-1, AC-2 and AC-10.
78
+
79
+ - 1cb60f7: Every key the workflow canvas answers can be read in one place, and one of them
80
+ could not be pressed on a German keyboard (PO-431).
81
+
82
+ **Press `?` on the canvas** for a list of its keys, grouped and written the way your
83
+ own keyboard is labelled. The canvas answers two dozen shortcuts, and until now
84
+ learning them meant hovering thirteen controls and right-clicking three different
85
+ things — two of them, activating the workflow and renaming a node with Space, were
86
+ shown nowhere at all.
87
+
88
+ **Zooming in now works on every layout.** The binding was `=`, which a German
89
+ keyboard types as Shift+0 — and a Shift the layout requires to produce a character
90
+ was being counted as part of the shortcut, so the key answered on a US keyboard and
91
+ on no other. A character key is now reached however a layout types it, while a
92
+ shortcut that means Shift over a letter still needs Shift held. **Zooming in is now
93
+ `+`** — the key a German keyboard has of its own and a US one reaches with Shift; the
94
+ old `=`, which only ever worked on a US layout, is gone rather than kept as a second
95
+ undocumented way in. Zooming out is `-`, as before.
96
+
97
+ **A modifier is spelled the way its platform spells it.** The command and option keys
98
+ already followed the platform; the shift key was the glyph ⇧ everywhere, so a Windows
99
+ or Linux reader got `Ctrl+⇧+G`. On those platforms it now reads `Ctrl+Shift+G`; on a
100
+ mac nothing changes.
101
+
102
+ Behind all three: the keys live in one list that the overview, the canvas's context
103
+ menu and the control pane's tooltips all read, so a key cannot be shown in one place
104
+ and answered in another. The menu had already drifted — it offered renaming as `F2`
105
+ for a key that answers `Space` too.
106
+
107
+ Promised behaviour: `specs/canvas-keyboard-shortcuts.md` AC-4, AC-5 and AC-6.
108
+
109
+ - 4aa26a6: The editor points out an error output nobody connected, and attaches the ending
110
+ our own templates use (PO-443).
111
+
112
+ **A step that fails on a path nobody drew is not handled.** Most nodes have a red
113
+ error output; if the author never drew a line out of it, a failure there is not
114
+ carried anywhere. The platform now ends the run on it — but by then the workflow
115
+ has been running unattended for weeks. This is the half that says so while it is
116
+ still being drawn.
117
+
118
+ **A slate strip above the canvas names each step whose error output leads
119
+ nowhere**, and a line jumps to that step on the canvas. Deliberately not amber:
120
+ amber already means "fix this before it runs" in this editor twice over, and this
121
+ is not that.
122
+
123
+ **It blocks nothing.** Not activation, not saving, not starting a run. That is the
124
+ whole point of it being advice: the same fact expressed as a validation finding
125
+ would have closed the activation gate — and the run gate — on every workflow that
126
+ has an unconnected error output, including ones that have run nightly for months.
127
+ The rule is therefore computed in the editor, where no gate can read it.
128
+
129
+ **One click attaches the ending.** _Stop the run on error_ drops a Stop and Error
130
+ step below the node, wires it to the error output, and pre-fills its message the
131
+ way all eight shipped templates write theirs — the failing step by name, then its
132
+ own reason quoted out of the port that carried it. Nothing is saved until the
133
+ author saves; deleting the step undoes it.
134
+
135
+ A step nobody has connected yet is left alone, so laying out a workflow before
136
+ wiring it does not produce a wall of complaints. A step whose ports are named only
137
+ once its settings resolve is left alone too — nothing is claimed about an output
138
+ the declaration cannot name.
139
+
140
+ Promised behaviour: `specs/workflow-advice.md`, a new spec that owns what the
141
+ editor says about a workflow it has no objection to. `specs/workflow-validation.md`
142
+ keeps the gate, and the two now say so about each other.
143
+
144
+ - 1a1edc3: A node's settings keep the order it declares them in, and only the tail is
145
+ folded (PO-463).
146
+
147
+ The node configuration dialog used to draw every required setting first and fold
148
+ every optional one away. That discarded the order the node author chose and made
149
+ requiredness stand for importance, which it cannot: the API refuses a required
150
+ setting that is empty, so a setting that is central and legitimately empty — Text
151
+ Replace's replacement, whose empty value is how a search value is deleted — had
152
+ to be optional, and therefore disappeared. Text Replace showed one of its six
153
+ settings, and not the one it is named after.
154
+
155
+ The Parameters tab now draws the settings a node declares as one list, in that
156
+ order. Four or fewer are all shown; beyond four the first three are always shown
157
+ and, from the fourth on, a setting is shown when it is required and folded when
158
+ it is not. The fold keeps its single control and its count, and now stands at the
159
+ first setting it hides, so opening it puts the tail back where the node declared
160
+ it. Counted over the 51 published core node types, 157 of 203 settings reach the
161
+ open form where 103 did before, and no node shows fewer than it did.
162
+
163
+ Nothing is required of a node package: the lever is the order a node already
164
+ declares. A node whose central setting still sits past the third — HTTP Request's
165
+ body, Send Email's two bodies — moves it up in its own manifest.
166
+
167
+ - 633f3bf: The workflows list says whether it is grouped by tag, and now promises what
168
+ grouping does (PO-468).
169
+
170
+ Grouping the list by tag was offered by a button that read "Group by tags"
171
+ whether or not the list was already grouped — so pressing it once more ungrouped
172
+ a list whose control still invited grouping, and the only sign of the state was
173
+ the button's colour. It is now a switch: it shows whether grouping is on, to a
174
+ reader and to a screen reader alike.
175
+
176
+ Two behaviours the list already had are promises from here on, with tests behind
177
+ them: the grouping an operator chose is the one the list arrives in on the next
178
+ visit, kept in that browser alone, and a workflow carrying several tags stands
179
+ under each of them, with the untagged ones together under "No tags" at the end.
180
+ No shipped string changes.
181
+
182
+ - 652a08f: Every canvas now zooms between the same two ends, and the editor no longer offers a
183
+ reach that shows nothing (PO-470).
184
+
185
+ The editor went to four times full size while the run replay and the history preview
186
+ stopped at twice. That was never decided: all three stopped at twice until the
187
+ editor's zoom picker was given a 400% preset and its ceiling was raised to match.
188
+ Asked what the extra reach was for, the answer turned out to be nothing — a step's
189
+ card carries a name, a version and a mark, and at four times full size a single card
190
+ fills the canvas without saying more than it did at half that.
191
+
192
+ So the editor comes down to twice full size, where the other two canvases already
193
+ were, and the 300% and 400% entries leave its zoom menu. The menu gains 25%, the
194
+ level the wheel could always reach and the list never offered — it now spans the
195
+ whole range, ends included. Anything typed into the zoom field beyond the range
196
+ still lands at the range's end rather than being refused.
197
+
198
+ Three criteria hold it from here on: the range on all three canvases, the clamping,
199
+ and the six levels the menu offers — 25%, 50%, 75%, 100%, 150% and 200%. That last
200
+ one is named rather than described on purpose: adding, removing or moving a level
201
+ changes what the editor offers an author, and it should take a decision rather than
202
+ a tweak.
203
+
204
+ ### Patch Changes
205
+
206
+ - 2b6af97: Three copy-catalogue entries have their source back (PO-244).
207
+
208
+ The catalogue records where each shipped string is used, and recomputing those
209
+ references after the template install step moved lost three of them: the
210
+ publisher badges — Official, Partner, Community — are looked up through a map
211
+ rather than named in a `t()` call, so a scan for `t('…')` did not find them. A
212
+ unit test guards exactly that, and it is why this was caught on `main` rather
213
+ than by a reader finding an entry that points nowhere. No shipped string changes.
214
+
215
+ - aff19fa: One answer to whether a response carries a `data` envelope, checked against the
216
+ service (PO-394).
217
+
218
+ Nine expressions in five composables each decided for themselves whether the
219
+ Integrations API had wrapped a single record, with a spelling that worked either
220
+ way — so nothing recorded which endpoints wrap, and a response in an unexpected
221
+ shape was handed to the caller as the envelope itself and drawn as empty fields.
222
+ They now share `unwrapRecord` and `unwrapList` in `src/runtime/utils/`, which
223
+ carry the service's rule as a table read from its controller bodies: **a
224
+ collection is wrapped in `data`, a single record is bare** — with templates as
225
+ the exception, because there the envelope follows the presenter rather than the
226
+ shape. A record that arrives in neither shape now fails visibly instead of
227
+ rendering blank, and the message a person sees is the page's own copy.
228
+
229
+ No promised behaviour changes. Three routes in the test mock answered a wrapped
230
+ record where the service answers a bare one — one of them contradicting a
231
+ contract this repository had signed a fortnight earlier — and now answer as the
232
+ service does; the envelope question is signed for the templates, workflows and
233
+ runs surfaces in `e2e/support/api-contract.json`.
234
+
235
+ - 2db01ec: A step in a run's timeline opens on what it was handed and what it gave back (PO-421).
236
+
237
+ The **Show payloads** button is gone. It was a fetch switch dressed as a display
238
+ switch: a step opened on an instruction to press it, and pressing it re-read every
239
+ step of the run with its payloads, whether or not the operator went on to open a
240
+ second one. Opening a step now reads them itself, once, and says so while they are
241
+ on their way.
242
+
243
+ What comes back is kept per step scheduling and merged into the reads that keep a
244
+ live run current, so the three-second poll neither blanks an open step nor goes back
245
+ to storage for payloads it already has. A step opened while it was still running is
246
+ read once more when it ends, and only then — the status a payload was read in is
247
+ what decides, so a step that legitimately returned nothing is not re-read on every
248
+ poll, and a read that failed is not retried three seconds later.
249
+
250
+ A failed payload read now leaves the timeline standing and says so inside the open
251
+ step. Before, it shared the read that draws the list of steps, which empties that
252
+ list when it fails.
253
+
254
+ Promised behaviour: `specs/run-detail.md` AC-7, and AC-15 for what an open step keeps
255
+ while its run goes on.
256
+
257
+ - 122659e: An option that stands for no choice can be chosen, and no longer takes the node
258
+ dialog with it (PO-422).
259
+
260
+ A node may offer "no choice" as an entry in a list — the format the file came in as,
261
+ unchanged, all kinds — by declaring an option with an empty value. Three published
262
+ node types do. The control underneath the studio's lists reserves the empty value for
263
+ "nothing selected" and refused the entry outright: the list did not open, and the
264
+ refusal tore down the dialog's dismissal machinery on its way out. From that click
265
+ on, the next list opened anywhere in the form stayed open — Escape and a click
266
+ outside both did nothing — and the only way out was to abandon the dialog with
267
+ everything half set in it.
268
+
269
+ Such an option is now carried through the list under a substitute value and written
270
+ back as the empty string the node reads. It keeps its own label and its place, and it
271
+ is still there once another option has been chosen, so a setting given a value can be
272
+ put back to no choice. A setting the platform is holding as nothing — the empty string
273
+ comes back as null — reads as that option rather than as a placeholder; a setting the
274
+ workflow holds nothing at all for still reads as the placeholder, because no choice
275
+ has been made.
276
+
277
+ - 107db51: Running a run again, above an open run's record, is offered for a run that failed
278
+ (PO-427).
279
+
280
+ It used to be offered for every run that had ended, a successful one included, under
281
+ the word **Retry** — and the press does not repeat that run: it starts a fresh run of
282
+ the workflow as it is saved now, carrying the old run's trigger payload. Over a run
283
+ that had succeeded, that meant doing everything the run already did to the systems at
284
+ the far end, behind a word saying nothing had happened yet. The button now reads **Run
285
+ again**, says in its title what it really runs, and stands over a failed run only. A
286
+ run's own row still offers it whatever the outcome, from behind its actions menu.
287
+
288
+ A press that the platform accepts now leaves the operator looking at the run it
289
+ started, instead of on the run they had just replaced with only a message naming the
290
+ new one. Stepping back returns to the run that failed, so the two can be compared —
291
+ and because the run just started is not over, the button that started it is gone
292
+ rather than standing ready to start a third.
293
+
294
+ Promised behaviour: `specs/run-detail.md` AC-16 and AC-17.
295
+
296
+ - 4a90a4b: A setting inside a list row is drawn as what it declares, and a new row starts from
297
+ what the node declared (PO-436).
298
+
299
+ A node setting that sits inside a repeating row was always drawn as an empty text
300
+ box, whatever its manifest said: the choices it offers, its default, whether it is
301
+ required, the sentence under it. The manifest schema permits all of that on a nested
302
+ setting and the engine reads it — only the form stopped one level short. Each
303
+ sub-setting is now drawn by the same renderer the rest of the form uses, so a
304
+ declared choice arrives as a list of its named options, a boolean as a switch, an
305
+ expression-capable setting as the mapping control, each with its own description and
306
+ required marker. Every control in a row still says which setting and which row it
307
+ belongs to, so a screen reader can still tell two rows apart.
308
+
309
+ The sharper half was the seeding. A new row was created with an empty string under
310
+ every one of its keys, which is a value rather than an absence — so a node's own
311
+ default could not apply, and the one published node with a choice inside its rows
312
+ (Set Fields) stopped every run with `Unknown operation ""` until the author guessed
313
+ one of three words nothing on screen named. A new row now carries each sub-setting's
314
+ declared default and leaves a setting that has none out of the row entirely.
315
+
316
+ One kind of setting is still withheld inside a row: the kind whose options — or
317
+ whose whole set of settings — the platform is asked for while the dialog is open.
318
+ That request carries the values the setting belongs to, and a row is not those
319
+ values. No published node declares one there.
320
+
321
+ **And a row's settings now answer their own conditions.** A node may say that one setting
322
+ in a row means something only under one of another's choices — Set Fields says it about
323
+ the value it writes and about the path it moves a field to. That condition was read by the
324
+ platform when it judged a saved workflow and by nothing on screen, so both boxes stood
325
+ under every action, each explaining in its own small print when it counted.
326
+
327
+ The condition is read against **the row**, which is where the node wrote it and where the
328
+ platform resolves it: two rows of one list show different settings, each according to its
329
+ own choice. And what was typed into a setting a row withdraws goes with it, in that row
330
+ alone — a value nowhere on screen is a value nobody can correct, and it would otherwise be
331
+ saved and read as though the author had meant it.
332
+
333
+ AC-27 and AC-28 gained the clause, and the gap the spec recorded about a row is closed. A
334
+ group is now the one shape left where a declared condition has nothing to hide.
335
+
336
+ - c0d4083: The view a workflow opens on is fitted to the canvas as it actually is (PO-446).
337
+
338
+ `fitView` reads the canvas out of the DOM, and it was being asked before Vue's
339
+ pending changes had been applied — the band above the canvas that points out an
340
+ unconnected error output among them. So the drawing was placed for a canvas a
341
+ frame out of date, and the same workflow opened at two different zooms depending
342
+ on which side of that frame it landed: measured at `scale(0.717)` with the layout
343
+ settled and `scale(0.626)` without.
344
+
345
+ At the smaller zoom the drawing sits far enough to the left that a step — or a
346
+ connection dot on one — can end up behind the node palette, and a card behind a
347
+ panel takes no pointer at all: it can be seen, and clicking it does nothing. The
348
+ opening view is now placed after the layout has settled, so a workflow opens the
349
+ same way every time.
350
+
351
+ What this does not do is stop a panel covering a card where the drawing genuinely
352
+ reaches under one. That is the older question PO-443 left open — the fit knows
353
+ nothing about what floats over the canvas — and it stays open, with what two
354
+ attempted fixes cost measured in PO-446.
355
+
356
+ - 9f155b3: A run's record offers the addresses in its result whole or not at all: every one of
357
+ them where there are at most fifty, none where there are more. It used to offer the
358
+ first eight and drop the rest without saying so. Promised by `specs/run-detail.md`
359
+ AC-19.
360
+ - 6a9a97f: The workflow editor's header survives a long workflow name (PO-461).
361
+
362
+ Beside a name too long for the room, the badge saying how the workflow
363
+ dispatches was cut off mid-word, and the two status texts next to Save — the
364
+ chip reporting a queued build and the unsaved-changes marker — broke onto a
365
+ second line inside boxes one line high, so their text ran over its own border.
366
+ The breadcrumb was laid out inline, so it sized to its content rather than to
367
+ the heading it sits in and nothing in it ever shrank; the two status texts were
368
+ the only items in the actions row not drawn as buttons, and so the only two that
369
+ could wrap. The name is now the only thing in the header that gives way, and it
370
+ gives way as an ellipsis — with the whole of it on the name itself, so pointing
371
+ at a shortened name shows what was cut.
372
+
373
+ The control that folds the studio navigation away is icon-only while the
374
+ navigation is standing, and carries its words again once it is folded — the way
375
+ back is the direction that needs them. Its accessible name is unchanged in both
376
+ directions, so anything addressing it by name is unaffected.
377
+
378
+ - 7bb3a25: Space renames a node on the canvas, which the Keyboard shortcuts list has been
379
+ promising since it existed (PO-472).
380
+
381
+ **Press `Space` on a selected node** and its configuration opens with the name
382
+ selected, exactly as `F2` does. The list under `?` has offered both keys all along,
383
+ and the second of them answered nothing: the library that draws the canvas binds
384
+ Space as a pan key of its own, on the document, and claimed the keystroke before the
385
+ canvas's own bindings were consulted. That pan key is now switched off, and holding
386
+ Space gains rather than loses: with the key claimed, Space over a drag neither panned
387
+ the canvas nor drew the selection rectangle, so the gesture did nothing at all. The
388
+ one thing that changes is Space held over the scroll wheel, which zooms now instead
389
+ of panning, the way the wheel alone already did.
390
+
391
+ **Every key in that list is now pressed by a test.** The test this shipped past
392
+ read the `Space` cap out of the list and never pressed it — and it was not alone:
393
+ of the twenty-six gestures the list offers, ten were proven by a test that pressed
394
+ them and sixteen were not, `Space` among the sixteen. All sixteen have one now, so
395
+ the surface built so that the list and the keyboard could not disagree is checked in
396
+ the direction that matters.
397
+
398
+ Promised behaviour: `specs/canvas-keyboard-shortcuts.md` AC-7.
399
+
400
+ - 3d07d03: Six behaviours the studio already had are now promises with tests behind them
401
+ (PO-475).
402
+
403
+ Nothing ships differently. What changes is what may change silently afterwards:
404
+ each of these was deliberate in the code, visible on screen, and held by no test,
405
+ so any of them could have gone the day somebody touched the surface.
406
+
407
+ - **A credential cannot be saved without a name**, and picking a system fills one
408
+ in so nobody starts from an empty field. Clearing it puts saving out of reach
409
+ again.
410
+ - **A node's mark is the same wherever the node is named in full** — on the canvas,
411
+ in the header of its own configuration, and in a template's drawing of the flow
412
+ it installs.
413
+ - **A node the catalogue marks with nothing is still marked**, with the same
414
+ generic mark every such node gets. Nothing in today's catalogue is in that state,
415
+ so this had never been seen.
416
+ - **A node whose type this installation does not know still opens.** It says it has
417
+ no configuration, and the settings the engine applies around every node are still
418
+ there — which is what makes a workflow with a withdrawn package repairable.
419
+ - **A double-click zooms the run replay and the history preview.** Neither carries
420
+ a zoom control, so it is the only way in; the editor answers the same gesture by
421
+ opening a node, which is why it turns the zoom off and these two do not.
422
+ - **A group folded shut is drawn open on those two surfaces.** A fold is how an
423
+ author makes their own canvas fit; a replay answers which nodes ran.
424
+
425
+ Promised behaviour: `specs/credential-create.md` AC-10, `specs/node-marks.md` AC-5
426
+ and AC-6, `specs/node-configuration.md` AC-32, `specs/readonly-canvases.md` AC-4,
427
+ `specs/group-frames.md` AC-11.
428
+
3
429
  ## 1.3.0
4
430
 
5
431
  ### 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": "1.3.0",
4
+ "version": "1.4.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ open: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:open": (args_0: boolean) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
7
+ "onUpdate:open"?: ((args_0: boolean) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ export default _default;
@@ -0,0 +1,53 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@revenexx/studio";
4
+ import { CANVAS_SHORTCUTS, SHORTCUT_GROUPS, SHORTCUT_GROUP_LABELS, shortcutCaps } from "../../data/canvasShortcuts";
5
+ import { formatShortcut } from "../../utils/shortcutLabel";
6
+ defineProps({
7
+ open: { type: Boolean, required: true }
8
+ });
9
+ const emit = defineEmits(["update:open"]);
10
+ const t = useIntegrationsT();
11
+ const groups = computed(() => SHORTCUT_GROUPS.map((group) => ({
12
+ group,
13
+ label: t(SHORTCUT_GROUP_LABELS[group]),
14
+ items: CANVAS_SHORTCUTS.filter((s) => s.group === group).map((s) => ({
15
+ id: s.id,
16
+ label: t(s.labelKey),
17
+ // One cap per gesture, in the platform's own alphabet.
18
+ caps: shortcutCaps(s.id).map((k) => formatShortcut(k))
19
+ }))
20
+ })).filter((g) => g.items.length > 0));
21
+ </script>
22
+
23
+ <template>
24
+ <Dialog :open="open" @update:open="(v) => emit('update:open', v)">
25
+ <DialogContent class="max-w-[94vw] sm:max-w-2xl">
26
+ <DialogHeader>
27
+ <DialogTitle>{{ t("integration.canvas.shortcuts.dialog.title") }}</DialogTitle>
28
+ <DialogDescription class="text-sm">
29
+ {{ t("integration.canvas.shortcuts.dialog.description") }}
30
+ </DialogDescription>
31
+ </DialogHeader>
32
+
33
+ <div class="grid max-h-[70vh] gap-x-8 gap-y-5 overflow-y-auto sm:grid-cols-2">
34
+ <section v-for="g in groups" :key="g.group" class="min-w-0">
35
+ <h3 class="mb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
36
+ {{ g.label }}
37
+ </h3>
38
+ <ul class="space-y-1">
39
+ <li v-for="item in g.items" :key="item.id" class="flex items-baseline justify-between gap-3 text-sm">
40
+ <span class="min-w-0 truncate">{{ item.label }}</span>
41
+ <span class="flex shrink-0 items-baseline gap-1">
42
+ <template v-for="(cap, i) in item.caps" :key="cap">
43
+ <span v-if="i > 0" class="text-xs text-muted-foreground">{{ t("integration.canvas.shortcuts.or") }}</span>
44
+ <kbd class="rounded border border-black/[0.08] bg-slate-50 px-1.5 py-0.5 text-xs tabular-nums text-foreground">{{ cap }}</kbd>
45
+ </template>
46
+ </span>
47
+ </li>
48
+ </ul>
49
+ </section>
50
+ </div>
51
+ </DialogContent>
52
+ </Dialog>
53
+ </template>
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ open: boolean;
3
+ };
4
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:open": (args_0: boolean) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
7
+ "onUpdate:open"?: ((args_0: boolean) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ export default _default;
@@ -1,25 +1,12 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
3
  import { Tooltip, TooltipContent, TooltipTrigger } from "@revenexx/studio";
4
+ import { formatShortcut } from "../../utils/shortcutLabel";
4
5
  const props = defineProps({
5
6
  label: { type: String, required: true },
6
7
  shortcut: { type: String, required: false }
7
8
  });
8
- const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "");
9
- const keys = computed(() => {
10
- if (!props.shortcut) return "";
11
- const out = [];
12
- for (const raw of props.shortcut.split("+")) {
13
- const t = raw.toLowerCase();
14
- if (t === "mod") out.push(isMac ? "\u2318" : "Ctrl");
15
- else if (t === "shift") out.push("\u21E7");
16
- else if (t === "alt") out.push(isMac ? "\u2325" : "Alt");
17
- else if (t === "enter") out.push("\u21B5");
18
- else if (t === "escape") out.push("Esc");
19
- else out.push(t.toUpperCase());
20
- }
21
- return isMac ? out.join("") : out.join("+");
22
- });
9
+ const keys = computed(() => props.shortcut ? formatShortcut(props.shortcut) : "");
23
10
  </script>
24
11
 
25
12
  <template>
@@ -21,6 +21,16 @@ type __VLS_Props = {
21
21
  }>;
22
22
  /** Why the last save refused this setting, if it did. */
23
23
  refused?: string;
24
+ /**
25
+ * The 1-based row this setting sits in, when it is a sub-setting of a list
26
+ * row — set only by the list branch below, rendering itself.
27
+ *
28
+ * It is what keeps a repeating control nameable: the visible label repeats
29
+ * per row, so every row's control would otherwise answer to the same name
30
+ * (specs/accessibility.md AC-1). With it, each says which setting AND which
31
+ * row it belongs to.
32
+ */
33
+ rowIndex?: number;
24
34
  };
25
35
  declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
26
36
  set: (key: string, value: unknown) => any;