Skip to content

Commit 0d33cda

Browse files
authored
Merge pull request #101 from FlowTestAI/shortcut-keys
feat: Integrate two important shortcut keys
2 parents b12ccab + a5d61a8 commit 0d33cda

13 files changed

Lines changed: 1290 additions & 1188 deletions

File tree

.changeset/five-bikes-study.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"flowtestai": minor
3+
---
4+
5+
Introduce a UI theme, json editor powered by codemirror and few shortcut keys to improve workflow

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636
]
3737
},
3838
"dependencies": {
39+
"@codemirror/commands": "^6.5.0",
40+
"@codemirror/lang-json": "^6.0.1",
41+
"@codemirror/language": "^6.10.1",
42+
"@codemirror/state": "^6.4.1",
43+
"@codemirror/view": "^6.26.3",
3944
"@emotion/react": "^11.11.1",
4045
"@emotion/styled": "^11.11.0",
4146
"@headlessui/react": "^1.7.18",
@@ -44,18 +49,17 @@
4449
"@testing-library/react": "^13.4.0",
4550
"@testing-library/user-event": "^13.5.0",
4651
"@tippyjs/react": "^4.2.6",
47-
"ace-builds": "^1.33.1",
4852
"allotment": "^1.20.0",
4953
"autoprefixer": "^10.4.18",
5054
"axios": "^1.5.1",
55+
"codemirror": "^6.0.1",
5156
"eslint-import-resolver-alias": "^1.1.2",
5257
"eslint-plugin-import": "^2.29.1",
5358
"immer": "^10.0.4",
5459
"lodash": "^4.17.21",
5560
"notistack": "^3.0.1",
5661
"postcss": "^8.4.35",
5762
"react": "^18.2.0",
58-
"react-ace": "^11.0.1",
5963
"react-dom": "^18.2.0",
6064
"react-edit-text": "^5.1.1",
6165
"react-icons": "^5.0.1",
@@ -69,6 +73,7 @@
6973
"reactflow": "^11.8.3",
7074
"socket.io-client": "^4.7.4",
7175
"tailwindcss": "^3.4.1",
76+
"typescript": "4",
7277
"web-vitals": "^2.1.4",
7378
"zustand": "^4.5.2"
7479
},

pnpm-lock.yaml

Lines changed: 1112 additions & 972 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/atoms/Editor.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.cm-editor {
2+
height: 380px;
3+
width: 380px;
4+
}
5+
.cm-scroller {
6+
overflow: auto;
7+
}

src/components/atoms/Editor.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React, { useRef, useEffect, useState } from 'react';
2+
3+
import { basicSetup } from 'codemirror';
4+
import { EditorState, Prec } from '@codemirror/state';
5+
import { EditorView, keymap, lineNumbers } from '@codemirror/view';
6+
import { indentWithTab, history } from '@codemirror/commands';
7+
import { json } from '@codemirror/lang-json';
8+
import { defaultKeymap } from '@codemirror/commands';
9+
import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
10+
import 'components/atoms/Editor.css';
11+
12+
export const Editor = ({ ...props }) => {
13+
const editor = useRef();
14+
//const [code, setCode] = useState('{}');
15+
16+
const onUpdate = EditorView.updateListener.of((v) => {
17+
if (props.onChange) {
18+
props.onChange(v.state.doc.toString());
19+
}
20+
});
21+
22+
useEffect(() => {
23+
const state = EditorState.create({
24+
doc: props.value || '',
25+
extensions: [
26+
lineNumbers(),
27+
json(),
28+
basicSetup,
29+
keymap.of([defaultKeymap, indentWithTab]),
30+
onUpdate,
31+
EditorState.readOnly.of(props.readOnly || false),
32+
history(),
33+
],
34+
});
35+
36+
const view = new EditorView({ state, parent: editor.current });
37+
38+
return () => {
39+
view.destroy();
40+
};
41+
}, []);
42+
43+
return <div ref={editor} className='cm-editor cm-scroller'></div>;
44+
};

src/components/atoms/JsonEditor.js

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/components/molecules/environment/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import ConfirmActionModal from '../modals/ConfirmActionModal';
77
import Button from 'components/atoms/common/Button';
88
import { BUTTON_TYPES, OBJ_TYPES } from 'constants/Common';
99
import EditEnvVariableModal from '../modals/EditEnvVariableModal';
10+
import { useKeyPress } from 'reactflow';
11+
import { saveHandle } from '../modals/SaveFlowModal';
1012

