Compare commits

..

8 Commits

Author SHA1 Message Date
Rıdvan Akca
582228d3af feat(ntfy/send-message): add priority and tag fields 2024-06-04 15:07:34 +02:00
Ali BARIN
751eb41e72 Merge pull request #1817 from automatisch/new-editor-feature-flag
feat: introduce feature flag for new flow editor
2024-06-04 12:45:00 +02:00
kasia.oczkowska
f08dc25711 feat: introduce style and behavior improvements 2024-06-04 07:18:18 +00:00
kasia.oczkowska
737eb31776 feat: introduce custom edges, auto layout improvements and node data updates 2024-06-04 07:17:38 +00:00
kasia.oczkowska
d6abf283bc feat: introduce automatic layout for new flow editor 2024-06-04 07:17:38 +00:00
kasia.oczkowska
bac4ab5aa4 feat: introduce feature flag for new flow editor 2024-06-04 07:17:38 +00:00
Ali BARIN
b5839390fd Merge pull request #1911 from automatisch/render-yaml-fix
fix(render.yaml): correct docker contexts
2024-06-03 11:21:31 +02:00
Ali BARIN
d19271dae1 fix(render.yaml): correct docker contexts 2024-06-03 10:23:28 +02:00
37 changed files with 1051 additions and 362 deletions

View File

@@ -1,29 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Detect language',
key: 'detectLanguage',
description: 'Detects language of a text.',
arguments: [
{
label: 'Text',
key: 'text',
type: 'string',
required: true,
description: 'The text to detect.',
variables: true,
},
],
async run($) {
const { text } = $.step.parameters;
const body = {
q: text,
};
const response = await $.http.post('/detect', body);
$.setActionItem({ raw: response.data[0] });
},
});

View File

@@ -1,4 +0,0 @@
import detectLanguage from './detect-language/index.js';
import translateText from './translate-text/index.js';
export default [detectLanguage, translateText];

View File

@@ -1,86 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Translate text',
key: 'translateText',
description: 'Translate a text.',
arguments: [
{
label: 'Text',
key: 'text',
type: 'string',
required: true,
description: 'The text to translate.',
variables: true,
},
{
label: 'Source Language',
key: 'sourceLanguage',
type: 'dropdown',
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listLanguages',
},
],
},
},
{
label: 'Target Language',
key: 'targetLanguage',
type: 'dropdown',
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listLanguages',
},
],
},
},
{
label: 'Format',
key: 'format',
type: 'dropdown',
description: '',
required: false,
variables: true,
options: [
{
label: 'Text',
value: 'text',
},
{
label: 'HTML',
value: 'html',
},
],
},
],
async run($) {
const { text, sourceLanguage, targetLanguage, format } = $.step.parameters;
const body = {
q: text,
source: sourceLanguage,
target: targetLanguage,
format,
};
const response = await $.http.post('/translate', body);
$.setActionItem({ raw: response.data });
},
});

View File

