@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
@@ -0,0 +1,119 @@
1
+ <script setup>
2
+ import { computed, resolveComponent } from "vue";
3
+ import { RefreshCircle as Loader2, ClockCircle as Clock, Global as Webhook, Play, Bolt as Zap } from "#solar-icons";
4
+ import { templateCategoryLabel, templatePackageLabel } from "../../data/integrationTemplates";
5
+ import TemplateFlowPreview from "./TemplateFlowPreview.vue";
6
+ const props = defineProps({
7
+ template: { type: Object, required: true },
8
+ density: { type: String, required: true },
9
+ to: { type: String, required: false },
10
+ busy: { type: Boolean, required: false },
11
+ disabled: { type: Boolean, required: false }
12
+ });
13
+ const emit = defineEmits(["select"]);
14
+ const t = useIntegrationsT();
15
+ const NuxtLink = resolveComponent("NuxtLink");
16
+ const full = computed(() => props.density === "full");
17
+ const banner = computed(
18
+ () => full.value ? props.template.images.find((i) => i.category === "banner")?.src ?? null : null
19
+ );
20
+ const TRIGGER_ICON = { schedule: Clock, webhook: Webhook, manual: Play, event: Zap };
21
+ const TRIGGER_LABEL_KEY = {
22
+ schedule: "integration.templates.card.trigger.schedule",
23
+ webhook: "integration.templates.trigger.webhook",
24
+ manual: "integration.templates.trigger.manual",
25
+ event: "integration.templates.card.trigger.event"
26
+ };
27
+ function triggerTypeLabel(type) {
28
+ const key = TRIGGER_LABEL_KEY[type];
29
+ return key ? t(key) : type;
30
+ }
31
+ function levelDot(level) {
32
+ return level === "beginner" ? "bg-emerald-500" : level === "intermediate" ? "bg-amber-500" : "bg-rose-500";
33
+ }
34
+ const packageLabel = computed(() => templatePackageLabel(props.template));
35
+ const categoryLabel = computed(() => templateCategoryLabel(props.template.category));
36
+ const CHIP = "inline-flex items-center whitespace-nowrap rounded-full uppercase font-medium";
37
+ const chipSize = computed(() => full.value ? "px-2 py-0.5 text-xs" : "px-1.5 py-px text-[11px]");
38
+ </script>
39
+
40
+ <template>
41
+ <component
42
+ :is="to ? NuxtLink : 'button'"
43
+ v-bind="to ? { to } : { type: 'button', disabled: disabled || busy }"
44
+ class="flex flex-col overflow-hidden rounded-lg border border-black/[0.06] bg-white text-left transition hover:border-black/20 hover:shadow-sm disabled:opacity-60"
45
+ @click="to ? void 0 : emit('select')"
46
+ >
47
+ <!-- The picture: the flow this template builds, drawn from its own
48
+ definition, or the publisher's banner where one ships. -->
49
+ <div
50
+ class="relative shrink-0 border-b border-black/[0.06] bg-slate-50/70 [background-image:radial-gradient(circle,rgba(15,23,42,0.07)_1px,transparent_1px)]"
51
+ :class="full ? 'h-36 [background-size:14px_14px]' : 'h-24 [background-size:12px_12px]'"
52
+ >
53
+ <img v-if="banner" :src="banner" alt="" class="h-full w-full object-cover">
54
+ <!-- `full` keeps its chips on the picture, so the drawing starts below
55
+ them: the graph is fitted to whichever axis binds, and a flow slightly
56
+ taller in proportion than this box reaches the top edge — where the
57
+ chip is. The 28px are the chip's own 20 plus its 8 of inset, so the
58
+ two can no longer meet (PO-243).
59
+
60
+ No horizontal padding, deliberately: all three surfaces used to pass
61
+ one (`px-2` in the gallery, `px-1` in the compact places) and none of
62
+ them insetted anything. The preview's `svg` is `absolute inset-0`, so
63
+ it is laid out against its parent's *padding box* and fills it whatever
64
+ the padding is — measured identical to the picture, 421.3px wide either
65
+ way. The graph's own inset is `TemplateFlowPreview`'s `padding` prop,
66
+ 36 logical units, which is where it belongs. -->
67
+ <TemplateFlowPreview
68
+ v-else-if="template.detailLoaded"
69
+ :blob="template.blob"
70
+ :height="full ? 116 : 96"
71
+ :class="full ? 'mt-7' : ''"
72
+ />
73
+ <!-- Named, because "still on its way" and "this template draws nothing"
74
+ are the same sight otherwise — and a spinner nobody can address is a
75
+ promise no test can hold to. -->
76
+ <div
77
+ v-else
78
+ role="status"
79
+ :aria-label="t('integration.templates.card.state.loadingFlow')"
80
+ class="flex h-full items-center justify-center"
81
+ >
82
+ <Loader2 :size="full ? 16 : 14" class="animate-spin text-muted-foreground/50" />
83
+ </div>
84
+
85
+ <div v-if="full" class="absolute left-2 top-2 flex items-center gap-1.5">
86
+ <span v-if="categoryLabel" :class="[CHIP, chipSize, 'bg-slate-100 text-slate-700']">{{ categoryLabel }}</span>
87
+ <span v-if="packageLabel" :class="[CHIP, chipSize, 'bg-violet-100 text-violet-700']">{{ packageLabel }}</span>
88
+ </div>
89
+
90
+ <Loader2 v-if="busy" :size="13" class="absolute right-1.5 top-1.5 animate-spin text-muted-foreground" />
91
+ </div>
92
+
93
+ <div class="flex min-h-0 flex-1 flex-col" :class="full ? 'p-4' : 'p-3'">
94
+ <div class="flex items-center gap-2">
95
+ <div class="min-w-0 flex-1 truncate text-sm font-semibold text-foreground">{{ template.name }}</div>
96
+ <!-- Compact has no room on the picture, so the category rides the name. -->
97
+ <span v-if="!full && categoryLabel" :class="[CHIP, chipSize, 'bg-slate-100 text-slate-700']">{{ categoryLabel }}</span>
98
+ </div>
99
+ <p class="mt-1 flex-1 text-sm text-muted-foreground line-clamp-2">{{ template.description }}</p>
100
+
101
+ <div v-if="full" class="mt-3 flex items-center gap-2 text-xs text-muted-foreground">
102
+ <span
103
+ v-for="triggerType in template.triggerTypes"
104
+ :key="triggerType"
105
+ class="inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-2 py-0.5 uppercase"
106
+ >
107
+ <component :is="TRIGGER_ICON[triggerType] ?? Zap" :size="10" />
108
+ {{ triggerTypeLabel(triggerType) }}
109
+ </span>
110
+ <span class="flex-1" />
111
+ <span class="inline-flex items-center gap-1 whitespace-nowrap rounded-full bg-slate-100 px-2 py-0.5 uppercase">
112
+ <span class="size-1.5 rounded-full" :class="levelDot(template.level)" />
113
+ {{ template.level }}
114
+ </span>
115
+ <span v-if="template.publisher.kind !== 'revenexx'" class="inline-flex items-center rounded-full bg-slate-100 px-2 py-0.5 uppercase">{{ template.publisher.name }}</span>
116
+ </div>
117
+ </div>
118
+ </component>
119
+ </template>
@@ -0,0 +1,18 @@
1
+ import type { WorkflowTemplate } from '../../types/integrations.js';
2
+ type __VLS_Props = {
3
+ template: WorkflowTemplate;
4
+ /** How much room the surface has. See the comment above; no default on purpose. */
5
+ density: 'full' | 'compact';
6
+ /** Given → the card is a link there. Omitted → a button that emits `select`. */
7
+ to?: string;
8
+ /** This card is the one being acted on: it says so and cannot be pressed again. */
9
+ busy?: boolean;
10
+ /** Another card on the surface is being acted on, so this one is out of reach. */
11
+ disabled?: boolean;
12
+ };
13
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
+ select: () => any;
15
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
16
+ onSelect?: (() => any) | undefined;
17
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ export default _default;
@@ -11,9 +11,9 @@ type __VLS_Props = {
11
11
  */
12
12
  showErrorBranches?: boolean;
13
13
  };
