@tduniec/plugin-template-designer 0.1.7
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 +120 -0
- package/dist/api/scaffolderActions.esm.js +42 -0
- package/dist/api/scaffolderActions.esm.js.map +1 -0
- package/dist/components/DesignerFlow/DesignerFlow.esm.js +370 -0
- package/dist/components/DesignerFlow/DesignerFlow.esm.js.map +1 -0
- package/dist/components/DesignerFlow/flowConfig.esm.js +21 -0
- package/dist/components/DesignerFlow/flowConfig.esm.js.map +1 -0
- package/dist/components/DesignerFlow/handlers.esm.js +288 -0
- package/dist/components/DesignerFlow/handlers.esm.js.map +1 -0
- package/dist/components/DesignerFlow/model.esm.js +181 -0
- package/dist/components/DesignerFlow/model.esm.js.map +1 -0
- package/dist/components/DesignerFlow/nodeLayout.esm.js +138 -0
- package/dist/components/DesignerFlow/nodeLayout.esm.js.map +1 -0
- package/dist/components/DesignerFlow/parameterTransforms.esm.js +184 -0
- package/dist/components/DesignerFlow/parameterTransforms.esm.js.map +1 -0
- package/dist/components/Nodes/ActionNode.esm.js +437 -0
- package/dist/components/Nodes/ActionNode.esm.js.map +1 -0
- package/dist/components/Nodes/OutputNode.esm.js +368 -0
- package/dist/components/Nodes/OutputNode.esm.js.map +1 -0
- package/dist/components/Nodes/ParameterInputNode.esm.js +310 -0
- package/dist/components/Nodes/ParameterInputNode.esm.js.map +1 -0
- package/dist/components/Nodes/ParameterTitlesNode.esm.js +251 -0
- package/dist/components/Nodes/ParameterTitlesNode.esm.js.map +1 -0
- package/dist/components/Nodes/ParametersNode.esm.js +142 -0
- package/dist/components/Nodes/ParametersNode.esm.js.map +1 -0
- package/dist/components/Nodes/action/schema.esm.js +117 -0
- package/dist/components/Nodes/action/schema.esm.js.map +1 -0
- package/dist/components/Nodes/action/useActionInputs.esm.js +66 -0
- package/dist/components/Nodes/action/useActionInputs.esm.js.map +1 -0
- package/dist/components/Nodes/common/AutoWidthPopper.esm.js +22 -0
- package/dist/components/Nodes/common/AutoWidthPopper.esm.js.map +1 -0
- package/dist/components/Nodes/common/nodeInteraction.esm.js +9 -0
- package/dist/components/Nodes/common/nodeInteraction.esm.js.map +1 -0
- package/dist/components/Nodes/output/useOutputController.esm.js +191 -0
- package/dist/components/Nodes/output/useOutputController.esm.js.map +1 -0
- package/dist/components/Nodes/parameters/useParameterSections.esm.js +162 -0
- package/dist/components/Nodes/parameters/useParameterSections.esm.js.map +1 -0
- package/dist/components/Nodes/types.esm.js +8 -0
- package/dist/components/Nodes/types.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/TemplateDesigner.esm.js +115 -0
- package/dist/components/TemplateDesigner/TemplateDesigner.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/components/FieldEditorDialog.esm.js +32 -0
- package/dist/components/TemplateDesigner/components/FieldEditorDialog.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/components/TemplateLanding.esm.js +102 -0
- package/dist/components/TemplateDesigner/components/TemplateLanding.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/components/TemplateWorkspace.esm.js +200 -0
- package/dist/components/TemplateDesigner/components/TemplateWorkspace.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/index.esm.js +2 -0
- package/dist/components/TemplateDesigner/index.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/useFieldEditor.esm.js +96 -0
- package/dist/components/TemplateDesigner/useFieldEditor.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/useTemplateState.esm.js +391 -0
- package/dist/components/TemplateDesigner/useTemplateState.esm.js.map +1 -0
- package/dist/components/TemplateDesigner/utils.esm.js +46 -0
- package/dist/components/TemplateDesigner/utils.esm.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +19 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/utils/createSequentialEdges.esm.js +15 -0
- package/dist/utils/createSequentialEdges.esm.js.map +1 -0
- package/dist/utils/sampleTemplate.esm.js +40 -0
- package/dist/utils/sampleTemplate.esm.js.map +1 -0
- package/dist/utils/yamlJsonConversion.esm.js +47 -0
- package/dist/utils/yamlJsonConversion.esm.js.map +1 -0
- package/package.json +116 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Backstage Template Designer Plugin
|
|
2
|
+
|
|
3
|
+
**From YAML to Canvas — simplifying Backstage scaffolding**
|
|
4
|
+
|
|
5
|
+
Empowering the **Democratization of Templates** in Backstage 🚀
|
|
6
|
+
Visually **design, connect, and manage** your Backstage scaffolder templates through an **intuitive drag-and-drop interface** — all inside your Backstage instance.
|
|
7
|
+
No YAML complexity. No coding required. Just creativity. **We are breaking the glass!** templating in Backstage made easy! 🚀
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🌟 Why “Democratization of Templates”?
|
|
12
|
+
|
|
13
|
+
Backstage templates shouldn’t be just for developers.
|
|
14
|
+
The Template Designer makes **template creation accessible to everyone** — from DevOps engineers to product teams — enabling true **collaboration and transparency** in how your software templates are built and evolve.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🚀 Features
|
|
19
|
+
|
|
20
|
+
- ⚡ **Drag & Drop Editing** — visually compose your Backstage scaffolder workflows.
|
|
21
|
+
- 🧩 **Three Node Types**
|
|
22
|
+
- **Action Node** – represents a single scaffolder action.
|
|
23
|
+
- **Template Node** – groups multiple actions into a reusable unit.
|
|
24
|
+
- **Output Node** – defines exported values or pipeline results.
|
|
25
|
+
- 💾 **Work with Files** — open existing template definitions or save your flow as a JSON file directly from the UI.
|
|
26
|
+
- 🔄 **Live Flow Connections** — connect nodes with arrows to define execution order.
|
|
27
|
+
- 💡 **Frontend-Only Plugin** — zero backend setup required.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🖼️ Preview
|
|
32
|
+
|
|
33
|
+
Checkout the video!
|
|
34
|
+
|
|
35
|
+
[▶ Watch the PREVIEW on YouTube](https://youtu.be/Pwzlzvig4-c)
|
|
36
|
+
|
|
37
|
+
## ⚙️ Installation
|
|
38
|
+
|
|
39
|
+
From your Backstage root directory
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
yarn add --cwd packages/app @your-org/plugin-template-designer
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In packages/app/src/App.tsx:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { TemplateDesignerPage } from "@your-org/plugin-template-designer";
|
|
49
|
+
|
|
50
|
+
const routes = (
|
|
51
|
+
<FlatRoutes>
|
|
52
|
+
{/* other routes */}
|
|
53
|
+
<Route path="/template-designer" element={<TemplateDesignerPage />} />
|
|
54
|
+
</FlatRoutes>
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
In `packages/app/src/components/Root/Root.tsx`:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import DesignServicesIcon from "@mui/icons-material/DesignServices";
|
|
62
|
+
import { SidebarItem } from "@backstage/core-components";
|
|
63
|
+
|
|
64
|
+
<SidebarItem
|
|
65
|
+
icon={DesignServicesIcon}
|
|
66
|
+
to="template-designer"
|
|
67
|
+
text="Template Designer"
|
|
68
|
+
/>;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## ⚙️ Usage
|
|
72
|
+
|
|
73
|
+
Visit your local Backstage instance:
|
|
74
|
+
|
|
75
|
+
http://localhost:7007/template-designer
|
|
76
|
+
|
|
77
|
+
Create and connect nodes, adjust properties, and export your flow as a JSON file.
|
|
78
|
+
You can also open an existing template file, modify it visually, and save your changes.
|
|
79
|
+
|
|
80
|
+
## 💾 File Management
|
|
81
|
+
|
|
82
|
+
Template Designer allows you to easily work with your Backstage scaffolder definitions:
|
|
83
|
+
Open a template file (.json) – load an existing flow directly into the canvas.
|
|
84
|
+
Edit visually – move nodes, adjust connections, rename actions.
|
|
85
|
+
Save – export your template back to a .json file ready for scaffolder integration.
|
|
86
|
+
|
|
87
|
+
_Template Designer can read your registered actions!_
|
|
88
|
+
|
|
89
|
+
## 🧠 Tech Stack
|
|
90
|
+
|
|
91
|
+
- React + TypeScript
|
|
92
|
+
- React Flow
|
|
93
|
+
- Backstage Core Components
|
|
94
|
+
|
|
95
|
+
## 🛠️ Development
|
|
96
|
+
|
|
97
|
+
To run locally during development:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
yarn start
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This runs a local Backstage app with hot reload support for your plugin.
|
|
104
|
+
|
|
105
|
+
## ❤️ Contributing
|
|
106
|
+
|
|
107
|
+
Template Designer is still fresh out of the oven, so rough edges and open questions are expected—and that’s part of the fun.
|
|
108
|
+
If you spot a bug, have an idea, or simply want to riff on better tooling for templates, please open an issue or PR.
|
|
109
|
+
|
|
110
|
+
Help us push forward the Democratization of Templates in Backstage!
|
|
111
|
+
Ideas, feedback, and PRs are all welcome.
|
|
112
|
+
|
|
113
|
+
## 📄 License
|
|
114
|
+
|
|
115
|
+
Apache-2.0 © 2025 — Created by [tduniec](https://github.com/tduniec)
|
|
116
|
+
|
|
117
|
+
## 🌐 Roadmap
|
|
118
|
+
|
|
119
|
+
TODO -> please help me gather your **[feedback](https://forms.gle/wqBtpNA3SrDoofgM8)** first!
|
|
120
|
+
Take 2 minutes to fill up the **[survey](https://forms.gle/wqBtpNA3SrDoofgM8)**!
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
3
|
+
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
|
|
4
|
+
|
|
5
|
+
const buildCache = (list) => {
|
|
6
|
+
const { inputsById, outputsById } = list.reduce(
|
|
7
|
+
(acc, action) => {
|
|
8
|
+
acc.inputsById[action.id] = action.schema?.input?.properties ?? {};
|
|
9
|
+
acc.outputsById[action.id] = action.schema?.output?.properties ?? {};
|
|
10
|
+
return acc;
|
|
11
|
+
},
|
|
12
|
+
{ inputsById: {}, outputsById: {} }
|
|
13
|
+
);
|
|
14
|
+
return {
|
|
15
|
+
ids: list.map((action) => action.id),
|
|
16
|
+
inputsById,
|
|
17
|
+
outputsById
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const useScaffolderActions = () => {
|
|
21
|
+
const scaffolderApi = useApi(scaffolderApiRef);
|
|
22
|
+
const [cache, setCache] = useState(
|
|
23
|
+
() => buildCache([])
|
|
24
|
+
);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let cancelled = false;
|
|
27
|
+
scaffolderApi.listActions().then((remoteActions) => {
|
|
28
|
+
if (cancelled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
setCache(buildCache(remoteActions));
|
|
32
|
+
}).catch(() => {
|
|
33
|
+
});
|
|
34
|
+
return () => {
|
|
35
|
+
cancelled = true;
|
|
36
|
+
};
|
|
37
|
+
}, [scaffolderApi]);
|
|
38
|
+
return cache;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { useScaffolderActions };
|
|
42
|
+
//# sourceMappingURL=scaffolderActions.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffolderActions.esm.js","sources":["../../src/api/scaffolderActions.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useApi } from \"@backstage/core-plugin-api\";\nimport { scaffolderApiRef } from \"@backstage/plugin-scaffolder-react\";\n\n// Encapsulated hook for loading/caching scaffolder action metadata.\n\ntype ScaffolderAction = {\n id: string;\n schema?: {\n input?: {\n properties?: Record<string, unknown>;\n };\n output?: {\n properties?: Record<string, unknown>;\n };\n };\n};\n\nexport type ScaffolderActionsCache = {\n ids: string[];\n inputsById: Record<string, Record<string, unknown>>;\n outputsById: Record<string, Record<string, unknown>>;\n};\n\nconst buildCache = (list: ScaffolderAction[]): ScaffolderActionsCache => {\n const { inputsById, outputsById } = list.reduce<{\n inputsById: Record<string, Record<string, unknown>>;\n outputsById: Record<string, Record<string, unknown>>;\n }>(\n (acc, action) => {\n acc.inputsById[action.id] = action.schema?.input?.properties ?? {};\n acc.outputsById[action.id] = action.schema?.output?.properties ?? {};\n return acc;\n },\n { inputsById: {}, outputsById: {} }\n );\n\n return {\n ids: list.map((action) => action.id),\n inputsById,\n outputsById,\n };\n};\n\nexport const useScaffolderActions = () => {\n const scaffolderApi = useApi(scaffolderApiRef);\n const [cache, setCache] = useState<ScaffolderActionsCache>(() =>\n buildCache([])\n );\n\n useEffect(() => {\n let cancelled = false;\n\n scaffolderApi\n .listActions()\n .then((remoteActions) => {\n if (cancelled) {\n return;\n }\n setCache(buildCache(remoteActions));\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, [scaffolderApi]);\n\n return cache;\n};\n"],"names":[],"mappings":";;;;AAwBA,MAAM,UAAA,GAAa,CAAC,IAAA,KAAqD;AACvE,EAAA,MAAM,EAAE,UAAA,EAAY,WAAA,EAAY,GAAI,IAAA,CAAK,MAAA;AAAA,IAIvC,CAAC,KAAK,MAAA,KAAW;AACf,MAAA,GAAA,CAAI,UAAA,CAAW,OAAO,EAAE,CAAA,GAAI,OAAO,MAAA,EAAQ,KAAA,EAAO,cAAc,EAAC;AACjE,MAAA,GAAA,CAAI,WAAA,CAAY,OAAO,EAAE,CAAA,GAAI,OAAO,MAAA,EAAQ,MAAA,EAAQ,cAAc,EAAC;AACnE,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA,EAAE,UAAA,EAAY,EAAC,EAAG,WAAA,EAAa,EAAC;AAAE,GACpC;AAEA,EAAA,OAAO;AAAA,IACL,KAAK,IAAA,CAAK,GAAA,CAAI,CAAC,MAAA,KAAW,OAAO,EAAE,CAAA;AAAA,IACnC,UAAA;AAAA,IACA;AAAA,GACF;AACF,CAAA;AAEO,MAAM,uBAAuB,MAAM;AACxC,EAAA,MAAM,aAAA,GAAgB,OAAO,gBAAgB,CAAA;AAC7C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA;AAAA,IAAiC,MACzD,UAAA,CAAW,EAAE;AAAA,GACf;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,aAAA,CACG,WAAA,EAAY,CACZ,IAAA,CAAK,CAAC,aAAA,KAAkB;AACvB,MAAA,IAAI,SAAA,EAAW;AACb,QAAA;AAAA,MACF;AACA,MAAA,QAAA,CAAS,UAAA,CAAW,aAAa,CAAC,CAAA;AAAA,IACpC,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAEjB,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAElB,EAAA,OAAO,KAAA;AACT;;;;"}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useNodesState, applyNodeChanges, applyEdgeChanges, addEdge, ReactFlow } from '@xyflow/react';
|
|
4
|
+
import '@xyflow/react/dist/style.css';
|
|
5
|
+
import { createSequentialEdges } from '../../utils/createSequentialEdges.esm.js';
|
|
6
|
+
import { collectStepOutputReferences, createHandleReorderAndAlignNodes, createHandleUpdateField, createHandleUpdateInput, createHandleRemoveInputKey, createHandleUpdateOutput, createHandleUpdateSections, createHandleAddNode } from './handlers.esm.js';
|
|
7
|
+
import { buildNodesFromModel, stableStringify, resolveNodeHeightForTracking, collectParameterReferences, extractStepsFromNodes, extractParametersFromNodes, extractOutputFromNodes } from './model.esm.js';
|
|
8
|
+
import { alignNodes } from './nodeLayout.esm.js';
|
|
9
|
+
import { nodeTypes, FLOW_LAYOUT, nodeDefaults } from './flowConfig.esm.js';
|
|
10
|
+
import { useScaffolderActions } from '../../api/scaffolderActions.esm.js';
|
|
11
|
+
|
|
12
|
+
const FIXED_X_POSITION = FLOW_LAYOUT.fixedXPosition;
|
|
13
|
+
const VERTICAL_SPACING = FLOW_LAYOUT.verticalSpacing;
|
|
14
|
+
function App({
|
|
15
|
+
steps = [],
|
|
16
|
+
parameters,
|
|
17
|
+
output,
|
|
18
|
+
onStepsChange,
|
|
19
|
+
onParametersChange,
|
|
20
|
+
onOutputChange
|
|
21
|
+
}) {
|
|
22
|
+
const scaffolderActionsCache = useScaffolderActions();
|
|
23
|
+
const {
|
|
24
|
+
ids: scaffolderActionIds,
|
|
25
|
+
inputsById: scaffolderActionInputsById,
|
|
26
|
+
outputsById: scaffolderActionOutputsById
|
|
27
|
+
} = scaffolderActionsCache;
|
|
28
|
+
const normalizedParametersProp = parameters ?? void 0;
|
|
29
|
+
const normalizedOutputProp = output ?? null;
|
|
30
|
+
const initialNodes = useMemo(
|
|
31
|
+
() => buildNodesFromModel(
|
|
32
|
+
steps,
|
|
33
|
+
normalizedParametersProp,
|
|
34
|
+
normalizedOutputProp,
|
|
35
|
+
{
|
|
36
|
+
scaffolderActionIds,
|
|
37
|
+
scaffolderActionInputsById,
|
|
38
|
+
scaffolderActionOutputsById
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
[
|
|
42
|
+
steps,
|
|
43
|
+
normalizedParametersProp,
|
|
44
|
+
normalizedOutputProp,
|
|
45
|
+
scaffolderActionIds,
|
|
46
|
+
scaffolderActionInputsById,
|
|
47
|
+
scaffolderActionOutputsById
|
|
48
|
+
]
|
|
49
|
+
);
|
|
50
|
+
const [nodes, setNodes] = useNodesState(initialNodes);
|
|
51
|
+
const [edges, setEdges] = useState(
|
|
52
|
+
() => createSequentialEdges(initialNodes)
|
|
53
|
+
);
|
|
54
|
+
const modelHash = useMemo(
|
|
55
|
+
() => stableStringify({
|
|
56
|
+
steps,
|
|
57
|
+
parameters: normalizedParametersProp,
|
|
58
|
+
output: normalizedOutputProp
|
|
59
|
+
}),
|
|
60
|
+
[steps, normalizedParametersProp, normalizedOutputProp]
|
|
61
|
+
);
|
|
62
|
+
const cacheFingerprint = useMemo(
|
|
63
|
+
() => stableStringify({
|
|
64
|
+
ids: scaffolderActionIds,
|
|
65
|
+
inputs: scaffolderActionInputsById,
|
|
66
|
+
outputs: scaffolderActionOutputsById
|
|
67
|
+
}),
|
|
68
|
+
[
|
|
69
|
+
scaffolderActionIds,
|
|
70
|
+
scaffolderActionInputsById,
|
|
71
|
+
scaffolderActionOutputsById
|
|
72
|
+
]
|
|
73
|
+
);
|
|
74
|
+
const lastAppliedModelHashRef = useRef(null);
|
|
75
|
+
const lastEmittedModelHashRef = useRef(null);
|
|
76
|
+
const skipNextModelHashRef = useRef(null);
|
|
77
|
+
const lastCacheFingerprintRef = useRef(null);
|
|
78
|
+
const nodeHeightsRef = useRef({});
|
|
79
|
+
const shouldAutoFitViewRef = useRef(true);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
const isCacheChanged = cacheFingerprint !== lastCacheFingerprintRef.current;
|
|
82
|
+
const shouldSkip = modelHash === skipNextModelHashRef.current && !isCacheChanged;
|
|
83
|
+
if (shouldSkip) {
|
|
84
|
+
skipNextModelHashRef.current = null;
|
|
85
|
+
lastAppliedModelHashRef.current = modelHash;
|
|
86
|
+
lastCacheFingerprintRef.current = cacheFingerprint;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (modelHash === lastAppliedModelHashRef.current && !isCacheChanged) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const nextNodes = buildNodesFromModel(
|
|
93
|
+
steps,
|
|
94
|
+
normalizedParametersProp,
|
|
95
|
+
normalizedOutputProp,
|
|
96
|
+
{
|
|
97
|
+
scaffolderActionIds,
|
|
98
|
+
scaffolderActionInputsById,
|
|
99
|
+
scaffolderActionOutputsById
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
lastAppliedModelHashRef.current = modelHash;
|
|
103
|
+
lastCacheFingerprintRef.current = cacheFingerprint;
|
|
104
|
+
lastEmittedModelHashRef.current = modelHash;
|
|
105
|
+
setNodes(nextNodes);
|
|
106
|
+
setEdges(createSequentialEdges(nextNodes));
|
|
107
|
+
shouldAutoFitViewRef.current = true;
|
|
108
|
+
}, [
|
|
109
|
+
steps,
|
|
110
|
+
normalizedParametersProp,
|
|
111
|
+
normalizedOutputProp,
|
|
112
|
+
modelHash,
|
|
113
|
+
cacheFingerprint,
|
|
114
|
+
scaffolderActionIds,
|
|
115
|
+
scaffolderActionInputsById,
|
|
116
|
+
scaffolderActionOutputsById,
|
|
117
|
+
setNodes,
|
|
118
|
+
setEdges
|
|
119
|
+
]);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!nodes.length) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (nodes.some((node) => node.dragging)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const activeNodeIds = /* @__PURE__ */ new Set();
|
|
128
|
+
let hasMeasuredChange = false;
|
|
129
|
+
nodes.forEach((node) => {
|
|
130
|
+
activeNodeIds.add(node.id);
|
|
131
|
+
const measuredHeight = resolveNodeHeightForTracking(node);
|
|
132
|
+
if (typeof measuredHeight !== "number") {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const previousHeight = nodeHeightsRef.current[node.id];
|
|
136
|
+
if (previousHeight !== measuredHeight) {
|
|
137
|
+
nodeHeightsRef.current[node.id] = measuredHeight;
|
|
138
|
+
hasMeasuredChange = true;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
Object.keys(nodeHeightsRef.current).forEach((id) => {
|
|
142
|
+
if (!activeNodeIds.has(id)) {
|
|
143
|
+
delete nodeHeightsRef.current[id];
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
if (!hasMeasuredChange) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
setNodes((currentNodes) => {
|
|
150
|
+
const alignedNodes = alignNodes(
|
|
151
|
+
currentNodes,
|
|
152
|
+
FIXED_X_POSITION,
|
|
153
|
+
VERTICAL_SPACING
|
|
154
|
+
);
|
|
155
|
+
const positionsChanged = alignedNodes.some((node, index) => {
|
|
156
|
+
const previousNode = currentNodes[index];
|
|
157
|
+
if (!previousNode) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return node.position.x !== previousNode.position.x || node.position.y !== previousNode.position.y;
|
|
161
|
+
});
|
|
162
|
+
return positionsChanged ? alignedNodes : currentNodes;
|
|
163
|
+
});
|
|
164
|
+
}, [nodes, setNodes]);
|
|
165
|
+
const parameterReferences = useMemo(
|
|
166
|
+
() => collectParameterReferences(normalizedParametersProp),
|
|
167
|
+
[normalizedParametersProp]
|
|
168
|
+
);
|
|
169
|
+
const stepOutputReferencesByNode = useMemo(
|
|
170
|
+
() => collectStepOutputReferences(nodes, parameterReferences),
|
|
171
|
+
[nodes, parameterReferences]
|
|
172
|
+
);
|
|
173
|
+
const onNodesChange = useCallback(
|
|
174
|
+
(changes) => setNodes((ns) => applyNodeChanges(changes, ns)),
|
|
175
|
+
[setNodes]
|
|
176
|
+
);
|
|
177
|
+
const onEdgesChange = useCallback(
|
|
178
|
+
(changes) => setEdges((es) => applyEdgeChanges(changes, es)),
|
|
179
|
+
[setEdges]
|
|
180
|
+
);
|
|
181
|
+
const onConnect = useCallback(
|
|
182
|
+
(params) => setEdges((es) => addEdge(params, es)),
|
|
183
|
+
[setEdges]
|
|
184
|
+
);
|
|
185
|
+
const reorderAndAlignNodes = useMemo(
|
|
186
|
+
() => createHandleReorderAndAlignNodes(setNodes, setEdges, {
|
|
187
|
+
fixedXPosition: FIXED_X_POSITION,
|
|
188
|
+
verticalSpacing: VERTICAL_SPACING
|
|
189
|
+
}),
|
|
190
|
+
[setNodes, setEdges]
|
|
191
|
+
);
|
|
192
|
+
const onNodeDragStop = useCallback(
|
|
193
|
+
(_, node) => {
|
|
194
|
+
reorderAndAlignNodes(node);
|
|
195
|
+
},
|
|
196
|
+
[reorderAndAlignNodes]
|
|
197
|
+
);
|
|
198
|
+
const onUpdateField = useMemo(
|
|
199
|
+
() => createHandleUpdateField(setNodes),
|
|
200
|
+
[setNodes]
|
|
201
|
+
);
|
|
202
|
+
const onUpdateInput = useMemo(
|
|
203
|
+
() => createHandleUpdateInput(setNodes),
|
|
204
|
+
[setNodes]
|
|
205
|
+
);
|
|
206
|
+
const onRemoveInputKey = useMemo(
|
|
207
|
+
() => createHandleRemoveInputKey(setNodes),
|
|
208
|
+
[setNodes]
|
|
209
|
+
);
|
|
210
|
+
const onUpdateOutput = useMemo(
|
|
211
|
+
() => createHandleUpdateOutput(setNodes),
|
|
212
|
+
[setNodes]
|
|
213
|
+
);
|
|
214
|
+
const onUpdateSections = useMemo(
|
|
215
|
+
() => createHandleUpdateSections(setNodes),
|
|
216
|
+
[setNodes]
|
|
217
|
+
);
|
|
218
|
+
const handleAddNode = useMemo(
|
|
219
|
+
() => createHandleAddNode(setNodes, setEdges, {
|
|
220
|
+
fixedXPosition: FIXED_X_POSITION,
|
|
221
|
+
verticalSpacing: VERTICAL_SPACING,
|
|
222
|
+
nodeDefaults,
|
|
223
|
+
scaffolderActionIds,
|
|
224
|
+
scaffolderActionInputsById,
|
|
225
|
+
scaffolderActionOutputsById
|
|
226
|
+
}),
|
|
227
|
+
[
|
|
228
|
+
scaffolderActionIds,
|
|
229
|
+
scaffolderActionInputsById,
|
|
230
|
+
scaffolderActionOutputsById,
|
|
231
|
+
setNodes,
|
|
232
|
+
setEdges
|
|
233
|
+
]
|
|
234
|
+
);
|
|
235
|
+
const nodesWithHandlers = useMemo(
|
|
236
|
+
() => nodes.map((node) => {
|
|
237
|
+
const stepOutputReferences = stepOutputReferencesByNode[node.id] ?? [];
|
|
238
|
+
if (node.type === "parametersNode") {
|
|
239
|
+
const data2 = node.data;
|
|
240
|
+
return {
|
|
241
|
+
...node,
|
|
242
|
+
data: {
|
|
243
|
+
...data2,
|
|
244
|
+
onAddNode: handleAddNode,
|
|
245
|
+
onUpdateSections,
|
|
246
|
+
stepOutputReferences
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (node.type === "outputNode") {
|
|
251
|
+
const data2 = node.data;
|
|
252
|
+
return {
|
|
253
|
+
...node,
|
|
254
|
+
data: {
|
|
255
|
+
...data2,
|
|
256
|
+
onAddNode: handleAddNode,
|
|
257
|
+
onUpdateOutput,
|
|
258
|
+
stepOutputReferences
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const data = node.data;
|
|
263
|
+
return {
|
|
264
|
+
...node,
|
|
265
|
+
data: {
|
|
266
|
+
...data,
|
|
267
|
+
onAddNode: handleAddNode,
|
|
268
|
+
onUpdateField,
|
|
269
|
+
onUpdateInput,
|
|
270
|
+
onRemoveInputKey,
|
|
271
|
+
stepOutputReferences
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}),
|
|
275
|
+
[
|
|
276
|
+
nodes,
|
|
277
|
+
handleAddNode,
|
|
278
|
+
onUpdateField,
|
|
279
|
+
onUpdateInput,
|
|
280
|
+
onRemoveInputKey,
|
|
281
|
+
onUpdateOutput,
|
|
282
|
+
onUpdateSections,
|
|
283
|
+
stepOutputReferencesByNode
|
|
284
|
+
]
|
|
285
|
+
);
|
|
286
|
+
const stepsFromNodes = useMemo(() => extractStepsFromNodes(nodes), [nodes]);
|
|
287
|
+
const parametersFromNodes = useMemo(
|
|
288
|
+
() => extractParametersFromNodes(nodes),
|
|
289
|
+
[nodes]
|
|
290
|
+
);
|
|
291
|
+
const outputFromNodes = useMemo(() => extractOutputFromNodes(nodes), [nodes]);
|
|
292
|
+
const normalizedOutputFromNodes = outputFromNodes ?? null;
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
if (!onStepsChange && !onParametersChange && !onOutputChange) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const serialized = stableStringify({
|
|
298
|
+
steps: stepsFromNodes,
|
|
299
|
+
parameters: parametersFromNodes ?? null,
|
|
300
|
+
output: normalizedOutputFromNodes
|
|
301
|
+
});
|
|
302
|
+
if (serialized === lastEmittedModelHashRef.current) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
lastEmittedModelHashRef.current = serialized;
|
|
306
|
+
skipNextModelHashRef.current = serialized;
|
|
307
|
+
if (onStepsChange) {
|
|
308
|
+
onStepsChange(stepsFromNodes);
|
|
309
|
+
}
|
|
310
|
+
if (onParametersChange) {
|
|
311
|
+
onParametersChange(parametersFromNodes ?? void 0);
|
|
312
|
+
}
|
|
313
|
+
if (onOutputChange) {
|
|
314
|
+
onOutputChange(outputFromNodes);
|
|
315
|
+
}
|
|
316
|
+
}, [
|
|
317
|
+
stepsFromNodes,
|
|
318
|
+
parametersFromNodes,
|
|
319
|
+
normalizedOutputFromNodes,
|
|
320
|
+
outputFromNodes,
|
|
321
|
+
onStepsChange,
|
|
322
|
+
onParametersChange,
|
|
323
|
+
onOutputChange
|
|
324
|
+
]);
|
|
325
|
+
const fitViewOptions = useMemo(() => ({ padding: 0.2, duration: 300 }), []);
|
|
326
|
+
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
|
327
|
+
const fitFlowToView = useCallback(() => {
|
|
328
|
+
if (!reactFlowInstance) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
reactFlowInstance.fitView(fitViewOptions);
|
|
332
|
+
}, [fitViewOptions, reactFlowInstance]);
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
if (!reactFlowInstance) {
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (!shouldAutoFitViewRef.current) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
shouldAutoFitViewRef.current = false;
|
|
341
|
+
fitFlowToView();
|
|
342
|
+
}, [fitFlowToView, nodes, edges, reactFlowInstance]);
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
if (!reactFlowInstance) {
|
|
345
|
+
return void 0;
|
|
346
|
+
}
|
|
347
|
+
window.addEventListener("resize", fitFlowToView);
|
|
348
|
+
return () => {
|
|
349
|
+
window.removeEventListener("resize", fitFlowToView);
|
|
350
|
+
};
|
|
351
|
+
}, [fitFlowToView, reactFlowInstance]);
|
|
352
|
+
return /* @__PURE__ */ jsx("div", { style: { width: "100%", height: "100%", minHeight: "70vh" }, children: /* @__PURE__ */ jsx(
|
|
353
|
+
ReactFlow,
|
|
354
|
+
{
|
|
355
|
+
nodes: nodesWithHandlers,
|
|
356
|
+
edges,
|
|
357
|
+
nodeTypes,
|
|
358
|
+
onNodesChange,
|
|
359
|
+
onEdgesChange,
|
|
360
|
+
onNodeDragStop,
|
|
361
|
+
onConnect,
|
|
362
|
+
fitView: true,
|
|
363
|
+
onInit: setReactFlowInstance,
|
|
364
|
+
fitViewOptions
|
|
365
|
+
}
|
|
366
|
+
) });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export { App as default };
|
|
370
|
+
//# sourceMappingURL=DesignerFlow.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DesignerFlow.esm.js","sources":["../../../src/components/DesignerFlow/DesignerFlow.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { MouseEvent as ReactMouseEvent } from \"react\";\nimport {\n ReactFlow,\n applyNodeChanges,\n applyEdgeChanges,\n addEdge,\n Node,\n Edge,\n NodeChange,\n EdgeChange,\n useNodesState,\n ReactFlowInstance,\n} from \"@xyflow/react\";\nimport \"@xyflow/react/dist/style.css\";\nimport type {\n ScaffolderTaskOutput,\n TaskStep,\n} from \"@backstage/plugin-scaffolder-common\";\nimport type {\n ActionNodeData,\n OutputNodeData,\n ParametersNodeData,\n TemplateParametersValue,\n} from \"../Nodes/types\";\nimport { createSequentialEdges } from \"../../utils/createSequentialEdges\";\nimport {\n collectStepOutputReferences,\n createHandleAddNode,\n createHandleRemoveInputKey,\n createHandleReorderAndAlignNodes,\n createHandleUpdateField,\n createHandleUpdateInput,\n createHandleUpdateOutput,\n createHandleUpdateSections,\n} from \"./handlers\";\nimport {\n buildNodesFromModel,\n collectParameterReferences,\n stableStringify,\n resolveNodeHeightForTracking,\n extractStepsFromNodes,\n extractParametersFromNodes,\n extractOutputFromNodes,\n} from \"./model\";\nimport { alignNodes } from \"./nodeLayout\";\nimport { FLOW_LAYOUT, nodeDefaults, nodeTypes } from \"./flowConfig\";\nimport { useScaffolderActions } from \"../../api/scaffolderActions\";\n\n// Main orchestration component that renders and synchronizes the Designer flow.\n\nconst FIXED_X_POSITION = FLOW_LAYOUT.fixedXPosition;\nconst VERTICAL_SPACING = FLOW_LAYOUT.verticalSpacing;\n\ntype DesignerFlowProps = {\n steps?: TaskStep[];\n parameters?: TemplateParametersValue;\n output?: ScaffolderTaskOutput | null;\n onStepsChange?: (steps: TaskStep[]) => void;\n onParametersChange?: (parameters: TemplateParametersValue) => void;\n onOutputChange?: (output: ScaffolderTaskOutput | undefined) => void;\n};\n\nexport default function App({\n steps = [],\n parameters,\n output,\n onStepsChange,\n onParametersChange,\n onOutputChange,\n}: DesignerFlowProps) {\n const scaffolderActionsCache = useScaffolderActions();\n\n const {\n ids: scaffolderActionIds,\n inputsById: scaffolderActionInputsById,\n outputsById: scaffolderActionOutputsById,\n } = scaffolderActionsCache;\n\n const normalizedParametersProp = parameters ?? undefined;\n const normalizedOutputProp = output ?? null;\n\n const initialNodes = useMemo(\n () =>\n buildNodesFromModel(\n steps,\n normalizedParametersProp,\n normalizedOutputProp,\n {\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n }\n ),\n [\n steps,\n normalizedParametersProp,\n normalizedOutputProp,\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n ]\n );\n\n const [nodes, setNodes] = useNodesState(initialNodes);\n const [edges, setEdges] = useState<Edge[]>(() =>\n createSequentialEdges(initialNodes)\n );\n\n const modelHash = useMemo(\n () =>\n stableStringify({\n steps,\n parameters: normalizedParametersProp,\n output: normalizedOutputProp,\n }),\n [steps, normalizedParametersProp, normalizedOutputProp]\n );\n\n const cacheFingerprint = useMemo(\n () =>\n stableStringify({\n ids: scaffolderActionIds,\n inputs: scaffolderActionInputsById,\n outputs: scaffolderActionOutputsById,\n }),\n [\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n ]\n );\n\n const lastAppliedModelHashRef = useRef<string | null>(null);\n const lastEmittedModelHashRef = useRef<string | null>(null);\n const skipNextModelHashRef = useRef<string | null>(null);\n const lastCacheFingerprintRef = useRef<string | null>(null);\n const nodeHeightsRef = useRef<Record<string, number>>({});\n const shouldAutoFitViewRef = useRef(true);\n\n useEffect(() => {\n const isCacheChanged = cacheFingerprint !== lastCacheFingerprintRef.current;\n const shouldSkip =\n modelHash === skipNextModelHashRef.current && !isCacheChanged;\n\n if (shouldSkip) {\n skipNextModelHashRef.current = null;\n lastAppliedModelHashRef.current = modelHash;\n lastCacheFingerprintRef.current = cacheFingerprint;\n return;\n }\n\n if (modelHash === lastAppliedModelHashRef.current && !isCacheChanged) {\n return;\n }\n\n const nextNodes = buildNodesFromModel(\n steps,\n normalizedParametersProp,\n normalizedOutputProp,\n {\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n }\n );\n\n lastAppliedModelHashRef.current = modelHash;\n lastCacheFingerprintRef.current = cacheFingerprint;\n lastEmittedModelHashRef.current = modelHash;\n\n setNodes(nextNodes);\n setEdges(createSequentialEdges(nextNodes));\n shouldAutoFitViewRef.current = true;\n }, [\n steps,\n normalizedParametersProp,\n normalizedOutputProp,\n modelHash,\n cacheFingerprint,\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n setNodes,\n setEdges,\n ]);\n\n useEffect(() => {\n if (!nodes.length) {\n return;\n }\n\n if (nodes.some((node) => node.dragging)) {\n return;\n }\n\n const activeNodeIds = new Set<string>();\n let hasMeasuredChange = false;\n\n nodes.forEach((node) => {\n activeNodeIds.add(node.id);\n const measuredHeight = resolveNodeHeightForTracking(node);\n if (typeof measuredHeight !== \"number\") {\n return;\n }\n const previousHeight = nodeHeightsRef.current[node.id];\n if (previousHeight !== measuredHeight) {\n nodeHeightsRef.current[node.id] = measuredHeight;\n hasMeasuredChange = true;\n }\n });\n\n Object.keys(nodeHeightsRef.current).forEach((id) => {\n if (!activeNodeIds.has(id)) {\n delete nodeHeightsRef.current[id];\n }\n });\n\n if (!hasMeasuredChange) {\n return;\n }\n\n setNodes((currentNodes) => {\n const alignedNodes = alignNodes(\n currentNodes,\n FIXED_X_POSITION,\n VERTICAL_SPACING\n );\n const positionsChanged = alignedNodes.some((node, index) => {\n const previousNode = currentNodes[index];\n if (!previousNode) {\n return true;\n }\n return (\n node.position.x !== previousNode.position.x ||\n node.position.y !== previousNode.position.y\n );\n });\n\n return positionsChanged ? alignedNodes : currentNodes;\n });\n }, [nodes, setNodes]);\n\n const parameterReferences = useMemo(\n () => collectParameterReferences(normalizedParametersProp),\n [normalizedParametersProp]\n );\n\n const stepOutputReferencesByNode = useMemo(\n () => collectStepOutputReferences(nodes, parameterReferences),\n [nodes, parameterReferences]\n );\n\n const onNodesChange = useCallback(\n (changes: NodeChange<Node>[]) =>\n setNodes((ns) => applyNodeChanges(changes, ns)),\n [setNodes]\n );\n\n const onEdgesChange = useCallback(\n (changes: EdgeChange<Edge>[]) =>\n setEdges((es) => applyEdgeChanges(changes, es)),\n [setEdges]\n );\n\n const onConnect = useCallback(\n (params: any) => setEdges((es) => addEdge(params, es)),\n [setEdges]\n );\n\n const reorderAndAlignNodes = useMemo(\n () =>\n createHandleReorderAndAlignNodes(setNodes, setEdges, {\n fixedXPosition: FIXED_X_POSITION,\n verticalSpacing: VERTICAL_SPACING,\n }),\n [setNodes, setEdges]\n );\n\n const onNodeDragStop = useCallback(\n (_: ReactMouseEvent, node: Node) => {\n reorderAndAlignNodes(node);\n },\n [reorderAndAlignNodes]\n );\n\n const onUpdateField = useMemo(\n () => createHandleUpdateField(setNodes),\n [setNodes]\n );\n\n const onUpdateInput = useMemo(\n () => createHandleUpdateInput(setNodes),\n [setNodes]\n );\n\n const onRemoveInputKey = useMemo(\n () => createHandleRemoveInputKey(setNodes),\n [setNodes]\n );\n\n const onUpdateOutput = useMemo(\n () => createHandleUpdateOutput(setNodes),\n [setNodes]\n );\n\n const onUpdateSections = useMemo(\n () => createHandleUpdateSections(setNodes),\n [setNodes]\n );\n\n const handleAddNode = useMemo(\n () =>\n createHandleAddNode(setNodes, setEdges, {\n fixedXPosition: FIXED_X_POSITION,\n verticalSpacing: VERTICAL_SPACING,\n nodeDefaults,\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n }),\n [\n scaffolderActionIds,\n scaffolderActionInputsById,\n scaffolderActionOutputsById,\n setNodes,\n setEdges,\n ]\n );\n\n const nodesWithHandlers = useMemo(\n () =>\n nodes.map((node) => {\n const stepOutputReferences = stepOutputReferencesByNode[node.id] ?? [];\n if (node.type === \"parametersNode\") {\n const data = node.data as ParametersNodeData;\n return {\n ...node,\n data: {\n ...data,\n onAddNode: handleAddNode,\n onUpdateSections,\n stepOutputReferences,\n },\n };\n }\n if (node.type === \"outputNode\") {\n const data = node.data as OutputNodeData;\n return {\n ...node,\n data: {\n ...data,\n onAddNode: handleAddNode,\n onUpdateOutput,\n stepOutputReferences,\n },\n };\n }\n\n const data = node.data as ActionNodeData;\n return {\n ...node,\n data: {\n ...data,\n onAddNode: handleAddNode,\n onUpdateField,\n onUpdateInput,\n onRemoveInputKey,\n stepOutputReferences,\n },\n };\n }),\n [\n nodes,\n handleAddNode,\n onUpdateField,\n onUpdateInput,\n onRemoveInputKey,\n onUpdateOutput,\n onUpdateSections,\n stepOutputReferencesByNode,\n ]\n );\n\n const stepsFromNodes = useMemo(() => extractStepsFromNodes(nodes), [nodes]);\n\n const parametersFromNodes = useMemo(\n () => extractParametersFromNodes(nodes),\n [nodes]\n );\n\n const outputFromNodes = useMemo(() => extractOutputFromNodes(nodes), [nodes]);\n\n const normalizedOutputFromNodes = outputFromNodes ?? null;\n\n useEffect(() => {\n if (!onStepsChange && !onParametersChange && !onOutputChange) {\n return;\n }\n const serialized = stableStringify({\n steps: stepsFromNodes,\n parameters: parametersFromNodes ?? null,\n output: normalizedOutputFromNodes,\n });\n if (serialized === lastEmittedModelHashRef.current) {\n return;\n }\n lastEmittedModelHashRef.current = serialized;\n skipNextModelHashRef.current = serialized;\n if (onStepsChange) {\n onStepsChange(stepsFromNodes);\n }\n if (onParametersChange) {\n onParametersChange(parametersFromNodes ?? undefined);\n }\n if (onOutputChange) {\n onOutputChange(outputFromNodes);\n }\n }, [\n stepsFromNodes,\n parametersFromNodes,\n normalizedOutputFromNodes,\n outputFromNodes,\n onStepsChange,\n onParametersChange,\n onOutputChange,\n ]);\n\n const fitViewOptions = useMemo(() => ({ padding: 0.2, duration: 300 }), []);\n const [reactFlowInstance, setReactFlowInstance] =\n useState<ReactFlowInstance | null>(null);\n\n const fitFlowToView = useCallback(() => {\n if (!reactFlowInstance) {\n return;\n }\n reactFlowInstance.fitView(fitViewOptions);\n }, [fitViewOptions, reactFlowInstance]);\n\n useEffect(() => {\n if (!reactFlowInstance) {\n return;\n }\n if (!shouldAutoFitViewRef.current) {\n return;\n }\n shouldAutoFitViewRef.current = false;\n fitFlowToView();\n }, [fitFlowToView, nodes, edges, reactFlowInstance]);\n\n useEffect(() => {\n if (!reactFlowInstance) {\n return undefined;\n }\n window.addEventListener(\"resize\", fitFlowToView);\n return () => {\n window.removeEventListener(\"resize\", fitFlowToView);\n };\n }, [fitFlowToView, reactFlowInstance]);\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", minHeight: \"70vh\" }}>\n <ReactFlow\n nodes={nodesWithHandlers}\n edges={edges}\n nodeTypes={nodeTypes}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeDragStop={onNodeDragStop}\n onConnect={onConnect}\n fitView\n onInit={setReactFlowInstance}\n fitViewOptions={fitViewOptions}\n />\n </div>\n );\n}\n"],"names":["data"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Position } from '@xyflow/react';
|
|
2
|
+
import { ActionNode } from '../Nodes/ActionNode.esm.js';
|
|
3
|
+
import { ParametersNode } from '../Nodes/ParametersNode.esm.js';
|
|
4
|
+
import { OutputNode } from '../Nodes/OutputNode.esm.js';
|
|
5
|
+
|
|
6
|
+
const FLOW_LAYOUT = {
|
|
7
|
+
verticalSpacing: 400,
|
|
8
|
+
fixedXPosition: 100
|
|
9
|
+
};
|
|
10
|
+
const nodeTypes = {
|
|
11
|
+
parametersNode: ParametersNode,
|
|
12
|
+
actionNode: ActionNode,
|
|
13
|
+
outputNode: OutputNode
|
|
14
|
+
};
|
|
15
|
+
const nodeDefaults = {
|
|
16
|
+
sourcePosition: Position.Bottom,
|
|
17
|
+
targetPosition: Position.Top
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { FLOW_LAYOUT, nodeDefaults, nodeTypes };
|
|
21
|
+
//# sourceMappingURL=flowConfig.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flowConfig.esm.js","sources":["../../../src/components/DesignerFlow/flowConfig.ts"],"sourcesContent":["import { Position } from \"@xyflow/react\";\nimport { ActionNode } from \"../Nodes/ActionNode\";\nimport { ParametersNode } from \"../Nodes/ParametersNode\";\nimport { OutputNode } from \"../Nodes/OutputNode\";\n\n// Centralized flow layout + node type definitions consumed across DesignerFlow.\n\nexport const FLOW_LAYOUT = {\n verticalSpacing: 400,\n fixedXPosition: 100,\n} as const;\n\nexport const nodeTypes = {\n parametersNode: ParametersNode,\n actionNode: ActionNode,\n outputNode: OutputNode,\n};\n\nexport const nodeDefaults = {\n sourcePosition: Position.Bottom,\n targetPosition: Position.Top,\n};\n"],"names":[],"mappings":";;;;;AAOO,MAAM,WAAA,GAAc;AAAA,EACzB,eAAA,EAAiB,GAAA;AAAA,EACjB,cAAA,EAAgB;AAClB;AAEO,MAAM,SAAA,GAAY;AAAA,EACvB,cAAA,EAAgB,cAAA;AAAA,EAChB,UAAA,EAAY,UAAA;AAAA,EACZ,UAAA,EAAY;AACd;AAEO,MAAM,YAAA,GAAe;AAAA,EAC1B,gBAAgB,QAAA,CAAS,MAAA;AAAA,EACzB,gBAAgB,QAAA,CAAS;AAC3B;;;;"}
|