@@ -1,28 +0,0 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="187.04305" height="188.81523" viewBox="0 0 49.488472 49.957363" version="1.1" id="svg8">
<defs id="defs2">
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect835"/>
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect835-7"/>
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect874"/>
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect923"/>
</defs>
<metadata id="metadata5">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" transform="translate(-23.040803,-26.932047)">
<g id="g861" transform="translate(-42.114518,-17.993737)" style="fill:#000000">
<g aria-label="众" transform="matrix(4.3205134,0,0,4.3205134,-37.271798,-327.6536)" id="text833" style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835);fill:#000000;fill-opacity:1;stroke:none">
<path d="m 34.996103,90.121035 -0.614947,0.759641 q -2.754346,-1.41593 -3.948067,-2.950715 -1.167884,1.617467 -3.7672,2.888703 L 26.0096,90.084861 q 3.457142,-1.601964 4.283963,-3.849882 l 0.878496,0.273884 q -0.175699,0.516763 -0.232543,0.604613 1.116207,1.596797 4.056587,3.007559 z m 0.165364,4.91958 -0.676959,0.645954 q -1.514115,-1.157549 -2.346102,-2.826692 -0.547769,1.550288 -2.268589,2.806021 l -0.676959,-0.625283 q 1.19889,-0.795814 1.798334,-1.875848 0.599445,-1.080034 0.682127,-3.079906 l 0.909502,0.07751 q 0,0.268716 -0.04134,0.671791 l -0.03617,0.361734 q 0,0.273884 0.30489,1.033525 0.310058,0.754474 0.899167,1.467606 0.594277,0.707965 1.452103,1.343583 z m -4.800725,-1.374588 -0.702797,0.63045 q -0.594277,-0.780312 -1.162716,-1.276404 -0.651121,1.421098 -2.020542,2.676831 l -0.687295,-0.614948 q 1.229895,-1.095537 1.767329,-2.201409 0.5426,-1.105872 0.697629,-2.795686 l 0.919838,0.09819 q -0.103353,0.940508 -0.366902,1.91719 1.00252,0.862993 1.555456,1.565791 z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Adobe Garamond Pro';-inkscape-font-specification:'Adobe Garamond Pro Bold';fill:#00000" id="path961"/>
</g>
<g aria-label="L" id="text841" style="font-style:normal;font-weight:normal;font-size:43.3964px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.08492">
<path d="M 84.81389,94.883148 V 91.324643 H 69.191186 V 63.247172 h -4.035865 v 31.635976 z" style="fill:#000000;stroke-width:1.08492" id="path964"/>
</g>
</g>
<g id="g921" transform="translate(29.198135,-14.725175)"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,44 +0,0 @@
import verifyCredentials from './verify-credentials.js';
import isStillVerified from './is-still-verified.js';
export default {
fields: [
{
key: 'screenName',
label: 'Screen Name',
type: 'string',
required: true,
readOnly: false,
value: null,
placeholder: null,
description:
'Screen name of your connection to be used on Automatisch UI.',
clickToCopy: false,
},
{
key: 'instanceUrl',
label: 'Instance URL',
type: 'string',
required: false,
readOnly: false,
value: null,
placeholder: null,
description: 'For the self hosted version.',
clickToCopy: false,
},
{
key: 'apiKey',
label: 'API Key',
type: 'string',
required: false,
readOnly: false,
value: null,
placeholder: null,
description: 'LibreTranslate API key of your account.',
clickToCopy: false,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -1,8 +0,0 @@
import verifyCredentials from './verify-credentials.js';
const isStillVerified = async ($) => {
await verifyCredentials($);
return true;
};
export default isStillVerified;

View File

@@ -1,14 +0,0 @@
const verifyCredentials = async ($) => {
const body = {
q: 'Hi!',
};
await $.http.post('/detect', body);
await $.auth.set({
screenName: $.auth.data.screenName,
apiKey: $.auth.data.apiKey,
});
};
export default verifyCredentials;

View File

@@ -1,9 +0,0 @@
const addApiKey = ($, requestConfig) => {
const apiKey = $.auth.data.apiKey;
requestConfig.data = { api_key: apiKey, ...(requestConfig.data || {}) };
return requestConfig;
};
export default addApiKey;

View File

@@ -1,16 +0,0 @@
const setBaseUrl = ($, requestConfig) => {
const instanceUrl = $.auth.data.instanceUrl;
const apiKey = $.auth.data.apiKey;
if (instanceUrl) {
requestConfig.baseURL = instanceUrl;
} else if ($.app.apiBaseUrl) {
requestConfig.baseURL = $.app.apiBaseUrl;
}
requestConfig.data = { api_key: apiKey, ...(requestConfig.data || {}) };
return requestConfig;
};
export default setBaseUrl;

View File

@@ -1,3 +0,0 @@
import listLanguages from './list-languages/index.js';
export default [listLanguages];

View File

@@ -1,25 +0,0 @@
export default {
name: 'List languages',
key: 'listLanguages',
async run($) {
const languages = {
data: [],
};
const { data } = await $.http.get('/languages');
if (!data?.length) {
return;
}
for (const language of data) {
languages.data.push({
value: language.code,
name: language.name,
});
}
return languages;
},
};

View File

@@ -1,21 +0,0 @@
import defineApp from '../../helpers/define-app.js';
import auth from './auth/index.js';
import addApiKey from './common/add-api-key.js';
import setBaseUrl from './common/set-base-url.js';
import actions from './actions/index.js';
import dynamicData from './dynamic-data/index.js';
export default defineApp({
name: 'LibreTranslate',
key: 'libretranslate',
iconUrl: '{BASE_URL}/apps/libretranslate/assets/favicon.svg',
authDocUrl: '{DOCS_URL}/apps/libretranslate/connection',
supportsConnections: true,
baseUrl: 'https://libretranslate.com',
apiBaseUrl: 'https://libretranslate.com',
primaryColor: 'ffffff',
beforeRequest: [setBaseUrl, addApiKey],
auth,
actions,
dynamicData,
});

View File

@@ -71,11 +71,77 @@ export default defineAction({
'Timestamp or duration for delayed delivery. For example, 30min or 9am.',
variables: true,
},
{
label: 'Priority',
key: 'priority',
type: 'dropdown',
required: false,
description: '',
value: 3,
variables: true,
options: [
{ label: 'Max Priority', value: 5 },
{ label: 'High Priority', value: 4 },
{ label: 'Default Priority', value: 3 },
{ label: 'Low Priority', value: 2 },
{ label: 'Min Priority', value: 1 },
],
},
{
label: 'Tags',
key: 'tags',
type: 'dynamic',
required: false,
description: '',
fields: [
{
label: 'Tag',
key: 'tag',
type: 'dropdown',
required: false,
description: '',
variables: true,
options: [
{ label: '👍', value: '+1' },
{ label: '🥳', value: 'partying_face' },
{ label: '🎉', value: 'tada' },
{ label: '✔', value: 'heavy_check_mark' },
{ label: '📢', value: 'loudspeaker' },
{ label: '👎', value: '-1' },
{ label: '⚠', value: 'warning' },
{ label: '🚨', value: 'rotating_light' },
{ label: '🚩', value: 'triangular_flag_on_post' },
{ label: '💀', value: 'skull' },
{ label: '🤦‍♂️', value: 'facepalm' },
{ label: '⛔️', value: 'no_entry' },
{ label: '🚫', value: 'no_entry_sign' },
{ label: '💿', value: 'cd' },
{ label: '💻', value: 'computer' },
],
},
],
},
],
async run($) {
const { topic, message, title, email, click, attach, filename, delay } =
$.step.parameters;
const {
topic,
message,
title,
email,
click,
attach,
filename,
delay,
priority,
tags,
} = $.step.parameters;
const allTags = tags
.map((tag) => tag.tag)
.filter(Boolean)
.join(',');
const payload = {
topic,
message,
@@ -87,7 +153,12 @@ export default defineAction({
delay,
};
const response = await $.http.post('/', payload);
const response = await $.http.post('/', payload, {
headers: {
'X-Priority': priority,
'X-Tags': allTags,
},
});
$.setActionItem({
raw: response.data,

View File

@@ -243,15 +243,6 @@ export default defineConfig({
{ text: 'Connection', link: '/apps/invoice-ninja/connection' },
],
},
{
text: 'LibreTranslate',
collapsible: true,
collapsed: true,
items: [
{ text: 'Actions', link: '/apps/libretranslate/actions' },
{ text: 'Connection', link: '/apps/libretranslate/connection' },
],
},
{
text: 'Mattermost',
collapsible: true,

View File

@@ -1,12 +0,0 @@
---
favicon: /favicons/libretranslate.svg
items:
- name: Translate text
desc: Translate a text.
---
<script setup>
import CustomListing from '../../components/CustomListing.vue'
</script>
<CustomListing />

View File

@@ -1,12 +0,0 @@
# LibreTranslate
:::info
This page explains the steps you need to follow to set up the LibreTranslate
connection in Automatisch. If any of the steps are outdated, please let us know!
:::
1. Login to your LibreTranslate account.
2. Copy your `API key` from the page to the `API Key` field on Automatisch.
3. If you are using a self hosted version of LibreTranslate, copy your `Instance URl` to the `Instance URL` field on Automatisch.
4. Write any screen name to be displayed in Automatisch.
5. Now, you can start using the LibreTranslate connection with Automatisch.

View File

@@ -25,7 +25,6 @@ The following integrations are currently supported by Automatisch.
- [HTTP Request](/apps/http-request/actions)
- [HubSpot](/apps/hubspot/actions)
- [Invoice Ninja](/apps/invoice-ninja/triggers)
- [LibreTranslate](/apps/libretranslate/actions)
- [Mattermost](/apps/mattermost/actions)
- [Miro](/apps/miro/actions)
- [Notion](/apps/notion/triggers)

View File

@@ -1,28 +0,0 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="187.04305" height="188.81523" viewBox="0 0 49.488472 49.957363" version="1.1" id="svg8">
<defs id="defs2">
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect835"/>
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect835-7"/>
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect874"/>
<rect x="25.162016" y="84.327377" width="71.115189" height="52.835255" id="rect923"/>
</defs>
<metadata id="metadata5">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" transform="translate(-23.040803,-26.932047)">
<g id="g861" transform="translate(-42.114518,-17.993737)" style="fill:#000000">
<g aria-label="众" transform="matrix(4.3205134,0,0,4.3205134,-37.271798,-327.6536)" id="text833" style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect835);fill:#000000;fill-opacity:1;stroke:none">
<path d="m 34.996103,90.121035 -0.614947,0.759641 q -2.754346,-1.41593 -3.948067,-2.950715 -1.167884,1.617467 -3.7672,2.888703 L 26.0096,90.084861 q 3.457142,-1.601964 4.283963,-3.849882 l 0.878496,0.273884 q -0.175699,0.516763 -0.232543,0.604613 1.116207,1.596797 4.056587,3.007559 z m 0.165364,4.91958 -0.676959,0.645954 q -1.514115,-1.157549 -2.346102,-2.826692 -0.547769,1.550288 -2.268589,2.806021 l -0.676959,-0.625283 q 1.19889,-0.795814 1.798334,-1.875848 0.599445,-1.080034 0.682127,-3.079906 l 0.909502,0.07751 q 0,0.268716 -0.04134,0.671791 l -0.03617,0.361734 q 0,0.273884 0.30489,1.033525 0.310058,0.754474 0.899167,1.467606 0.594277,0.707965 1.452103,1.343583 z m -4.800725,-1.374588 -0.702797,0.63045 q -0.594277,-0.780312 -1.162716,-1.276404 -0.651121,1.421098 -2.020542,2.676831 l -0.687295,-0.614948 q 1.229895,-1.095537 1.767329,-2.201409 0.5426,-1.105872 0.697629,-2.795686 l 0.919838,0.09819 q -0.103353,0.940508 -0.366902,1.91719 1.00252,0.862993 1.555456,1.565791 z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Adobe Garamond Pro';-inkscape-font-specification:'Adobe Garamond Pro Bold';fill:#00000" id="path961"/>
</g>
<g aria-label="L" id="text841" style="font-style:normal;font-weight:normal;font-size:43.3964px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.08492">
<path d="M 84.81389,94.883148 V 91.324643 H 69.191186 V 63.247172 h -4.035865 v 31.635976 z" style="fill:#000000;stroke-width:1.08492" id="path964"/>
</g>
</g>
<g id="g921" transform="translate(29.198135,-14.725175)"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -7,6 +7,7 @@
"@apollo/client": "^3.6.9",
"@casl/ability": "^6.5.0",
"@casl/react": "^3.1.0",
"@dagrejs/dagre": "^1.1.2",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@hookform/resolvers": "^2.8.8",
@@ -32,6 +33,7 @@
"react-router-dom": "^6.0.2",
"react-scripts": "5.0.0",
"react-window": "^1.8.9",
"reactflow": "^11.11.2",
"slate": "^0.94.1",
"slate-history": "^0.93.0",
"slate-react": "^0.94.2",

View File

@@ -165,6 +165,7 @@ function ChooseAppAndEventSubstep(props) {
value={getOption(appOptions, step.appKey) || null}
onChange={onAppChange}
data-test="choose-app-autocomplete"
componentsProps={{ popper: { className: 'nowheel' } }}
/>
{step.appKey && (
@@ -227,6 +228,7 @@ function ChooseAppAndEventSubstep(props) {
value={getOption(actionOrTriggerOptions, step.key) || null}
onChange={onEventChange}
data-test="choose-event-autocomplete"
componentsProps={{ popper: { className: 'nowheel' } }}
/>
</Box>
)}

View File

@@ -240,6 +240,7 @@ function ChooseConnectionSubstep(props) {
onChange={handleChange}
loading={isAppConnectionsLoading}
data-test="choose-connection-autocomplete"
componentsProps={{ popper: { className: 'nowheel' } }}
/>
<Button

View File

@@ -32,9 +32,11 @@ function ControlledAutocomplete(props) {
...autocompleteProps
} = props;
let dependsOnValues = [];
if (dependsOn?.length) {
dependsOnValues = watch(dependsOn);
}
React.useEffect(() => {
const hasDependencies = dependsOnValues.length;
const allDepsSatisfied = dependsOnValues.every(Boolean);
@@ -44,6 +46,7 @@ function ControlledAutocomplete(props) {
resetField(name);
}
}, dependsOnValues);
return (
<Controller
rules={{ required }}

View File

@@ -47,6 +47,7 @@ const CustomOptions = (props) => {
},
},
]}
className="nowheel"
>
<Paper elevation={5} sx={{ width: '100%' }}>
<Tabs

View File

@@ -8,6 +8,7 @@ import Tooltip from '@mui/material/Tooltip';
import IconButton from '@mui/material/IconButton';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import Snackbar from '@mui/material/Snackbar';
import { ReactFlowProvider } from 'reactflow';
import { EditorProvider } from 'contexts/Editor';
import EditableTypography from 'components/EditableTypography';
@@ -20,6 +21,9 @@ import * as URLS from 'config/urls';
import { TopBar } from './style';
import useFlow from 'hooks/useFlow';
import { useQueryClient } from '@tanstack/react-query';
import EditorNew from 'components/EditorNew/EditorNew';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
export default function EditorLayout() {
const { flowId } = useParams();
@@ -131,15 +135,28 @@ export default function EditorLayout() {
</Button>
</Box>
</TopBar>
<Stack direction="column" height="100%">
<Container maxWidth="md">
<EditorProvider value={{ readOnly: !!flow?.active }}>
{!flow && !isFlowLoading && 'not found'}
{flow && <Editor flow={flow} />}
</EditorProvider>
</Container>
</Stack>
{useNewFlowEditor ? (
<Stack direction="column" height="100%" flexGrow={1}>
<Stack direction="column" flexGrow={1}>
<EditorProvider value={{ readOnly: !!flow?.active }}>
<ReactFlowProvider>
{!flow && !isFlowLoading && 'not found'}
{flow && <EditorNew flow={flow} />}
</ReactFlowProvider>
</EditorProvider>
</Stack>
</Stack>
) : (
<Stack direction="column" height="100%">
<Container maxWidth="md">
<EditorProvider value={{ readOnly: !!flow?.active }}>
{!flow && !isFlowLoading && 'not found'}
{flow && <Editor flow={flow} />}
</EditorProvider>
</Container>
</Stack>
)}
<Snackbar
data-test="flow-cannot-edit-info-snackbar"

View File

@@ -0,0 +1,79 @@
import { EdgeLabelRenderer, getStraightPath } from 'reactflow';
import IconButton from '@mui/material/IconButton';
import AddIcon from '@mui/icons-material/Add';
import { useMutation } from '@apollo/client';
import { CREATE_STEP } from 'graphql/mutations/create-step';
import { useQueryClient } from '@tanstack/react-query';
import PropTypes from 'prop-types';
export default function Edge({
sourceX,
sourceY,
targetX,
targetY,
source,
data: { flowId, setCurrentStepId, flowActive, layouted },
}) {
const [createStep, { loading: creationInProgress }] =
useMutation(CREATE_STEP);
const queryClient = useQueryClient();
const [edgePath, labelX, labelY] = getStraightPath({
sourceX,
sourceY,
targetX,
targetY,
});
const addStep = async (previousStepId) => {
const mutationInput = {
previousStep: {
id: previousStepId,
},
flow: {
id: flowId,
},
};
const createdStep = await createStep({
variables: { input: mutationInput },
});
const createdStepId = createdStep.data.createStep.id;
setCurrentStepId(createdStepId);
await queryClient.invalidateQueries({ queryKey: ['flows', flowId] });
};
return (
<>
<EdgeLabelRenderer>
<IconButton
onClick={() => addStep(source)}
color="primary"
sx={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
pointerEvents: 'all',
visibility: layouted ? 'visible' : 'hidden',
}}
disabled={creationInProgress || flowActive}
>
<AddIcon />
</IconButton>
</EdgeLabelRenderer>
</>
);
}
Edge.propTypes = {
sourceX: PropTypes.number.isRequired,
sourceY: PropTypes.number.isRequired,
targetX: PropTypes.number.isRequired,
targetY: PropTypes.number.isRequired,
source: PropTypes.string.isRequired,
data: PropTypes.shape({
flowId: PropTypes.string.isRequired,
setCurrentStepId: PropTypes.func.isRequired,
flowActive: PropTypes.bool.isRequired,
layouted: PropTypes.bool,
}).isRequired,
};

View File

@@ -0,0 +1,258 @@
import { useEffect, useState, useCallback } from 'react';
import { useMutation } from '@apollo/client';
import { useQueryClient } from '@tanstack/react-query';
import { FlowPropType } from 'propTypes/propTypes';
import ReactFlow, { useNodesState, useEdgesState, addEdge } from 'reactflow';
import 'reactflow/dist/style.css';
import { Stack } from '@mui/material';
import { UPDATE_STEP } from 'graphql/mutations/update-step';
import { useAutoLayout } from './useAutoLayout';
import { useScrollBoundries } from './useScrollBoundries';
import FlowStepNode from './FlowStepNode/FlowStepNode';
import Edge from './Edge/Edge';
import InvisibleNode from './InvisibleNode/InvisibleNode';
import { EditorWrapper } from './style';
const nodeTypes = { flowStep: FlowStepNode, invisible: InvisibleNode };
const edgeTypes = {
addNodeEdge: Edge,
};
const INVISIBLE_NODE_ID = 'invisible-node';
const generateEdgeId = (sourceId, targetId) => `${sourceId}-${targetId}`;
const EditorNew = ({ flow }) => {
const [triggerStep] = flow.steps;
const [currentStepId, setCurrentStepId] = useState(triggerStep.id);
const [updateStep] = useMutation(UPDATE_STEP);
const queryClient = useQueryClient();
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
useAutoLayout();
useScrollBoundries();
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
[setEdges],
);
const openNextStep = useCallback(
(nextStep) => () => {
setCurrentStepId(nextStep?.id);
},
[],
);
const onStepChange = useCallback(
async (step) => {
const mutationInput = {
id: step.id,
key: step.key,
parameters: step.parameters,
connection: {
id: step.connection?.id,
},
flow: {
id: flow.id,
},
};
if (step.appKey) {
mutationInput.appKey = step.appKey;
}
await updateStep({
variables: { input: mutationInput },
});
await queryClient.invalidateQueries({
queryKey: ['steps', step.id, 'connection'],
});
await queryClient.invalidateQueries({ queryKey: ['flows', flow.id] });
},
[flow.id, updateStep, queryClient],
);
const generateEdges = useCallback((flow, prevEdges) => {
const newEdges =
flow.steps
.map((step, i) => {
const sourceId = step.id;
const targetId = flow.steps[i + 1]?.id;
const edge = prevEdges?.find(
(edge) => edge.id === generateEdgeId(sourceId, targetId),
);
if (targetId) {
return {
id: generateEdgeId(sourceId, targetId),
source: sourceId,
target: targetId,
type: 'addNodeEdge',
data: {
flowId: flow.id,
flowActive: flow.active,
setCurrentStepId,
layouted: !!edge,
},
};
}
})
.filter((edge) => !!edge) || [];
const lastStep = flow.steps[flow.steps.length - 1];
return lastStep
? [
...newEdges,
{
id: generateEdgeId(lastStep.id, INVISIBLE_NODE_ID),
source: lastStep.id,
target: INVISIBLE_NODE_ID,
type: 'addNodeEdge',
data: {
flowId: flow.id,
flowActive: flow.active,
setCurrentStepId,
layouted: false,
},
},
]
: newEdges;
}, []);
const generateNodes = useCallback(
(flow, prevNodes) => {
const newNodes = flow.steps.map((step, index) => {
const node = prevNodes?.find(({ id }) => id === step.id);
const collapsed = currentStepId !== step.id;
return {
id: step.id,
type: 'flowStep',
position: {
x: node ? node.position.x : 0,
y: node ? node.position.y : 0,
},
zIndex: collapsed ? 0 : 1,
data: {
step,
index: index,
flowId: flow.id,
collapsed,
openNextStep: openNextStep(flow.steps[index + 1]),
onOpen: () => setCurrentStepId(step.id),
onClose: () => setCurrentStepId(null),
onChange: onStepChange,
layouted: !!node,
},
};
});
const prevInvisibleNode = nodes.find((node) => node.type === 'invisible');
return [
...newNodes,
{
id: INVISIBLE_NODE_ID,
type: 'invisible',
position: {
x: prevInvisibleNode ? prevInvisibleNode.position.x : 0,
y: prevInvisibleNode ? prevInvisibleNode.position.y : 0,
},
},
];
},
[currentStepId, nodes, onStepChange, openNextStep],
);
const updateNodesData = useCallback(
(steps) => {
setNodes((nodes) =>
nodes.map((node) => {
const step = steps.find((step) => step.id === node.id);
if (step) {
return { ...node, data: { ...node.data, step: { ...step } } };
}
return node;
}),
);
},
[setNodes],
);
const updateEdgesData = useCallback(
(flow) => {
setEdges((edges) =>
edges.map((edge) => {
return {
...edge,
data: { ...edge.data, flowId: flow.id, flowActive: flow.active },
};
}),
);
},
[setEdges],
);
useEffect(() => {
setNodes(
nodes.map((node) => {
if (node.type === 'flowStep') {
const collapsed = currentStepId !== node.data.step.id;
return {
...node,
zIndex: collapsed ? 0 : 1,
data: {
...node.data,
collapsed,
},
};
}
return node;
}),
);
}, [currentStepId]);
useEffect(() => {
if (flow.steps.length + 1 !== nodes.length) {
const newNodes = generateNodes(flow, nodes);
const newEdges = generateEdges(flow, edges);
setNodes(newNodes);
setEdges(newEdges);
} else {
updateNodesData(flow.steps);
updateEdgesData(flow);
}
}, [flow]);
return (
<EditorWrapper direction="column">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
panOnScroll
panOnScrollMode="vertical"
panOnDrag={false}
zoomOnScroll={false}
zoomOnPinch={false}
zoomOnDoubleClick={false}
panActivationKeyCode={null}
/>
</EditorWrapper>
);
};
EditorNew.propTypes = {
flow: FlowPropType.isRequired,
};
export default EditorNew;

View File

@@ -0,0 +1,72 @@
import { Handle, Position } from 'reactflow';
import { Box } from '@mui/material';
import PropTypes from 'prop-types';
import FlowStep from 'components/FlowStep';
import { StepPropType } from 'propTypes/propTypes';
import { NodeWrapper, NodeInnerWrapper } from './style.js';
function FlowStepNode({
data: {
step,
index,
flowId,
collapsed,
openNextStep,
onOpen,
onClose,
onChange,
layouted,
},
}) {
return (
<NodeWrapper
className="nodrag"
sx={{
visibility: layouted ? 'visible' : 'hidden',
}}
>
<NodeInnerWrapper>
<Handle
type="target"
position={Position.Top}
isConnectable={false}
style={{ visibility: 'hidden' }}
/>
<FlowStep
step={step}
index={index + 1}
collapsed={collapsed}
onOpen={onOpen}
onClose={onClose}
onChange={onChange}
flowId={flowId}
onContinue={openNextStep}
/>
<Handle
type="source"
position={Position.Bottom}
isConnectable={false}
style={{ visibility: 'hidden' }}
/>
</NodeInnerWrapper>
</NodeWrapper>
);
}
FlowStepNode.propTypes = {
data: PropTypes.shape({
step: StepPropType.isRequired,
index: PropTypes.number.isRequired,
flowId: PropTypes.string.isRequired,
collapsed: PropTypes.bool.isRequired,
openNextStep: PropTypes.func.isRequired,
onOpen: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
layouted: PropTypes.bool.isRequired,
}).isRequired,
};
export default FlowStepNode;

View File

@@ -0,0 +1,14 @@
import { styled } from '@mui/material/styles';
import { Box } from '@mui/material';
export const NodeWrapper = styled(Box)(({ theme }) => ({
width: '100vw',
display: 'flex',
justifyContent: 'center',
padding: theme.spacing(0, 2.5),
}));
export const NodeInnerWrapper = styled(Box)(({ theme }) => ({
maxWidth: 900,
flex: 1,
}));

View File

@@ -0,0 +1,19 @@
import { Handle, Position } from 'reactflow';
import { Box } from '@mui/material';
// This node is used for adding an edge with add node button after the last flow step node
function InvisibleNode() {
return (
<Box
maxWidth={900}
width="100vw"
className="nodrag"
sx={{ visibility: 'hidden' }}
>
<Handle type="target" position={Position.Top} isConnectable={false} />
Invisible node
</Box>
);
}
export default InvisibleNode;

View File

@@ -0,0 +1,13 @@
import { Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
export const EditorWrapper = styled(Stack)(({ theme }) => ({
flexGrow: 1,
'& > div': {
flexGrow: 1,
},
'& .react-flow__pane, & .react-flow__node': {
cursor: 'auto !important',
},
}));

View File

@@ -0,0 +1,69 @@
import { useCallback, useEffect } from 'react';
import Dagre from '@dagrejs/dagre';
import { usePrevious } from 'hooks/usePrevious';
import { isEqual } from 'lodash';
import { useNodesInitialized, useNodes, useReactFlow } from 'reactflow';
const getLayoutedElements = (nodes, edges) => {
const graph = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
graph.setGraph({
rankdir: 'TB',
marginy: 60,
ranksep: 64,
});
edges.forEach((edge) => graph.setEdge(edge.source, edge.target));
nodes.forEach((node) => graph.setNode(node.id, node));
Dagre.layout(graph);
return {
nodes: nodes.map((node) => {
const { x, y, width, height } = graph.node(node.id);
return {
...node,
position: { x: x - width / 2, y: y - height / 2 },
};
}),
edges,
};
};
export const useAutoLayout = () => {
const nodes = useNodes();
const prevNodes = usePrevious(nodes);
const nodesInitialized = useNodesInitialized();
const { getEdges, setNodes, setEdges } = useReactFlow();
const onLayout = useCallback(
(nodes, edges) => {
const layoutedElements = getLayoutedElements(nodes, edges);
setNodes([
...layoutedElements.nodes.map((node) => ({
...node,
data: { ...node.data, layouted: true },
})),
]);
setEdges([
...layoutedElements.edges.map((edge) => ({
...edge,
data: { ...edge.data, layouted: true },
})),
]);
},
[setEdges, setNodes],
);
useEffect(() => {
const shouldAutoLayout =
nodesInitialized &&
!isEqual(
nodes.map(({ width, height }) => ({ width, height })),
prevNodes.map(({ width, height }) => ({ width, height })),
);
if (shouldAutoLayout) {
onLayout(nodes, getEdges());
}
}, [nodes]);
};

View File

@@ -0,0 +1,13 @@
import { useEffect } from 'react';
import { useViewport, useReactFlow } from 'reactflow';
export const useScrollBoundries = () => {
const { setViewport } = useReactFlow();
const { x, y, zoom } = useViewport();
useEffect(() => {
if (y > 0) {
setViewport({ x, y: 0, zoom });
}
}, [y]);
};

View File

@@ -80,6 +80,7 @@ export default function InputCreator(props) {
disabled={disabled}
showOptionValue={showOptionValue}
shouldUnregister={shouldUnregister}
componentsProps={{ popper: { className: 'nowheel' } }}
/>
)}

View File

@@ -17,6 +17,7 @@ import { StepExecutionsContext } from 'contexts/StepExecutions';
import Popper from './Popper';
import { processStepWithExecutions } from './data';
import { ChildrenWrapper, FakeInput, InputLabelWrapper } from './style';
const PowerInput = (props) => {
const { control } = useFormContext();
const {
@@ -31,33 +32,41 @@ const PowerInput = (props) => {
} = props;
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
const editorRef = React.useRef(null);
const renderElement = React.useCallback(
(props) => <Element {...props} />,
[],
);
const [editor] = React.useState(() => customizeEditor(createEditor()));
const [showVariableSuggestions, setShowVariableSuggestions] =
React.useState(false);
const disappearSuggestionsOnShift = (event) => {
if (event.code === 'Tab') {
setShowVariableSuggestions(false);
}
};
const stepsWithVariables = React.useMemo(() => {
return processStepWithExecutions(priorStepsWithExecutions);
}, [priorStepsWithExecutions]);
const handleBlur = React.useCallback(
(value) => {
onBlur?.(value);
},
[onBlur],
);
const handleVariableSuggestionClick = React.useCallback(
(variable) => {
insertVariable(editor, variable, stepsWithVariables);
},
[stepsWithVariables],
);
return (
<Controller
rules={{ required }}
@@ -127,6 +136,7 @@ const PowerInput = (props) => {
anchorEl={editorRef.current}
data={stepsWithVariables}
onSuggestionClick={handleVariableSuggestionClick}
className="nowheel"
/>
<FormHelperText variant="outlined">{description}</FormHelperText>

View File

@@ -0,0 +1,9 @@
import { useEffect, useRef } from "react";
export const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};

View File

@@ -3,7 +3,7 @@ services:
name: automatisch-main
env: docker
dockerfilePath: ./docker/Dockerfile
dockerContext: ./docker
dockerContext: .
repo: https://github.com/automatisch/automatisch
autoDeploy: false
envVars:
@@ -47,7 +47,7 @@ services:
name: automatisch-worker
env: docker
dockerfilePath: ./docker/Dockerfile
dockerContext: ./docker
dockerContext: .
repo: https://github.com/automatisch/automatisch
autoDeploy: false
envVars:

384
yarn.lock
View File

@@ -1455,6 +1455,18 @@
enabled "2.0.x"
kuler "^2.0.0"
"@dagrejs/dagre@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@dagrejs/dagre/-/dagre-1.1.2.tgz#5ec339979447091f48d2144deed8c70dfadae374"
integrity sha512-F09dphqvHsbe/6C2t2unbmpr5q41BNPEfJCdn8Z7aEBpVSy/zFQ/b4SWsweQjWNsYMDvE2ffNUN8X0CeFsEGNw==
dependencies:
"@dagrejs/graphlib" "2.2.2"
"@dagrejs/graphlib@2.2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@dagrejs/graphlib/-/graphlib-2.2.2.tgz#74154d5cb880a23b4fae71034a09b4b5aef06feb"
integrity sha512-CbyGpCDKsiTg/wuk79S7Muoj8mghDGAESWGxcSyhHX5jD35vYMBZochYVFzlHxynpE9unpu6O+4ZuhrLxASsOg==
"@docsearch/css@3.2.1", "@docsearch/css@^3.2.1":
version "3.2.1"
resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.2.1.tgz"
@@ -3333,6 +3345,72 @@
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
"@reactflow/background@11.3.12":
version "11.3.12"
resolved "https://registry.yarnpkg.com/@reactflow/background/-/background-11.3.12.tgz#9c9491cce4659bae13074fcdb48ac25664879d3f"
integrity sha512-jBuWVb43JQy5h4WOS7G0PU8voGTEJNA+qDmx8/jyBtrjbasTesLNfQvboTGjnQYYiJco6mw5vrtQItAJDNoIqw==
dependencies:
"@reactflow/core" "11.11.2"
classcat "^5.0.3"
zustand "^4.4.1"
"@reactflow/controls@11.2.12":
version "11.2.12"
resolved "https://registry.yarnpkg.com/@reactflow/controls/-/controls-11.2.12.tgz#85e2aa5de17e2af28a5ecf6a75bb9c828a20640b"
integrity sha512-L9F3+avFRShoprdT+5oOijm5gVsz2rqWCXBzOAgD923L1XFGIspdiHLLf8IlPGsT+mfl0GxbptZhaEeEzl1e3g==
dependencies:
"@reactflow/core" "11.11.2"
classcat "^5.0.3"
zustand "^4.4.1"
"@reactflow/core@11.11.2":
version "11.11.2"
resolved "https://registry.yarnpkg.com/@reactflow/core/-/core-11.11.2.tgz#c62f78297bda9d2e86a12228617ec3f91fbd4b22"
integrity sha512-+GfgyskweL1PsgRSguUwfrT2eDotlFgaKfDLm7x0brdzzPJY2qbCzVetaxedaiJmIli3817iYbILvE9qLKwbRA==
dependencies:
"@types/d3" "^7.4.0"
"@types/d3-drag" "^3.0.1"
"@types/d3-selection" "^3.0.3"
"@types/d3-zoom" "^3.0.1"
classcat "^5.0.3"
d3-drag "^3.0.0"
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
zustand "^4.4.1"
"@reactflow/minimap@11.7.12":
version "11.7.12"
resolved "https://registry.yarnpkg.com/@reactflow/minimap/-/minimap-11.7.12.tgz#6b2fc671ee17e37ccd3bc038ae8d2121d0ce6291"
integrity sha512-SRDU77c2PCF54PV/MQfkz7VOW46q7V1LZNOQlXAp7dkNyAOI6R+tb9qBUtUJOvILB+TCN6pRfD9fQ+2T99bW3Q==
dependencies:
"@reactflow/core" "11.11.2"
"@types/d3-selection" "^3.0.3"
"@types/d3-zoom" "^3.0.1"
classcat "^5.0.3"
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
zustand "^4.4.1"
"@reactflow/node-resizer@2.2.12":
version "2.2.12"
resolved "https://registry.yarnpkg.com/@reactflow/node-resizer/-/node-resizer-2.2.12.tgz#df82a7dfba883afea6a01a9c8210008a1ddba01f"
integrity sha512-6LHJGuI1zHyRrZHw5gGlVLIWnvVxid9WIqw8FMFSg+oF2DuS3pAPwSoZwypy7W22/gDNl9eD1Dcl/OtFtDFQ+w==
dependencies:
"@reactflow/core" "11.11.2"
classcat "^5.0.4"
d3-drag "^3.0.0"
d3-selection "^3.0.0"
zustand "^4.4.1"
"@reactflow/node-toolbar@1.3.12":
version "1.3.12"
resolved "https://registry.yarnpkg.com/@reactflow/node-toolbar/-/node-toolbar-1.3.12.tgz#89e7aa9d34b6213bb5e64c344d4e2e3cb7af3163"
integrity sha512-4kJRvNna/E3y2MZW9/80wTKwkhw4pLJiz3D5eQrD13XcmojSb1rArO9CiwyrI+rMvs5gn6NlCFB4iN1F+Q+lxQ==
dependencies:
"@reactflow/core" "11.11.2"
classcat "^5.0.3"
zustand "^4.4.1"
"@rollup/plugin-babel@^5.2.0":
version "5.3.0"
resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
@@ -3823,6 +3901,216 @@
dependencies:
"@types/node" "*"
"@types/d3-array@*":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5"
integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==
"@types/d3-axis@*":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.6.tgz#e760e5765b8188b1defa32bc8bb6062f81e4c795"
integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==
dependencies:
"@types/d3-selection" "*"
"@types/d3-brush@*":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.6.tgz#c2f4362b045d472e1b186cdbec329ba52bdaee6c"
integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==
dependencies:
"@types/d3-selection" "*"
"@types/d3-chord@*":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.6.tgz#1706ca40cf7ea59a0add8f4456efff8f8775793d"
integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==
"@types/d3-color@*":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
"@types/d3-contour@*":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.6.tgz#9ada3fa9c4d00e3a5093fed0356c7ab929604231"
integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==
dependencies:
"@types/d3-array" "*"
"@types/geojson" "*"
"@types/d3-delaunay@*":
version "6.0.4"
resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz#185c1a80cc807fdda2a3fe960f7c11c4a27952e1"
integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==
"@types/d3-dispatch@*":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz#096efdf55eb97480e3f5621ff9a8da552f0961e7"
integrity sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==
"@types/d3-drag@*", "@types/d3-drag@^3.0.1":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02"
integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
dependencies:
"@types/d3-selection" "*"
"@types/d3-dsv@*":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz#0a351f996dc99b37f4fa58b492c2d1c04e3dac17"
integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==
"@types/d3-ease@*":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b"
integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
"@types/d3-fetch@*":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz#c04a2b4f23181aa376f30af0283dbc7b3b569980"
integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==
dependencies:
"@types/d3-dsv" "*"
"@types/d3-force@*":
version "3.0.9"
resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.9.tgz#dd96ccefba4386fe4ff36b8e4ee4e120c21fcf29"
integrity sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==
"@types/d3-format@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.4.tgz#b1e4465644ddb3fdf3a263febb240a6cd616de90"
integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==
"@types/d3-geo@*":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.1.0.tgz#b9e56a079449174f0a2c8684a9a4df3f60522440"
integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==
dependencies:
"@types/geojson" "*"
"@types/d3-hierarchy@*":
version "3.1.7"
resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz#6023fb3b2d463229f2d680f9ac4b47466f71f17b"
integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==
"@types/d3-interpolate@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
dependencies:
"@types/d3-color" "*"
"@types/d3-path@*":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.0.tgz#2b907adce762a78e98828f0b438eaca339ae410a"
integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==
"@types/d3-polygon@*":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz#dfae54a6d35d19e76ac9565bcb32a8e54693189c"
integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==
"@types/d3-quadtree@*":
version "3.0.6"
resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz#d4740b0fe35b1c58b66e1488f4e7ed02952f570f"
integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==
"@types/d3-random@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.3.tgz#ed995c71ecb15e0cd31e22d9d5d23942e3300cfb"
integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==
"@types/d3-scale-chromatic@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz#fc0db9c10e789c351f4c42d96f31f2e4df8f5644"
integrity sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==
"@types/d3-scale@*":
version "4.0.8"
resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb"
integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==
dependencies:
"@types/d3-time" "*"
"@types/d3-selection@*", "@types/d3-selection@^3.0.3":
version "3.0.10"
resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.10.tgz#98cdcf986d0986de6912b5892e7c015a95ca27fe"
integrity sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==
"@types/d3-shape@*":
version "3.1.6"
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72"
integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==
dependencies:
"@types/d3-path" "*"
"@types/d3-time-format@*":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz#d6bc1e6b6a7db69cccfbbdd4c34b70632d9e9db2"
integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==
"@types/d3-time@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.3.tgz#3c186bbd9d12b9d84253b6be6487ca56b54f88be"
integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==
"@types/d3-timer@*":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70"
integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
"@types/d3-transition@*":
version "3.0.8"
resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.8.tgz#677707f5eed5b24c66a1918cde05963021351a8f"
integrity sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==
dependencies:
"@types/d3-selection" "*"
"@types/d3-zoom@*", "@types/d3-zoom@^3.0.1":
version "3.0.8"
resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b"
integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
dependencies:
"@types/d3-interpolate" "*"
"@types/d3-selection" "*"
"@types/d3@^7.4.0":
version "7.4.3"
resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.4.3.tgz#d4550a85d08f4978faf0a4c36b848c61eaac07e2"
integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==
dependencies:
"@types/d3-array" "*"
"@types/d3-axis" "*"
"@types/d3-brush" "*"
"@types/d3-chord" "*"
"@types/d3-color" "*"
"@types/d3-contour" "*"
"@types/d3-delaunay" "*"
"@types/d3-dispatch" "*"
"@types/d3-drag" "*"
"@types/d3-dsv" "*"
"@types/d3-ease" "*"
"@types/d3-fetch" "*"
"@types/d3-force" "*"
"@types/d3-format" "*"
"@types/d3-geo" "*"
"@types/d3-hierarchy" "*"
"@types/d3-interpolate" "*"
"@types/d3-path" "*"
"@types/d3-polygon" "*"
"@types/d3-quadtree" "*"
"@types/d3-random" "*"
"@types/d3-scale" "*"
"@types/d3-scale-chromatic" "*"
"@types/d3-selection" "*"
"@types/d3-shape" "*"
"@types/d3-time" "*"
"@types/d3-time-format" "*"
"@types/d3-timer" "*"
"@types/d3-transition" "*"
"@types/d3-zoom" "*"
"@types/debug@^4.1.7":
version "4.1.8"
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz"
@@ -3913,6 +4201,11 @@
"@types/qs" "*"
"@types/serve-static" "*"
"@types/geojson@*":
version "7946.0.14"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613"
integrity sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==
"@types/graceful-fs@^4.1.2":
version "4.1.5"
resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz"
@@ -6044,6 +6337,11 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
classcat@^5.0.3, classcat@^5.0.4:
version "5.0.5"
resolved "https://registry.yarnpkg.com/classcat/-/classcat-5.0.5.tgz#8c209f359a93ac302404a10161b501eba9c09c77"
integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==
clean-css@^5.2.2:
version "5.2.2"
resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz"
@@ -6829,6 +7127,68 @@ csstype@^3.1.1:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz"
integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
"d3-color@1 - 3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
"d3-dispatch@1 - 3":
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
"d3-drag@2 - 3", d3-drag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
dependencies:
d3-dispatch "1 - 3"
d3-selection "3"
"d3-ease@1 - 3":
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
"d3-interpolate@1 - 3":
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
dependencies:
d3-color "1 - 3"
"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
"d3-timer@1 - 3":
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
"d3-transition@2 - 3":
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
dependencies:
d3-color "1 - 3"
d3-dispatch "1 - 3"
d3-ease "1 - 3"
d3-interpolate "1 - 3"
d3-timer "1 - 3"
d3-zoom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
dependencies:
d3-dispatch "1 - 3"
d3-drag "2 - 3"
d3-interpolate "1 - 3"
d3-selection "2 - 3"
d3-transition "2 - 3"
damerau-levenshtein@^1.0.7:
version "1.0.8"
resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz"
@@ -13809,6 +14169,18 @@ react@^18.2.0:
dependencies:
loose-envify "^1.1.0"
reactflow@^11.11.2:
version "11.11.2"
resolved "https://registry.yarnpkg.com/reactflow/-/reactflow-11.11.2.tgz#4968866a9372e6004ad1e424a2141996f0ba769a"
integrity sha512-o1fT3stSdhzW+SedCGNSmEvZvULZygZIMLyW67NcWNZrgwx1wuJfzLg5fuQ0Nzf389wItumZX/zP3zdaPX7lEw==
dependencies:
"@reactflow/background" "11.3.12"
"@reactflow/controls" "11.2.12"
"@reactflow/core" "11.11.2"
"@reactflow/minimap" "11.7.12"
"@reactflow/node-resizer" "2.2.12"
"@reactflow/node-toolbar" "1.3.12"
read-cmd-shim@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz"
@@ -15977,6 +16349,11 @@ url-parse-lax@^3.0.0:
dependencies:
prepend-http "^2.0.0"
use-sync-external-store@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
@@ -16948,3 +17325,10 @@ zen-observable@0.8.15:
version "0.8.15"
resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
zustand@^4.4.1:
version "4.5.2"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"
integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==
dependencies:
use-sync-external-store "1.2.0"