14
- declare var __VLS_12: {};
14
+ declare var __VLS_8: {};
15
15
  type __VLS_Slots = {} & {
16
- empty?: (props: typeof __VLS_12) => any;
16
+ empty?: (props: typeof __VLS_8) => any;
17
17
  };
18
18
  declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
19
19
  height: number;
@@ -3,13 +3,15 @@ import { computed } from "vue";
3
3
  import * as Lucide from "lucide-vue-next";
4
4
  import { findManifest, NODE_CATEGORIES, TRIGGER_TEMPLATES } from "../../data/integrationNodeCatalog";
5
5
  import { resolveLocalized } from "../../utils/localize";
6
- import IntegrationsNodeLogo from "./NodeLogo.vue";
6
+ import { describeCron } from "../../utils/schedule";
7
+ import IntegrationsNodeMark from "./NodeMark.vue";
7
8
  const props = defineProps({
8
9
  blob: { type: [Object, null], required: true },
9
10
  height: { type: Number, required: false, default: 144 },
10
11
  padding: { type: Number, required: false, default: 36 },
11
12
  showErrorBranches: { type: Boolean, required: false, default: false }
12
13
  });
14
+ const t = useIntegrationsT();
13
15
  const STOP_SLUG = "revenexx:stop-and-error";