11-
const Env = () => {
13+
const Env = ({ tab }) => {
1214
const variables = useEnvStore((state) => state.variables);
1315
const handleAddVariable = useEnvStore((state) => state.handleAddVariable);
1416
const handleDeleteVariable = useEnvStore((state) => state.handleDeleteVariable);
@@ -21,8 +23,11 @@ const Env = () => {
2123
const [editKey, setEditKey] = useState('');
2224
const [editValue, setEditValue] = useState('');
2325

26+
const cmdAndSPressed = useKeyPress(['Meta+s', 'Strg+s']);
27+
2428
return (
2529
<div className='p-4'>
30+
{cmdAndSPressed && saveHandle(tab)}
2631
<table className='w-full leading-normal'>
2732
<thead>
2833
<tr className='text-xs font-bold tracking-wider text-left uppercase bg-ghost-50 text-ghost-600'>

src/components/molecules/flow/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { useCallback, useMemo, useState } from 'react';
22
import { PropTypes } from 'prop-types';
3-
import ReactFlow, { useNodesState, useEdgesState, addEdge, Controls, Background, ControlButton } from 'reactflow';
3+
import ReactFlow, { Controls, Background, ControlButton, useKeyPress } from 'reactflow';
44
import 'reactflow/dist/style.css';
5-
import { cloneDeep, isEqual } from 'lodash';
5+
import { cloneDeep } from 'lodash';
66
import { toast } from 'react-toastify';
77

88
// ReactFlow Canvas
@@ -22,6 +22,7 @@ import Graph from './graph/Graph';
2222
import ComplexNode from './nodes/ComplexNode';
2323
import { initFlowData } from './utils';
2424
import SetVarNode from './nodes/SetVarNode';
25+
import { saveHandle } from '../modals/SaveFlowModal';
2526
import Button from 'components/atoms/common/Button';
2627
import { BUTTON_INTENT_TYPES, BUTTON_TYPES } from 'constants/Common';
2728

@@ -80,7 +81,7 @@ const selector = (state) => ({
8081
setViewport: state.setViewport,
8182
});
8283

83-
const Flow = ({ collectionId }) => {
84+
const Flow = ({ tab, collectionId }) => {
8485
const { nodes, edges, onNodesChange, onEdgesChange, onConnect, setNodes, setEdges, setLogs, viewport, setViewport } =
8586
useCanvasStore(selector);
8687

@@ -201,8 +202,11 @@ const Flow = ({ collectionId }) => {
201202

202203
reactFlowInstance?.setViewport(viewport);
203204

205+
const cmdAndSPressed = useKeyPress(['Meta+s', 'Strg+s']);
206+
204207
return (
205208
<div className='flex-auto'>
209+
{cmdAndSPressed && saveHandle(tab)}
206210
<ReactFlow
207211
nodes={nodes}
208212
edges={edges}

src/components/molecules/flow/nodes/ComplexNode.js

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { getAllFlowTests } from 'stores/utils';
66
import useCollectionStore from 'stores/CollectionStore';
77
import Tippy from '@tippyjs/react';
88
import 'tippy.js/dist/tippy.css';
9+
import { useKeyPress } from 'reactflow';
10+
import { readFlowTest } from 'service/collection';
11+
import { toast } from 'react-toastify';
912

1013
// ToDo: Change standard select element(s) with headless list element
1114
const ComplexNode = ({ id, data }) => {
@@ -24,45 +27,64 @@ const ComplexNode = ({ id, data }) => {
2427
}
2528
}
2629

30+
const cmdPressed = useKeyPress('Meta'); // 'Meta' key for Cmd on Mac
31+
32+
const handleMouseClick = (event, relativePath) => {
33+
if (cmdPressed && event.type === 'click') {
34+
if (relativePath && relativePath.trim() != '') {
35+
readFlowTest(ipcRenderer.join(collection.pathname, relativePath), collectionId)
36+
.then((result) => {
37+
console.log(`Read flowtest: path = ${relativePath}, collectionId = ${collectionId}`);
38+
})
39+
.catch((error) => {
40+
console.log(`Error reading flowtest: ${error}`);
41+
toast.error(`Error reading flowtest`);
42+
});
43+
}
44+
}
45+
};
46+
2747
return (
28-
<FlowNode
29-
title='Flow Node'
30-
handleLeft={true}
31-
handleLeftData={{ type: 'target' }}
32-
handleRight={true}
33-
handleRightData={{ type: 'source' }}
34-
>
35-
<div>
36-
<Tippy
37-
content={data.relativePath && data.relativePath !== '' ? data.relativePath : 'Select a flow'}
38-
placement='top'
39-
maxWidth='none'
40-
>
41-
<select
42-
onChange={(event) => {
43-
const value = event.target?.value;
44-
setFlowForComplexNode(id, value);
45-
}}
46-
name='flow'
47-
value={data.relativePath ? data.relativePath : ''}
48-
className='h-12 p-2 border rounded outline-none cursor-default bg-background-light max-w-48 border-cyan-950'
48+
<div onClick={(e) => handleMouseClick(e, data.relativePath)}>
49+
<FlowNode
50+
title='Flow Node'
51+
handleLeft={true}
52+
handleLeftData={{ type: 'target' }}
53+
handleRight={true}
54+
handleRightData={{ type: 'source' }}
55+
>
56+
<div>
57+
<Tippy
58+
content={data.relativePath && data.relativePath !== '' ? data.relativePath : 'Select a flow'}
59+
placement='top'
60+
maxWidth='none'
4961
>
50-
<option key='None' value=''>
51-
Select a flow
52-
</option>
53-
{flowTests.map((flowTestPath) => {
54-
return (
55-
<option key={flowTestPath} value={flowTestPath} className='overflow-scroll'>
56-
{flowTestPath}
57-
</option>
58-
);
59-
})}
60-
</select>
61-
</Tippy>
62+
<select
63+
onChange={(event) => {
64+
const value = event.target?.value;
65+
setFlowForComplexNode(id, value);
66+
}}
67+
name='flow'
68+
value={data.relativePath ? data.relativePath : ''}
69+
className='h-12 p-2 border rounded outline-none cursor-default bg-background-light max-w-48 border-cyan-950'
70+
>
71+
<option key='None' value=''>
72+
Select a flow
73+
</option>
74+
{flowTests.map((flowTestPath) => {
75+
return (
76+
<option key={flowTestPath} value={flowTestPath} className='overflow-scroll'>
77+
{flowTestPath}
78+
</option>
79+
);
80+
})}
81+
</select>
82+
</Tippy>
6283

63-
<p className='hidden'></p>
64-
</div>
65-
</FlowNode>
84+
<p className='hidden'></p>
85+
</div>
86+
</FlowNode>
87+
</div>
6688
);
6789
};
6890

src/components/molecules/flow/nodes/OutputNode.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { PropTypes } from 'prop-types';
33
import FlowNode from 'components/atoms/flow/FlowNode';
4-
import JsonEditor from 'components/atoms/JsonEditor';
4+
import { Editor } from 'components/atoms/Editor';
55

66
const OutputNode = ({ id, data }) => {
77
return (
@@ -12,15 +12,9 @@ const OutputNode = ({ id, data }) => {
1212
handleRight={true}
1313
handleRightData={{ type: 'source' }}
1414
>
15-
<div className='w-full text-xs text-gray-900 border border-gray-300 rounded-lg nodrag nowheel min-h-96 min-w-80 bg-gray-50 outline-blue-300 focus:border-blue-100 focus:ring-blue-100'>
15+
<div className='w-full text-xs text-gray-900 border border-gray-300 rounded-lg nodrag nowheel min-h-96 min-w-96 bg-gray-50 outline-blue-300 focus:border-blue-100 focus:ring-blue-100'>
1616
{data.output ? (
17-
<JsonEditor
18-
name='output-text'
19-
value={data.output ? JSON.stringify(data.output, null, 2) : 'Run flow to see data'}
20-
placeholder='Run flow to see data'
21-
readOnly={true}
22-
maxLines={20}
23-
/>
17+
<Editor name='output-text' value={JSON.stringify(data.output, null, 2)} readOnly={true} />
2418
) : (
2519
<div className='p-2'>{'Run flow to see data'}</div>
2620
)}

0 commit comments

Comments
 (0)