@revenexx/studio-integrations 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/module.d.mts +2 -2
- package/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/CredentialCreateDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +279 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.d.vue.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +342 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue.d.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialLogo.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue +37 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +364 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +71 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.d.vue.ts +5 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +151 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue.d.ts +5 -0
- package/dist/runtime/components/Integrations/EditorTip.d.vue.ts +17 -0
- package/dist/runtime/components/Integrations/EditorTip.vue +46 -0
- package/dist/runtime/components/Integrations/EditorTip.vue.d.ts +17 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +64 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +19 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +236 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +19 -0
- package/dist/runtime/components/Integrations/GroupFrameView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +222 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +184 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +102 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +23 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +587 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +23 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue +80 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +22 -3
- package/dist/runtime/components/Integrations/NodeInspector.vue +1074 -294
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +22 -3
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +38 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +22 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +411 -90
- package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +22 -1
- package/dist/runtime/components/Integrations/QueryBuilder.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue +239 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.d.vue.ts → RunActionsMenu.d.vue.ts} +3 -4
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +86 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.vue.d.ts → RunActionsMenu.vue.d.ts} +3 -4
- package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +777 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +175 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +27 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue +84 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +27 -0
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/RunsTable.vue +114 -0
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.d.vue.ts +11 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +91 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue.d.ts +11 -0
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue +208 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +29 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +201 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +29 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +138 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +322 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +68 -6
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +1424 -144
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +417 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +89 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue +35 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +114 -84
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +654 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +136 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +35 -15
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +265 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +4 -1
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +122 -39
- package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +4 -1
- package/dist/runtime/components/Integrations/workflowLayout.d.ts +5 -0
- package/dist/runtime/components/Integrations/workflowLayout.js +5 -3
- package/dist/runtime/composables/integrationsApi.d.ts +6 -7
- package/dist/runtime/composables/integrationsApi.js +6 -12
- package/dist/runtime/composables/useCanvasShortcuts.d.ts +37 -0
- package/dist/runtime/composables/useCanvasShortcuts.js +62 -0
- package/dist/runtime/composables/useCredentialTypes.js +2 -1
- package/dist/runtime/composables/useIntegrationsAudit.js +2 -1
- package/dist/runtime/composables/useIntegrationsCredentials.js +2 -1
- package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +22 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +50 -0
- package/dist/runtime/composables/useIntegrationsNodes.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +8 -4
- package/dist/runtime/composables/useIntegrationsRevisions.d.ts +40 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +52 -0
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +21 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +94 -5
- package/dist/runtime/composables/useIntegrationsSecrets.js +2 -1
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsTemplates.js +34 -5
- package/dist/runtime/composables/useIntegrationsTriggers.js +2 -1
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsWorkflows.js +59 -2
- package/dist/runtime/composables/useMiniMapLayout.d.ts +34 -0
- package/dist/runtime/composables/useMiniMapLayout.js +119 -0
- package/dist/runtime/composables/useNodeConfigResolve.d.ts +43 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +124 -0
- package/dist/runtime/composables/useNodeDrag.d.ts +22 -0
- package/dist/runtime/composables/useNodeDrag.js +10 -0
- package/dist/runtime/composables/usePaletteLayout.d.ts +41 -0
- package/dist/runtime/composables/usePaletteLayout.js +113 -0
- package/dist/runtime/data/expressionEditor.d.ts +22 -0
- package/dist/runtime/data/expressionEditor.js +40 -0
- package/dist/runtime/data/integrationNodeCatalog.js +8 -8
- package/dist/runtime/data/nodeInputData.d.ts +37 -0
- package/dist/runtime/data/nodeInputData.js +62 -0
- package/dist/runtime/data/nodeRunData.d.ts +56 -0
- package/dist/runtime/data/nodeRunData.js +91 -0
- package/dist/runtime/data/payloadForm.d.ts +44 -0
- package/dist/runtime/data/payloadForm.js +126 -0
- package/dist/runtime/data/workflowIconCatalog.d.ts +34 -0
- package/dist/runtime/data/workflowIconCatalog.js +116 -0
- package/dist/runtime/pages/integrations/audit.vue +25 -35
- package/dist/runtime/pages/integrations/credentials.vue +117 -122
- package/dist/runtime/pages/integrations/index.vue +133 -36
- package/dist/runtime/pages/integrations/runs.vue +125 -75
- package/dist/runtime/pages/integrations/schedules.vue +5 -16
- package/dist/runtime/pages/integrations/secrets.vue +1 -3
- package/dist/runtime/pages/integrations/workflows/[id].vue +599 -194
- package/dist/runtime/pages/integrations/workflows/index.vue +10 -8
- package/dist/runtime/types/integrations.d.ts +190 -4
- package/dist/runtime/utils/apiError.d.ts +17 -0
- package/dist/runtime/utils/apiError.js +8 -0
- package/dist/runtime/utils/credentialState.d.ts +28 -0
- package/dist/runtime/utils/credentialState.js +29 -0
- package/dist/runtime/utils/localize.d.ts +3 -1
- package/dist/runtime/utils/localize.js +1 -2
- package/dist/runtime/utils/statusStyle.d.ts +6 -0
- package/dist/runtime/utils/statusStyle.js +15 -0
- package/package.json +20 -3
- package/dist/runtime/components/Integrations/RunDetailDialog.vue +0 -169
- package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +0 -2
- package/dist/runtime/pages/integrations/workflows/new.vue +0 -153
- package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +0 -2
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { computed } from "vue";
|
|
2
|
-
import {
|
|
2
|
+
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
|
+
import { IntegrationsApiError } from "../utils/apiError.js";
|
|
3
4
|
import { resolveLocalized } from "../utils/localize.js";
|
|
4
5
|
const BLOB_VERSION = "v0-draft";
|
|
5
6
|
const INDUSTRY_VALUES = ["mechanical-engineering", "chemicals", "wholesale", "manufacturing", "retail", "any"];
|
|
7
|
+
function plainExcerpt(md) {
|
|
8
|
+
for (const raw of md.split("\n")) {
|
|
9
|
+
const line = raw.trim();
|
|
10
|
+
if (!line || line.startsWith("#")) continue;
|
|
11
|
+
return line.replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
12
|
+
}
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
6
15
|
function publisherFor(slug, vendors) {
|
|
7
16
|
const ns = slug.includes(":") ? slug.split(":")[0] : vendors[0] ?? "revenexx";
|
|
8
17
|
const kind = ns === "revenexx" ? "revenexx" : "partner";
|
|
@@ -15,10 +24,16 @@ function emptyBlob(name) {
|
|
|
15
24
|
return { nodeManifestVersion: BLOB_VERSION, name, nodes: [], edges: [], triggerPositions: {}, triggers: [] };
|
|
16
25
|
}
|
|
17
26
|
function mapListItem(api) {
|
|
27
|
+
const short = resolveLocalized(api.shortDescription ?? "");
|
|
28
|
+
const md = resolveLocalized(api.description ?? "");
|
|
18
29
|
return {
|
|
19
30
|
slug: api.slug,
|
|
20
31
|
name: resolveLocalized(api.name),
|
|
21
|
-
description:
|
|
32
|
+
description: short || plainExcerpt(md),
|
|
33
|
+
descriptionMd: md,
|
|
34
|
+
triggerTypes: api.triggerTypes ?? [],
|
|
35
|
+
images: api.images ?? [],
|
|
36
|
+
package: api.package ? { name: api.package.name, label: api.package.label ?? null } : null,
|
|
22
37
|
category: api.category ?? "sync",
|
|
23
38
|
industries: mapIndustries(api.industries),
|
|
24
39
|
vendors: api.vendors ?? [],
|
|
@@ -48,13 +63,17 @@ function applyDetail(base, api) {
|
|
|
48
63
|
name: def.name ?? base.name,
|
|
49
64
|
nodes,
|
|
50
65
|
edges: def.edges ?? [],
|
|
51
|
-
triggerPositions: {},
|
|
66
|
+
triggerPositions: def.triggerPositions ?? {},
|
|
67
|
+
variables: def.variables,
|
|
52
68
|
triggers
|
|
53
69
|
};
|
|
70
|
+
const md = resolveLocalized(api.description ?? "") || base.descriptionMd;
|
|
54
71
|
return {
|
|
55
72
|
...base,
|
|
56
|
-
description:
|
|
73
|
+
description: base.description || plainExcerpt(md),
|
|
74
|
+
descriptionMd: md,
|
|
57
75
|
blob,
|
|
76
|
+
detailLoaded: true,
|
|
58
77
|
nodeCount: nodes.length,
|
|
59
78
|
outline: nodes.map((n) => n.nodeSlug ?? n.id).filter(Boolean)
|
|
60
79
|
};
|
|
@@ -107,6 +126,16 @@ export function useIntegrationsTemplates() {
|
|
|
107
126
|
return base;
|
|
108
127
|
}
|
|
109
128
|
}
|
|
129
|
+
async function ensureDetails(slugs, concurrency = 4) {
|
|
130
|
+
const queue = slugs.filter((slug) => get(slug)?.fromApi && !get(slug)?.detailLoaded);
|
|
131
|
+
const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
132
|
+
while (queue.length) {
|
|
133
|
+
const slug = queue.shift();
|
|
134
|
+
if (slug) await fetchDetail(slug);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
await Promise.all(workers);
|
|
138
|
+
}
|
|
110
139
|
async function requirements(slug) {
|
|
111
140
|
try {
|
|
112
141
|
const raw = unwrap(await api.get(`/templates/${slug}/requirements`));
|
|
@@ -137,5 +166,5 @@ export function useIntegrationsTemplates() {
|
|
|
137
166
|
}
|
|
138
167
|
const total = computed(() => templates.value.length);
|
|
139
168
|
const popular = computed(() => [...templates.value].sort((a, b) => b.installs - a.installs).slice(0, 4));
|
|
140
|
-
return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, requirements, instantiate, install };
|
|
169
|
+
return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, ensureDetails, requirements, instantiate, install };
|
|
141
170
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
2
|
+
import { IntegrationsApiError } from "../utils/apiError.js";
|
|
2
3
|
export function useIntegrationsTriggers() {
|
|
3
4
|
const all = useState("integrations.triggers.all", () => []);
|
|
4
5
|
const loading = useState("integrations.triggers.loading", () => false);
|
|
@@ -23,6 +23,7 @@ export declare function useIntegrationsWorkflows(): {
|
|
|
23
23
|
active: boolean;
|
|
24
24
|
executionMode?: WorkflowExecutionMode;
|
|
25
25
|
}) => Promise<Workflow>;
|
|
26
|
+
duplicate: (id: number, name?: string) => Promise<Workflow>;
|
|
26
27
|
remove: (id: number) => Promise<void>;
|
|
27
28
|
toggleActive: (id: number) => Promise<Workflow | null>;
|
|
28
29
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computed } from "vue";
|
|
2
|
-
import {
|
|
2
|
+
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
|
+
import { IntegrationsApiError } from "../utils/apiError.js";
|
|
3
4
|
const BLOB_VERSION = "v0-draft";
|
|
4
5
|
function toCockpit(api) {
|
|
5
6
|
const apiBlob = api.blob ?? {};
|
|
@@ -15,7 +16,12 @@ function toCockpit(api) {
|
|
|
15
16
|
name: apiBlob.name ?? api.name,
|
|
16
17
|
nodes: apiBlob.nodes ?? [],
|
|
17
18
|
edges: apiBlob.edges ?? [],
|
|
19
|
+
variables: apiBlob.variables ?? void 0,
|
|
18
20
|
triggerPositions: apiBlob.triggerPositions ?? {},
|
|
21
|
+
stickies: apiBlob.stickies ?? [],
|
|
22
|
+
groups: apiBlob.groups ?? [],
|
|
23
|
+
tags: apiBlob.tags ?? void 0,
|
|
24
|
+
icon: apiBlob.icon ?? void 0,
|
|
19
25
|
triggers
|
|
20
26
|
};
|
|
21
27
|
return {
|
|
@@ -27,6 +33,8 @@ function toCockpit(api) {
|
|
|
27
33
|
active: api.active ?? false,
|
|
28
34
|
revision: api.revision ?? 0,
|
|
29
35
|
execution_mode: api.execution_mode,
|
|
36
|
+
buildStatus: api.build_status ?? null,
|
|
37
|
+
buildError: api.build_error ?? null,
|
|
30
38
|
created_at: api.created_at ?? "",
|
|
31
39
|
updated_at: api.updated_at ?? ""
|
|
32
40
|
};
|
|
@@ -42,7 +50,15 @@ function toPayload(meta) {
|
|
|
42
50
|
name: typeof b.name === "string" ? b.name : meta.name,
|
|
43
51
|
nodes: b.nodes ?? [],
|
|
44
52
|
edges: b.edges ?? [],
|
|
45
|
-
triggerPositions: b.triggerPositions ?? {}
|
|
53
|
+
triggerPositions: b.triggerPositions ?? {},
|
|
54
|
+
// Only send `stickies` when non-empty: backends without the stickies
|
|
55
|
+
// schema PR reject unknown blob keys (additionalProperties:false).
|
|
56
|
+
...b.stickies?.length ? { stickies: b.stickies } : {},
|
|
57
|
+
...b.groups?.length ? { groups: b.groups } : {},
|
|
58
|
+
// variables were silently dropped before — the schema always allowed them.
|
|
59
|
+
...b.variables && Object.keys(b.variables).length ? { variables: b.variables } : {},
|
|
60
|
+
...b.tags?.length ? { tags: b.tags } : {},
|
|
61
|
+
...b.icon ? { icon: b.icon } : {}
|
|
46
62
|
},
|
|
47
63
|
active: meta.active,
|
|
48
64
|
execution_mode: meta.executionMode ?? "async_only",
|
|
@@ -112,6 +128,46 @@ export function useIntegrationsWorkflows() {
|
|
|
112
128
|
upsertCache(wf);
|
|
113
129
|
return wf;
|
|
114
130
|
}
|
|
131
|
+
async function duplicate(id, name) {
|
|
132
|
+
const source = await get(id);
|
|
133
|
+
if (!source) throw new Error("Workflow not found.");
|
|
134
|
+
const blob = JSON.parse(JSON.stringify(source.blob));
|
|
135
|
+
const handleMap = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const trigger of blob.triggers ?? []) {
|
|
137
|
+
const fresh = crypto.randomUUID();
|
|
138
|
+
handleMap.set(trigger.id, fresh);
|
|
139
|
+
trigger.id = fresh;
|
|
140
|
+
trigger.active = false;
|
|
141
|
+
}
|
|
142
|
+
for (const edge of blob.edges ?? []) {
|
|
143
|
+
const mapped = handleMap.get(edge.from.nodeId);
|
|
144
|
+
if (mapped) edge.from.nodeId = mapped;
|
|
145
|
+
}
|
|
146
|
+
if (blob.triggerPositions) {
|
|
147
|
+
const rekeyed = {};
|
|
148
|
+
for (const [handle, pos] of Object.entries(blob.triggerPositions)) {
|
|
149
|
+
rekeyed[handleMap.get(handle) ?? handle] = pos;
|
|
150
|
+
}
|
|
151
|
+
blob.triggerPositions = rekeyed;
|
|
152
|
+
}
|
|
153
|
+
const base = {
|
|
154
|
+
description: source.description,
|
|
155
|
+
blob,
|
|
156
|
+
active: false,
|
|
157
|
+
executionMode: source.execution_mode ?? void 0
|
|
158
|
+
};
|
|
159
|
+
if (name?.trim()) return await create({ ...base, name: name.trim() });
|
|
160
|
+
const attempts = [`${source.name} (Copy)`, ...Array.from({ length: 8 }, (_, i) => `${source.name} (Copy ${i + 2})`)];
|
|
161
|
+
let lastError = null;
|
|
162
|
+
for (const attempt of attempts) {
|
|
163
|
+
try {
|
|
164
|
+
return await create({ ...base, name: attempt });
|
|
165
|
+
} catch (err) {
|
|
166
|
+
lastError = err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw lastError instanceof Error ? lastError : new Error("Could not duplicate the workflow.");
|
|
170
|
+
}
|
|
115
171
|
async function update(id, patch) {
|
|
116
172
|
const wf = toCockpit(unwrap(await api.put(`/workflows/${id}`, toPayload({
|
|
117
173
|
name: patch.name,
|
|
@@ -146,6 +202,7 @@ export function useIntegrationsWorkflows() {
|
|
|
146
202
|
get,
|
|
147
203
|
create,
|
|
148
204
|
update,
|
|
205
|
+
duplicate,
|
|
149
206
|
remove,
|
|
150
207
|
toggleActive
|
|
151
208
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Ref } from 'vue';
|
|
2
|
+
export type MiniMapCorner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
3
|
+
export type MiniMapMode = MiniMapCorner | 'floating';
|
|
4
|
+
/**
|
|
5
|
+
* Layout state for the workflow editor's minimap: pinned to one of the four
|
|
6
|
+
* canvas corners, free-floating, or collapsed into a pill — the same
|
|
7
|
+
* interaction grammar as usePaletteLayout, with corner snapping instead of
|
|
8
|
+
* full-height docks. Drags by its header via pointer events; releasing within
|
|
9
|
+
* SNAP_ZONE of two edges snaps into that corner. Persists to localStorage.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useMiniMapLayout(canvasRef: Ref<HTMLElement | null>): {
|
|
12
|
+
mode: Ref<MiniMapMode, MiniMapMode>;
|
|
13
|
+
collapsed: Ref<boolean, boolean>;
|
|
14
|
+
floating: Ref<{
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
}, {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
} | {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
}>;
|
|
24
|
+
floatingStyle: import("vue").ComputedRef<{
|
|
25
|
+
left: string;
|
|
26
|
+
top: string;
|
|
27
|
+
}>;
|
|
28
|
+
dragging: Ref<boolean, boolean>;
|
|
29
|
+
dockPreview: Ref<MiniMapCorner | null, MiniMapCorner | null>;
|
|
30
|
+
startDrag: (ev: PointerEvent, shellEl: HTMLElement | null) => void;
|
|
31
|
+
dock: (corner: MiniMapCorner) => void;
|
|
32
|
+
floatHere: (shellEl: HTMLElement | null) => void;
|
|
33
|
+
setCollapsed: (v: boolean) => void;
|
|
34
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
|
2
|
+
const STORAGE_KEY = "integrations.minimap.layout";
|
|
3
|
+
const CORNERS = ["top-left", "top-right", "bottom-left", "bottom-right"];
|
|
4
|
+
const MARGIN = 12;
|
|
5
|
+
const BOTTOM_CLEARANCE = 64;
|
|
6
|
+
const SNAP_ZONE = 56;
|
|
7
|
+
function clampNum(v, lo, hi) {
|
|
8
|
+
return Math.min(hi, Math.max(lo, v));
|
|
9
|
+
}
|
|
10
|
+
export function useMiniMapLayout(canvasRef) {
|
|
11
|
+
const mode = ref("bottom-right");
|
|
12
|
+
const collapsed = ref(false);
|
|
13
|
+
const floating = ref({ x: MARGIN, y: MARGIN });
|
|
14
|
+
const dragging = ref(false);
|
|
15
|
+
const dockPreview = ref(null);
|
|
16
|
+
function persist() {
|
|
17
|
+
try {
|
|
18
|
+
const payload = { mode: mode.value, floating: floating.value, collapsed: collapsed.value };
|
|
19
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function restore() {
|
|
24
|
+
try {
|
|
25
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
26
|
+
if (!raw) return;
|
|
27
|
+
const saved = JSON.parse(raw);
|
|
28
|
+
if (saved.mode === "floating" || CORNERS.includes(saved.mode)) {
|
|
29
|
+
mode.value = saved.mode;
|
|
30
|
+
}
|
|
31
|
+
if (typeof saved.floating?.x === "number" && typeof saved.floating?.y === "number") {
|
|
32
|
+
floating.value = { x: saved.floating.x, y: saved.floating.y };
|
|
33
|
+
}
|
|
34
|
+
collapsed.value = saved.collapsed === true;
|
|
35
|
+
clampFloating();
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function clampFloating() {
|
|
40
|
+
const rect = canvasRef.value?.getBoundingClientRect();
|
|
41
|
+
if (!rect || rect.width === 0) return;
|
|
42
|
+
floating.value = {
|
|
43
|
+
x: clampNum(floating.value.x, MARGIN, Math.max(MARGIN, rect.width - panelW - MARGIN)),
|
|
44
|
+
y: clampNum(floating.value.y, MARGIN, Math.max(MARGIN, rect.height - BOTTOM_CLEARANCE - panelH))
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
onMounted(restore);
|
|
48
|
+
let grabDX = 0;
|
|
49
|
+
let grabDY = 0;
|
|
50
|
+
let panelW = 200;
|
|
51
|
+
let panelH = 190;
|
|
52
|
+
function startDrag(ev, shellEl) {
|
|
53
|
+
if (ev.button !== 0 || dragging.value) return;
|
|
54
|
+
if (ev.target?.closest("button")) return;
|
|
55
|
+
const canvas = canvasRef.value;
|
|
56
|
+
if (!canvas || !shellEl) return;
|
|
57
|
+
const cRect = canvas.getBoundingClientRect();
|
|
58
|
+
const pRect = shellEl.getBoundingClientRect();
|
|
59
|
+
floating.value = { x: pRect.left - cRect.left, y: pRect.top - cRect.top };
|
|
60
|
+
mode.value = "floating";
|
|
61
|
+
grabDX = ev.clientX - pRect.left;
|
|
62
|
+
grabDY = ev.clientY - pRect.top;
|
|
63
|
+
panelW = pRect.width;
|
|
64
|
+
panelH = pRect.height;
|
|
65
|
+
dragging.value = true;
|
|
66
|
+
ev.preventDefault();
|
|
67
|
+
window.addEventListener("pointermove", onDragMove);
|
|
68
|
+
window.addEventListener("pointerup", onDragEnd);
|
|
69
|
+
window.addEventListener("pointercancel", onDragEnd);
|
|
70
|
+
}
|
|
71
|
+
function onDragMove(ev) {
|
|
72
|
+
const rect = canvasRef.value?.getBoundingClientRect();
|
|
73
|
+
if (!rect) return;
|
|
74
|
+
const x = clampNum(ev.clientX - rect.left - grabDX, MARGIN, Math.max(MARGIN, rect.width - panelW - MARGIN));
|
|
75
|
+
const y = clampNum(ev.clientY - rect.top - grabDY, MARGIN, Math.max(MARGIN, rect.height - BOTTOM_CLEARANCE - panelH));
|
|
76
|
+
floating.value = { x, y };
|
|
77
|
+
const nearLeft = x <= SNAP_ZONE;
|
|
78
|
+
const nearRight = x + panelW >= rect.width - SNAP_ZONE;
|
|
79
|
+
const nearTop = y <= SNAP_ZONE;
|
|
80
|
+
const nearBottom = y + panelH >= rect.height - BOTTOM_CLEARANCE - SNAP_ZONE;
|
|
81
|
+
dockPreview.value = nearTop && nearLeft ? "top-left" : nearTop && nearRight ? "top-right" : nearBottom && nearLeft ? "bottom-left" : nearBottom && nearRight ? "bottom-right" : null;
|
|
82
|
+
}
|
|
83
|
+
function stopListeners() {
|
|
84
|
+
window.removeEventListener("pointermove", onDragMove);
|
|
85
|
+
window.removeEventListener("pointerup", onDragEnd);
|
|
86
|
+
window.removeEventListener("pointercancel", onDragEnd);
|
|
87
|
+
}
|
|
88
|
+
function onDragEnd() {
|
|
89
|
+
if (dockPreview.value) mode.value = dockPreview.value;
|
|
90
|
+
dockPreview.value = null;
|
|
91
|
+
dragging.value = false;
|
|
92
|
+
stopListeners();
|
|
93
|
+
persist();
|
|
94
|
+
}
|
|
95
|
+
onBeforeUnmount(stopListeners);
|
|
96
|
+
function dock(corner) {
|
|
97
|
+
mode.value = corner;
|
|
98
|
+
persist();
|
|
99
|
+
}
|
|
100
|
+
function floatHere(shellEl) {
|
|
101
|
+
if (mode.value !== "floating") {
|
|
102
|
+
const cRect = canvasRef.value?.getBoundingClientRect();
|
|
103
|
+
const pRect = shellEl?.getBoundingClientRect();
|
|
104
|
+
if (cRect && pRect) floating.value = { x: pRect.left - cRect.left, y: pRect.top - cRect.top };
|
|
105
|
+
mode.value = "floating";
|
|
106
|
+
clampFloating();
|
|
107
|
+
}
|
|
108
|
+
persist();
|
|
109
|
+
}
|
|
110
|
+
function setCollapsed(v) {
|
|
111
|
+
collapsed.value = v;
|
|
112
|
+
persist();
|
|
113
|
+
}
|
|
114
|
+
const floatingStyle = computed(() => ({
|
|
115
|
+
left: `${floating.value.x}px`,
|
|
116
|
+
top: `${floating.value.y}px`
|
|
117
|
+
}));
|
|
118
|
+
return { mode, collapsed, floating, floatingStyle, dragging, dockPreview, startDrag, dock, floatHere, setCollapsed };
|
|
119
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author-time resolution of a node's dynamic options / fields / outputs
|
|
3
|
+
* (PO-143, Phase 1). Ports the colleagues' React-Query hook
|
|
4
|
+
* (`integrations-ui/src/lib/useNodeConfigResolve.ts`) to a plain Vue composable
|
|
5
|
+
* — no query library, hand-rolled debounce + abort + keep-previous-data.
|
|
6
|
+
*
|
|
7
|
+
* Backend: `POST {integrationsApi}/nodes/{slug}/{version}/config:resolve`
|
|
8
|
+
* body `{ target, config, locale? }`. `target` is a config field key
|
|
9
|
+
* (→ `{ options }`), `'*'` (→ `{ fields }`) or `'outputs'` (→ `{ outputs }`).
|
|
10
|
+
*
|
|
11
|
+
* `dependsOn` given (options / dynamic-schema): those keys both gate the query
|
|
12
|
+
* (must be set as literals) and form the cache-key subset sent as `config`.
|
|
13
|
+
* `dependsOn` omitted (outputs): no gate, and the whole `config` keys the query
|
|
14
|
+
* and is sent — so any config edit re-resolves.
|
|
15
|
+
*/
|
|
16
|
+
import type { MaybeRefOrGetter, Ref } from 'vue';
|
|
17
|
+
import type { NodeConfigResolveError, NodeConfigResolveResult } from '../types/integrations.js';
|
|
18
|
+
/**
|
|
19
|
+
* A config value counts as "set" for gating a dynamic resolve when it is a
|
|
20
|
+
* non-empty literal. Expression-looking strings (`${{ … }}` / leading `=`) are
|
|
21
|
+
* treated as unset: the backend resolves against literal values only, so a
|
|
22
|
+
* dependency still holding an expression can't drive resolution yet.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isResolvableValue(v: unknown): boolean;
|
|
25
|
+
export declare function useNodeConfigResolve(opts: {
|
|
26
|
+
slug: MaybeRefOrGetter<string>;
|
|
27
|
+
version: MaybeRefOrGetter<string>;
|
|
28
|
+
target: MaybeRefOrGetter<string>;
|
|
29
|
+
config: MaybeRefOrGetter<Record<string, unknown>>;
|
|
30
|
+
dependsOn?: MaybeRefOrGetter<string[] | undefined>;
|
|
31
|
+
enabled?: MaybeRefOrGetter<boolean>;
|
|
32
|
+
/** Type-to-search term, forwarded to loadOptions (PE-334). Keys the query. */
|
|
33
|
+
search?: MaybeRefOrGetter<string | undefined>;
|
|
34
|
+
}): {
|
|
35
|
+
data: Ref<NodeConfigResolveResult | null>;
|
|
36
|
+
error: Ref<NodeConfigResolveError | null>;
|
|
37
|
+
isFetching: Ref<boolean>;
|
|
38
|
+
hasData: Ref<boolean>;
|
|
39
|
+
gated: Ref<boolean>;
|
|
40
|
+
unsatisfied: Ref<string[]>;
|
|
41
|
+
isEnabled: Ref<boolean>;
|
|
42
|
+
refetch: () => void;
|
|
43
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { computed, onScopeDispose, ref, toValue, watch } from "vue";
|
|
2
|
+
import { useIntegrationsApi } from "./integrationsApi.js";
|
|
3
|
+
import { IntegrationsApiError } from "../utils/apiError.js";
|
|
4
|
+
export function isResolvableValue(v) {
|
|
5
|
+
if (v === void 0 || v === null) return false;
|
|
6
|
+
if (typeof v === "string") {
|
|
7
|
+
const t = v.trim();
|
|
8
|
+
if (t === "") return false;
|
|
9
|
+
if (t.startsWith("=")) return false;
|
|
10
|
+
if (/\$?\{\{[\s\S]*?\}\}/.test(t)) return false;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(v)) return v.length > 0;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
function classifyError(err) {
|
|
17
|
+
const status = err instanceof IntegrationsApiError ? err.status : void 0;
|
|
18
|
+
const message = err instanceof IntegrationsApiError ? err.message : err?.message ?? "Resolve failed";
|
|
19
|
+
let kind;
|
|
20
|
+
switch (status) {
|
|
21
|
+
case 409:
|
|
22
|
+
kind = "building";
|
|
23
|
+
break;
|
|
24
|
+
case 502:
|
|
25
|
+
kind = "runtime";
|
|
26
|
+
break;
|
|
27
|
+
case 403:
|
|
28
|
+
kind = "forbidden";
|
|
29
|
+
break;
|
|
30
|
+
case 404:
|
|
31
|
+
kind = "notfound";
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
kind = "generic";
|
|
35
|
+
}
|
|
36
|
+
return { kind, message };
|
|
37
|
+
}
|
|
38
|
+
function isAbortError(err) {
|
|
39
|
+
return err instanceof DOMException ? err.name === "AbortError" : err?.name === "AbortError";
|
|
40
|
+
}
|
|
41
|
+
const DEBOUNCE_MS = 300;
|
|
42
|
+
const STALE_MS = 3e4;
|
|
43
|
+
export function useNodeConfigResolve(opts) {
|
|
44
|
+
const api = useIntegrationsApi();
|
|
45
|
+
const data = ref(null);
|
|
46
|
+
const error = ref(null);
|
|
47
|
+
const isFetching = ref(false);
|
|
48
|
+
const hasData = ref(false);
|
|
49
|
+
const unsatisfied = computed(() => {
|
|
50
|
+
const cfg = toValue(opts.config);
|
|
51
|
+
const gateKeys = toValue(opts.dependsOn) ?? [];
|
|
52
|
+
return gateKeys.filter((k) => !isResolvableValue(cfg[k]));
|
|
53
|
+
});
|
|
54
|
+
const gated = computed(() => unsatisfied.value.length > 0);
|
|
55
|
+
const isEnabled = computed(
|
|
56
|
+
() => (toValue(opts.enabled) ?? true) && !gated.value && !!toValue(opts.slug) && !!toValue(opts.version)
|
|
57
|
+
);
|
|
58
|
+
const subsetKey = computed(() => {
|
|
59
|
+
const cfg = toValue(opts.config);
|
|
60
|
+
const dep = toValue(opts.dependsOn);
|
|
61
|
+
const keyKeys = (dep ?? Object.keys(cfg)).slice().sort();
|
|
62
|
+
const subset = {};
|
|
63
|
+
for (const k of keyKeys) subset[k] = cfg[k];
|
|
64
|
+
const rawSearch = toValue(opts.search);
|
|
65
|
+
const search = typeof rawSearch === "string" ? rawSearch.trim().slice(0, 200) : "";
|
|
66
|
+
return JSON.stringify({ config: subset, search: search || void 0 });
|
|
67
|
+
});
|
|
68
|
+
const debouncedKey = ref(subsetKey.value);
|
|
69
|
+
let debounceTimer = null;
|
|
70
|
+
watch(subsetKey, (next) => {
|
|
71
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
72
|
+
debounceTimer = setTimeout(() => {
|
|
73
|
+
debouncedKey.value = next;
|
|
74
|
+
debounceTimer = null;
|
|
75
|
+
}, DEBOUNCE_MS);
|
|
76
|
+
});
|
|
77
|
+
let controller = null;
|
|
78
|
+
let lastFetchedKey = null;
|
|
79
|
+
let lastFetchedAt = 0;
|
|
80
|
+
async function run(key) {
|
|
81
|
+
if (controller) controller.abort();
|
|
82
|
+
controller = new AbortController();
|
|
83
|
+
const signal = controller.signal;
|
|
84
|
+
const slug = toValue(opts.slug);
|
|
85
|
+
const version = toValue(opts.version);
|
|
86
|
+
const target = toValue(opts.target);
|
|
87
|
+
const { config, search } = JSON.parse(key);
|
|
88
|
+
isFetching.value = true;
|
|
89
|
+
try {
|
|
90
|
+
const result = await api.post(
|
|
91
|
+
`/nodes/${encodeURIComponent(slug)}/${encodeURIComponent(version)}/config:resolve`,
|
|
92
|
+
{ target, config, ...search ? { search } : {} },
|
|
93
|
+
{ signal }
|
|
94
|
+
);
|
|
95
|
+
data.value = result;
|
|
96
|
+
hasData.value = true;
|
|
97
|
+
error.value = null;
|
|
98
|
+
lastFetchedKey = key;
|
|
99
|
+
lastFetchedAt = Date.now();
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (isAbortError(err) || signal.aborted) return;
|
|
102
|
+
error.value = classifyError(err);
|
|
103
|
+
} finally {
|
|
104
|
+
if (!signal.aborted) isFetching.value = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function maybeFetch(force) {
|
|
108
|
+
if (!isEnabled.value) return;
|
|
109
|
+
const key = debouncedKey.value;
|
|
110
|
+
if (!force && lastFetchedKey === key && Date.now() - lastFetchedAt < STALE_MS && !error.value) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
void run(key);
|
|
114
|
+
}
|
|
115
|
+
watch([debouncedKey, isEnabled], () => maybeFetch(false), { immediate: true });
|
|
116
|
+
function refetch() {
|
|
117
|
+
maybeFetch(true);
|
|
118
|
+
}
|
|
119
|
+
onScopeDispose(() => {
|
|
120
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
121
|
+
if (controller) controller.abort();
|
|
122
|
+
});
|
|
123
|
+
return { data, error, isFetching, hasData, gated, unsatisfied, isEnabled, refetch };
|
|
124
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NodeManifest } from '../types/integrations.js';
|
|
2
|
+
import type { TriggerTemplate } from '../data/integrationNodeCatalog.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared state of an in-flight palette drag. The HTML5 drag payload
|
|
5
|
+
* (`dataTransfer`) is unreadable during `dragover`, so the palette mirrors
|
|
6
|
+
* WHAT is being dragged here and the editor reads it to render the
|
|
7
|
+
* grid-snapped placement ghost on the canvas.
|
|
8
|
+
*/
|
|
9
|
+
export type NodeDragInfo = {
|
|
10
|
+
kind: 'manifest';
|
|
11
|
+
manifest: NodeManifest;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'trigger';
|
|
14
|
+
tpl: TriggerTemplate;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'group';
|
|
17
|
+
};
|
|
18
|
+
export declare function useNodeDrag(): {
|
|
19
|
+
dragging: any;
|
|
20
|
+
start: (info: NodeDragInfo) => void;
|
|
21
|
+
end: () => void;
|
|
22
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Ref } from 'vue';
|
|
2
|
+
export type PaletteDock = 'left' | 'right';
|
|
3
|
+
export type PaletteMode = 'docked-left' | 'docked-right' | 'floating';
|
|
4
|
+
/**
|
|
5
|
+
* Layout state for the workflow editor's node palette: docked left/right
|
|
6
|
+
* (full-height inlay), free-floating at a canvas position, or collapsed into
|
|
7
|
+
* a pill. `mode` survives collapse, so collapse/expand round-trips to the
|
|
8
|
+
* same placement.
|
|
9
|
+
*
|
|
10
|
+
* Moving uses plain pointer events — HTML5 drag stays reserved for the
|
|
11
|
+
* palette's node rows and the canvas drop. While dragging, coming within
|
|
12
|
+
* DOCK_ZONE of the left/right canvas edge arms a dock preview; release then
|
|
13
|
+
* snaps into the full-height dock, anywhere else keeps the panel floating.
|
|
14
|
+
*
|
|
15
|
+
* Persists to localStorage (guarded — private mode/quota must not throw) and
|
|
16
|
+
* restores clamped to the canvas on mount.
|
|
17
|
+
*/
|
|
18
|
+
export declare function usePaletteLayout(canvasRef: Ref<HTMLElement | null>): {
|
|
19
|
+
mode: Ref<PaletteMode, PaletteMode>;
|
|
20
|
+
collapsed: Ref<boolean, boolean>;
|
|
21
|
+
floating: Ref<{
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
}, {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
} | {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
}>;
|
|
31
|
+
floatingStyle: import("vue").ComputedRef<{
|
|
32
|
+
left: string;
|
|
33
|
+
top: string;
|
|
34
|
+
}>;
|
|
35
|
+
dragging: Ref<boolean, boolean>;
|
|
36
|
+
dockPreview: Ref<PaletteDock | null, PaletteDock | null>;
|
|
37
|
+
startDrag: (ev: PointerEvent, shellEl: HTMLElement | null) => void;
|
|
38
|
+
dock: (side: PaletteDock) => void;
|
|
39
|
+
floatHere: (shellEl: HTMLElement | null) => void;
|
|
40
|
+
setCollapsed: (v: boolean) => void;
|
|
41
|
+
};
|