14
16
  const NODE_W = 220;
15
17
  const NODE_H = 58;
@@ -20,20 +22,20 @@ const boxes = computed(() => {
20
22
  if (!blob) return [];
21
23
  const out = [];
22
24
  const triggers = blob.triggers ?? [];
23
- triggers.forEach((t, i) => {
24
- const pos = blob.triggerPositions?.[t.id] ?? { x: 40, y: 40 + i * 160 };
25
- const template = TRIGGER_TEMPLATES.find((tt) => tt.type === t.type);
26
- const subtitle = t.type === "schedule" && t.config?.cron ? String(t.config.cron) : t.type === "webhook" && t.config?.path ? String(t.config.path) : resolveLocalized(template?.description ?? "");
25
+ triggers.forEach((t2, i) => {
26
+ const pos = blob.triggerPositions?.[t2.id] ?? { x: 40, y: 40 + i * 160 };
27
+ const template = TRIGGER_TEMPLATES.find((tt) => tt.type === t2.type);
28
+ const subtitle = t2.type === "schedule" && t2.config?.cron ? describeCron(String(t2.config.cron)) ?? String(t2.config.cron) : t2.type === "webhook" && t2.config?.path ? String(t2.config.path) : resolveLocalized(template?.description ?? "");
27
29
  out.push({
28
- key: `t-${t.id}`,
30
+ key: `t-${t2.id}`,
29
31
  kind: "trigger",
30
32
  x: pos.x,
31
33
  y: pos.y,
32
34
  w: TRIG_W,
33
35
  h: TRIG_H,
34
- label: typeof t.name === "string" && t.name || t.type.charAt(0).toUpperCase() + t.type.slice(1),
36
+ label: typeof t2.name === "string" && t2.name || t2.type.charAt(0).toUpperCase() + t2.type.slice(1),
35
37
  subtitle,
36
- type: t.type,
38
+ type: t2.type,
37
39
  manifest: null,
38
40
  icon: iconFor(template?.icon ?? "Zap")
39
41
  });
@@ -59,9 +61,6 @@ const boxes = computed(() => {
59
61
  });
60
62
  return out;
61
63
  });
62
- function hasLogo(m) {
63
- return (m?.images ?? []).some((i) => i.category === "logo");
64
- }
65
64
  function prettify(slug) {
66
65
  const tail = slug.includes(":") ? slug.split(":").pop() : slug;
67
66
  return tail.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
@@ -100,8 +99,8 @@ const edgePaths = computed(() => {
100
99
  });
101
100
  const EDGE_STROKE = {
102
101
  default: "#94a3b8",
103
- trigger: "#10b981",
104
- error: "#f43f5e"
102
+ trigger: "#94a3b8",
103
+ error: "#cbd5e1"
105
104
  };
106
105
  const bounds = computed(() => {
107
106
  if (!boxes.value.length) return { x: 0, y: 0, w: 1, h: 1 };
@@ -171,7 +170,7 @@ const bounds = computed(() => {
171
170
  </div>
172
171
  </div>
173
172
  <div class="rounded-b-md border-t border-emerald-500/15 bg-emerald-500/[0.04] px-3 py-1 text-mono font-semibold text-emerald-700/80">
174
- Trigger · {{ b.type }}
173
+ {{ t("integration.templates.preview.triggerLabel") }} · {{ b.type }}
175
174
  </div>
176
175
  </div>
177
176
 
@@ -182,10 +181,9 @@ const bounds = computed(() => {
182
181
  >
183
182
  <span
184
183
  class="flex size-8 shrink-0 items-center justify-center rounded-md"
185
- :class="hasLogo(b.manifest) ? 'bg-white ring-1 ring-black/[0.06]' : b.categoryColor"
184
+ :class="b.categoryColor"
186
185
  >
187
- <IntegrationsNodeLogo v-if="hasLogo(b.manifest)" :manifest="b.manifest" :size="18" />
188
- <component :is="b.icon" v-else :size="15" :stroke-width="2.25" />
186
+ <IntegrationsNodeMark :manifest="b.manifest" :size="18" />
189
187
  </span>
190
188
  <div class="min-w-0 flex-1">
191
189
  <div class="truncate text-sm font-semibold leading-tight text-foreground">{{ b.label }}</div>
@@ -195,7 +193,7 @@ const bounds = computed(() => {
195
193
  </foreignObject>
196
194
  </svg>
197
195
  <div v-else class="flex h-full items-center justify-center text-sm text-muted-foreground">
198
- <slot name="empty">Preview loading…</slot>
196
+ <slot name="empty">{{ t("integration.templates.preview.state.loading") }}</slot>
199
197
  </div>
200
198
  </div>
201
199
  </template>
@@ -11,9 +11,9 @@ type __VLS_Props = {
11
11
  */
12
12
  showErrorBranches?: boolean;
13
13
  };
14
- declare var __VLS_12: {};
14
+ declare var __VLS_8: {};
15
15
  type __VLS_Slots = {} & {
16
- empty?: (props: typeof __VLS_12) => any;
16
+ empty?: (props: typeof __VLS_8) => any;
17
17
  };
18
18
  declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
19
19
  height: number;
@@ -17,23 +17,24 @@ const props = defineProps({
17
17
  runBusy: { type: Boolean, required: false }
18
18
  });
19
19
  const emit = defineEmits(["action", "update:open"]);
20
+ const t = useIntegrationsT();
20
21
  const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "");
21
22
  function formatShortcut(tokens) {
22
23
  const parts = tokens.split("+");
23
24
  const out = [];
24
25
  for (const raw of parts) {
25
- const t = raw.toLowerCase();
26
- if (t === "mod") out.push(isMac ? "\u2318" : "Ctrl");
27
- else if (t === "shift") out.push("\u21E7");
28
- else if (t === "alt") out.push(isMac ? "\u2325" : "Alt");
29
- else if (t === "enter") out.push("\u21B5");
30
- else if (t === "backspace") out.push("\u232B");
31
- else if (t === "delete") out.push("Del");
32
- else if (t === "escape") out.push("Esc");
33
- else if (t === "tab") out.push("Tab");
34
- else if (t === "space") out.push("Space");
35
- else if (t === "f2") out.push("F2");
36
- else out.push(t.toUpperCase());
26
+ const t2 = raw.toLowerCase();
27
+ if (t2 === "mod") out.push(isMac ? "\u2318" : "Ctrl");
28
+ else if (t2 === "shift") out.push("\u21E7");
29
+ else if (t2 === "alt") out.push(isMac ? "\u2325" : "Alt");
30
+ else if (t2 === "enter") out.push("\u21B5");
31
+ else if (t2 === "backspace") out.push("\u232B");
32
+ else if (t2 === "delete") out.push("Del");
33
+ else if (t2 === "escape") out.push("Esc");
34
+ else if (t2 === "tab") out.push("Tab");
35
+ else if (t2 === "space") out.push("Space");
36
+ else if (t2 === "f2") out.push("F2");
37
+ else out.push(t2.toUpperCase());
37
38
  }
38
39
  return isMac ? out.join("") : out.join("+");
39
40
  }
@@ -42,19 +43,19 @@ const sections = computed(() => {
42
43
  if (props.kind === "node") {
43
44
  return [
44
45
  [
45
- { id: "open", label: "Open", shortcut: "enter" },
46
- { id: "rename", label: "Rename", shortcut: "f2" }
46
+ { id: "open", label: t("integration.canvas.contextMenu.item.open"), shortcut: "enter" },
47
+ { id: "rename", label: t("integration.canvas.contextMenu.item.rename"), shortcut: "f2" }
47
48
  ],
48
49
  [
49
- { id: "copy", label: "Copy", shortcut: "mod+c", disabled: props.targetIsTrigger },
50
- { id: "duplicate", label: "Duplicate", shortcut: "mod+d", disabled: props.targetIsTrigger }
50
+ { id: "copy", label: t("integration.canvas.contextMenu.item.copy"), shortcut: "mod+c", disabled: props.targetIsTrigger },
51
+ { id: "duplicate", label: t("integration.canvas.contextMenu.item.duplicate"), shortcut: "mod+d", disabled: props.targetIsTrigger }
51
52
  ],
52
53
  [
53
- { id: "run", label: "Run workflow", shortcut: "mod+enter", disabled: props.runBusy },
54
- { id: "tidy-up", label: "Tidy up", shortcut: "shift+alt+t" }
54
+ { id: "run", label: t("integration.canvas.contextMenu.item.runWorkflow"), shortcut: "mod+enter", disabled: props.runBusy },
55
+ { id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: "shift+alt+t" }
55
56
  ],
56
57
  [
57
- { id: "delete", label: "Delete", shortcut: "backspace", danger: true }
58
+ { id: "delete", label: t("integration.common.action.delete"), shortcut: "backspace", danger: true }
58
59
  ]
59
60
  ];
60
61
  }
@@ -62,37 +63,37 @@ const sections = computed(() => {
62
63
  const n = props.selectionCount;
63
64
  return [
64
65
  [
65
- { id: "copy", label: `Copy ${n} nodes`, shortcut: "mod+c" },
66
- { id: "duplicate", label: `Duplicate ${n} nodes`, shortcut: "mod+d" }
66
+ { id: "copy", label: t("integration.canvas.contextMenu.item.copyNodes", n), shortcut: "mod+c" },
67
+ { id: "duplicate", label: t("integration.canvas.contextMenu.item.duplicateNodes", n), shortcut: "mod+d" }
67
68
  ],
68
69
  [
69
- { id: "group", label: `Group ${n} nodes`, shortcut: "mod+g" },
70
- { id: "tidy-up", label: "Tidy up", shortcut: "shift+alt+t" },
71
- { id: "select-all", label: "Select all", shortcut: "mod+a", disabled: allSelected.value },
72
- { id: "clear-selection", label: "Clear selection", shortcut: "escape" }
70
+ { id: "group", label: t("integration.canvas.contextMenu.item.groupNodes", n), shortcut: "mod+g" },
71
+ { id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: "shift+alt+t" },
72
+ { id: "select-all", label: t("integration.canvas.contextMenu.item.selectAll"), shortcut: "mod+a", disabled: allSelected.value },
73
+ { id: "clear-selection", label: t("integration.canvas.contextMenu.item.clearSelection"), shortcut: "escape" }
73
74
  ],
74
75
  [
75
- { id: "delete", label: `Delete ${n} nodes`, shortcut: "backspace", danger: true }
76
+ { id: "delete", label: t("integration.canvas.contextMenu.item.deleteNodes", n), shortcut: "backspace", danger: true }
76
77
  ]
77
78
  ];
78
79
  }
79
80
  return [
80
81
  [
81
- { id: "add-node", label: "Add node\u2026", shortcut: "tab" },
82
- { id: "add-sticky", label: "Add sticky", shortcut: "shift+s" },
83
- { id: "paste", label: "Paste", shortcut: "mod+v" }
82
+ { id: "add-node", label: t("integration.canvas.contextMenu.item.addNode"), shortcut: "tab" },
83
+ { id: "add-sticky", label: t("integration.canvas.contextMenu.item.addSticky"), shortcut: "shift+s" },
84
+ { id: "paste", label: t("integration.canvas.contextMenu.item.paste"), shortcut: "mod+v" }
84
85
  ],
85
86
  [
86
- { id: "tidy-up", label: "Tidy up", shortcut: "shift+alt+t" },
87
- { id: "fit-view", label: "Fit view", shortcut: "1" }
87
+ { id: "tidy-up", label: t("integration.canvas.contextMenu.item.tidyUp"), shortcut: "shift+alt+t" },
88
+ { id: "fit-view", label: t("integration.canvas.contextMenu.item.fitView"), shortcut: "1" }
88
89
  ],
89
90
  [
90
- { id: "select-all", label: "Select all", shortcut: "mod+a", disabled: allSelected.value || props.totalNodes === 0 }
91
+ { id: "select-all", label: t("integration.canvas.contextMenu.item.selectAll"), shortcut: "mod+a", disabled: allSelected.value || props.totalNodes === 0 }
91
92
  ]
92
93
  ];
93
94
  });
94
95
  const headerLabel = computed(
95
- () => props.kind === "selection" ? `${props.selectionCount} nodes selected` : null
96
+ () => props.kind === "selection" ? t("integration.canvas.contextMenu.header.selectionCount", props.selectionCount) : null
96
97
  );
97
98
  function onSelect(item) {
98
99
  if (item.disabled) return;