├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── build ├── entitlements.mac.plist ├── latest-release.md └── scripts │ ├── notarize.js │ └── publish.js ├── jest.config.js ├── package.json ├── resources ├── docs │ └── app-arch.png ├── img │ ├── icon.png │ ├── index.d.ts │ ├── logo-md.png │ ├── logo-sm.png │ ├── sc-model-editor.png │ ├── sc-process-editor.png │ └── sc-ui-editor.png └── scripts │ ├── development │ ├── react-dom.js │ └── react.js │ └── production │ ├── react-dom.js │ └── react.js ├── src ├── components │ ├── ConfirmPopover │ │ ├── ConfirmPopover.tsx │ │ └── index.ts │ ├── ConfirmationDialog │ │ ├── ConfirmationDialog.tsx │ │ └── index.ts │ ├── DialogForm │ │ ├── DialogForm.tsx │ │ └── index.ts │ ├── ErrorBoundary │ │ ├── ErrorBoundary.tsx │ │ └── index.ts │ ├── Toast │ │ ├── Toast.tsx │ │ └── index.ts │ └── interface.ts ├── constants │ ├── data.ts │ ├── editor.ts │ ├── index.ts │ ├── starter.ts │ └── styles.ts ├── containers │ ├── editorWindow │ │ ├── EditorContainer.tsx │ │ ├── FileBrowser │ │ │ ├── CodeEditor │ │ │ │ ├── CodeEditor.tsx │ │ │ │ └── index.ts │ │ │ ├── FileBrowser.tsx │ │ │ ├── FileTreeView │ │ │ │ ├── FileTreeView.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── MVCEditor │ │ │ ├── HeaderView │ │ │ │ ├── HeaderView.tsx │ │ │ │ ├── NotificationListView │ │ │ │ │ ├── NotificationListView.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── RevisionListView │ │ │ │ │ ├── RevisionListView.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── WidgetUpdateDialog │ │ │ │ │ ├── WidgetUpdateDialog.tsx │ │ │ │ │ └── index.ts │ │ │ ├── MVCEditor.tsx │ │ │ ├── ModelEditorView │ │ │ │ ├── BlockEditDialog │ │ │ │ │ ├── BlockEditDialog.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ModelEditorView.tsx │ │ │ │ ├── ModelListView │ │ │ │ │ ├── ModelListView.tsx │ │ │ │ │ ├── definition.ts │ │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ │ ├── ProcessEditorView │ │ │ │ ├── EditView │ │ │ │ │ ├── EditView.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── ProcessEditorView.tsx │ │ │ │ ├── ProcessListView │ │ │ │ │ ├── ProcessListView.tsx │ │ │ │ │ ├── definition.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── StepEditDialog │ │ │ │ │ ├── CodeBlockPane │ │ │ │ │ │ ├── CodeBlockPane.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── templates │ │ │ │ │ │ │ ├── executeFunc.txt │ │ │ │ │ │ │ ├── templateMap.ts │ │ │ │ │ │ │ └── triggerFunc.txt │ │ │ │ │ ├── ManualPane │ │ │ │ │ │ ├── ManualPane.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── StepAttributePane │ │ │ │ │ │ ├── StepAttributePane.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── StepEditDialog.tsx │ │ │ │ │ ├── StepOutputsPane │ │ │ │ │ │ ├── StepOutputsPane.tsx │ │ │ │ │ │ ├── formDefinition.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── TriggerPane │ │ │ │ │ │ ├── TriggerPane.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── interface.ts │ │ │ │ ├── index.ts │ │ │ │ └── stepOptions.tsx │ │ │ ├── SettingsView │ │ │ │ ├── LibraryPane │ │ │ │ │ ├── LibraryPane.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── SettingsView.tsx │ │ │ │ └── index.ts │ │ │ ├── UIEditorView │ │ │ │ ├── AddLibraryDialog │ │ │ │ │ ├── AddLibraryDialog.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── AddWidgetDialog │ │ │ │ │ ├── AddWidgetDialog.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── UIEditorView.tsx │ │ │ │ └── index.ts │ │ │ ├── exampleData.ts │ │ │ └── index.ts │ │ └── NavigationSidebar │ │ │ ├── NavigationSidebar.tsx │ │ │ └── index.ts │ └── starterWindow │ │ ├── ActionView │ │ ├── ActionView.tsx │ │ └── index.ts │ │ ├── CreateProjectDialog │ │ ├── CreateProjectDialog.tsx │ │ ├── ParamForm.tsx │ │ └── index.ts │ │ ├── ProjectListView │ │ ├── ProjectListView.tsx │ │ └── index.ts │ │ ├── StarterContainer.tsx │ │ └── ValidationDialog │ │ ├── ValidationDialog.tsx │ │ └── index.ts ├── controllers │ ├── files │ │ ├── __test__ │ │ │ └── sourceFileManager.test.ts │ │ └── sourceFileManager.ts │ ├── localStoreManager.ts │ ├── mainProcessCommunicator.ts │ ├── model │ │ ├── README.md │ │ ├── __test__ │ │ │ └── specGenerator.test.ts │ │ ├── dataConverter.ts │ │ ├── modelManager.ts │ │ ├── sourceFileGenerator.ts │ │ ├── specGenerator.ts │ │ └── templates │ │ │ └── crd.yaml │ ├── pluginFileManager.ts │ ├── process │ │ ├── processDataHandler.ts │ │ ├── processManager.ts │ │ ├── sourceFileGenerator.ts │ │ └── templates │ │ │ ├── config-go.txt │ │ │ ├── definition-go.txt │ │ │ ├── executors │ │ │ └── python │ │ │ │ ├── app-py.txt │ │ │ │ ├── init-py.txt │ │ │ │ ├── manage-sh.txt │ │ │ │ └── requirements.txt │ │ │ ├── go-mod.txt │ │ │ ├── init-go.txt │ │ │ ├── main-go.txt │ │ │ └── manage-sh.txt │ ├── project │ │ └── projectManager.ts │ ├── runtime │ │ ├── sourceFileGenerator.ts │ │ └── templates │ │ │ └── docker-compose-yaml.txt │ ├── ui │ │ ├── reactCodeFormatter.ts │ │ ├── sourceFileGenerator.ts │ │ ├── templates │ │ │ ├── action-index-js.txt │ │ │ ├── actions-root-index-js.txt │ │ │ ├── babelrc.txt │ │ │ ├── component-with-children.txt │ │ │ ├── component-without-children.txt │ │ │ ├── index-html.txt │ │ │ ├── index-jsx.txt │ │ │ ├── npmrc.txt │ │ │ ├── package-json.txt │ │ │ ├── react-code.txt │ │ │ ├── redux-actions-js.txt │ │ │ ├── redux-reducer-js.txt │ │ │ ├── redux-store-js.txt │ │ │ ├── redux-types-js.txt │ │ │ ├── repeat-component.txt │ │ │ ├── server-js.txt │ │ │ └── webpack-config.txt │ │ ├── uiActionHandler.ts │ │ ├── uiDataManager.ts │ │ ├── widgetLibraryWrapper.ts │ │ └── widgetManager.ts │ └── utils │ │ ├── datetimeHelper.ts │ │ ├── fsHelper.ts │ │ └── githubHelper.ts ├── electron │ ├── main.ts │ ├── utils │ │ ├── autoUpdater.ts │ │ ├── debugHelper.ts │ │ ├── dev-app-update.yml │ │ ├── gitHelper.ts │ │ ├── menuBuilder.ts │ │ ├── startDebugging.ts │ │ └── starterMenuBuilder.ts │ └── views │ │ ├── editor.html │ │ └── starter.html ├── entries │ ├── Editor.tsx │ └── Starter.tsx ├── interface.ts ├── migrations │ ├── migrationHandler.ts │ └── ui │ │ ├── revise.ts │ │ └── v2.ts ├── redux │ ├── modules │ │ ├── components │ │ │ ├── actions.ts │ │ │ ├── reducer.ts │ │ │ └── types.ts │ │ ├── config │ │ │ ├── actions.ts │ │ │ ├── reducer.ts │ │ │ └── types.ts │ │ ├── editor │ │ │ ├── actions.ts │ │ │ ├── modelEditor │ │ │ │ ├── actions.ts │ │ │ │ ├── reducer.ts │ │ │ │ └── types.ts │ │ │ ├── navigation │ │ │ │ ├── actions.ts │ │ │ │ ├── reducer.ts │ │ │ │ └── types.ts │ │ │ ├── processEditor │ │ │ │ ├── actions.ts │ │ │ │ ├── reducer.ts │ │ │ │ └── types.ts │ │ │ ├── reducer.ts │ │ │ ├── settings │ │ │ │ ├── actions.ts │ │ │ │ ├── reducer.ts │ │ │ │ └── types.ts │ │ │ ├── types.ts │ │ │ └── uiEditor │ │ │ │ ├── actions.ts │ │ │ │ ├── reducer.ts │ │ │ │ └── types.ts │ │ ├── files │ │ │ ├── actions.ts │ │ │ ├── reducer.ts │ │ │ └── types.ts │ │ └── starter │ │ │ ├── actions.ts │ │ │ ├── reducer.ts │ │ │ └── types.ts │ ├── reducer.ts │ ├── state.ts │ └── store.ts └── typings.d.ts ├── tsconfig.json ├── tslint.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .idea/.gitignore 3 | .idea/flint.iml 4 | .idea/misc.xml 5 | .idea/modules.xml 6 | .idea/vcs.xml 7 | node_modules/ 8 | package-lock.json 9 | dist/ 10 | webpack-stats.json 11 | .settings.json 12 | .DS_Store 13 | .env -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | @flintdev:registry=https://npm.pkg.github.com/flintdev -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |

Flint

4 |

5 | 6 | [![](https://img.shields.io/badge/download-Mac-brightgreen)](https://github.com/flintdev/flint/releases/latest) 7 | ![](https://img.shields.io/badge/stage-alpha-red) 8 | ![](https://img.shields.io/github/v/release/flintdev/flint) 9 | ![](https://img.shields.io/github/release-date/flintdev/flint) 10 | ![](https://img.shields.io/david/flintdev/flint) 11 | ![](https://img.shields.io/badge/platform-MacOS-orange) 12 | ![](https://img.shields.io/github/license/flintdev/flint) 13 | ![](https://img.shields.io/github/package-json/dependency-version/flintdev/flint/dev/electron) 14 | 15 | ## Introduction 16 | 17 | Flint is a low-code development platform based on kubernetes architecture and corresponding ecosystem in open-source community. 18 | It provides developers complete solution to build modern web applications for their complex business requirements in a visualized way with minimum coding efforts. 19 | 20 | Advantages comparing with other commercial low-code solutions: 21 | 22 | * **Single Page Application (SPA).** The applications built in Flint are react-based single page applications with dynamic UI and efficient rendering performance. 23 | Flint provides a serial of innovative functions in product to enable developers to build UI and frontend logic in a fast and comfortable way. 24 | * **State-based UI construction.** The UI construction method is state based supported by the complete state management mechanism provided by React & Redux. 25 | Developers only need to design and update the state model to implement dynamic UI interactions. 26 | * **DAG based workflow engine.** Any complex business logic could be defined in the DAG workflow engine with manual and parallel steps. 27 | * **Model-driven design pattern for both frontend and backend.** Each component in frontend UI is bind to global state defined by developers and each UI action is to update the state. 28 | The workflows in backend are always triggered by the update events of data models. 29 | * **Source code generation.** After the application is constructed, Flint will automatically generate the structured source code. Users can use the source code for secondary development, or 30 | integrated with existing repo. 31 | * **Microservices architecture.** Backend follows the best practice of cloud native applications to build and deployment all the services in microservice architecture. 32 | * **Plugin Framework.** The UI components are installed as plugins of Flint. Users can choose the component libraries and install them on demand. Third-party developers can also develop and publish their components as plugins in Flint. 33 | * **Cloud native deployment solution.** Based on the Kubernetes ecosystem in open-source community, Flint provides complete solution to test and deployment applications to public or private cloud. 34 | 35 | > Please note: current stage of Flint is still alpha and we are in rapid iterations to deliver features in the roadmap. The beta version is planned to be released by the end of this year. It is not recommended to use Flint in production before beta. 36 | 37 | ### UI Editor 38 | 39 | ![](resources/img/sc-ui-editor.png) 40 | 41 | ### Process Editor 42 | 43 | ![](resources/img/sc-process-editor.png) 44 | 45 | ### Model Editor 46 | 47 | ![](resources/img/sc-model-editor.png) 48 | 49 | ## Application Architecture 50 | 51 | ![](resources/docs/app-arch.png) 52 | 53 | -------------------------------------------------------------------------------- /build/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | 8 | -------------------------------------------------------------------------------- /build/latest-release.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/build/latest-release.md -------------------------------------------------------------------------------- /build/scripts/notarize.js: -------------------------------------------------------------------------------- 1 | // build/scripts/notarize.js 2 | 3 | require('dotenv').config(); 4 | const { notarize } = require('electron-notarize'); 5 | 6 | exports.default = async function notarizing(context) { 7 | const { electronPlatformName, appOutDir } = context; 8 | if (electronPlatformName !== 'darwin') { 9 | return; 10 | } 11 | 12 | const appName = context.packager.appInfo.productFilename; 13 | 14 | return await notarize({ 15 | appBundleId: 'io.flintdev.flint', 16 | appPath: `${appOutDir}/${appName}.app`, 17 | appleId: process.env.APPLEID, 18 | appleIdPassword: process.env.APPLEIDPASS, 19 | }); 20 | }; -------------------------------------------------------------------------------- /build/scripts/publish.js: -------------------------------------------------------------------------------- 1 | // upload latest release to github 2 | 3 | const fs = require('fs'); 4 | const fetch = require('node-fetch'); 5 | const Bluebird = require('bluebird'); 6 | fetch.Promise = Bluebird; 7 | const mime = require('mime-types'); 8 | const ora = require('ora'); 9 | const chalk = require('chalk'); 10 | const pjsonStr = fs.readFileSync('./package.json'); 11 | const pjson = JSON.parse(pjsonStr); 12 | const version = pjson.version; 13 | const {host, owner, repo} = pjson.build.mac.publish[0]; 14 | const baseURL = `https://api.${host}/repos/${owner}/${repo}/releases`; 15 | // 16 | const releaseContent = fs.readFileSync('./build/latest-release.md').toString(); 17 | const settingsJson = JSON.parse(fs.readFileSync('./.settings.json')); 18 | const githubToken = settingsJson.github.token; 19 | const headers = {Authorization: `token ${githubToken}`}; 20 | 21 | 22 | const createRelease = async () => { 23 | const spinner = ora(`Creating new release of version ${version}`).start(); 24 | const data = { 25 | tag_name: `v${version}`, 26 | name: `v${version}`, 27 | body: releaseContent, 28 | prerelease: true, 29 | }; 30 | try { 31 | const response = await fetch(baseURL, { 32 | headers: {...headers, 'Content-Type': 'application/json'}, 33 | method: 'POST', 34 | body: JSON.stringify(data) 35 | }); 36 | spinner.succeed('Release is created'); 37 | return await response.json(); 38 | } catch (err) { 39 | spinner.fail('Unable to create release'); 40 | console.log(err); 41 | return false; 42 | } 43 | }; 44 | 45 | const uploadReleaseAsset = async (uploadURL, assetName, binary, mediaType) => { 46 | const url = uploadURL.replace('{?name,label}', `?name=${assetName}`); 47 | if (!binary) return false; 48 | try { 49 | const response = await fetch(url, { 50 | headers: {...headers, 'Content-Type': mediaType}, 51 | method: 'POST', 52 | body: binary, 53 | }); 54 | return await response.json(); 55 | } catch (err) { 56 | console.log(err); 57 | return false; 58 | } 59 | }; 60 | 61 | const getReleaseBinary = (version) => { 62 | const namePathMap = { 63 | "latest-mac.yml": "./dist/latest-mac.yml", 64 | [`Flint-${version}-mac.zip`]: `./dist/Flint-${version}-mac.zip`, 65 | [`Flint-${version}.dmg`]: `./dist/Flint-${version}.dmg`, 66 | }; 67 | let nameBinaryList = []; 68 | for (let name of Object.keys(namePathMap)) { 69 | try { 70 | const filePath = namePathMap[name]; 71 | const binary = fs.readFileSync(filePath); 72 | if (!binary) return false; 73 | const mediaType = mime.lookup(filePath); 74 | nameBinaryList.push({name, binary, mediaType}); 75 | } catch (err) { 76 | console.log(err); 77 | return false 78 | } 79 | } 80 | return nameBinaryList; 81 | }; 82 | 83 | async function publish () { 84 | const response = await createRelease(); 85 | if (!response) return false; 86 | const uploadURL = response['upload_url']; 87 | const nameBinaryList = getReleaseBinary(version); 88 | if (!nameBinaryList || nameBinaryList.length === 0) { 89 | console.log(chalk.red("Unable to find binary files")); 90 | return false; 91 | } 92 | for (let item of nameBinaryList) { 93 | const {name, binary, mediaType} = item; 94 | const assetName = name; 95 | const spinner = ora(`Uploading ${name}`).start(); 96 | try { 97 | const result = await uploadReleaseAsset(uploadURL, assetName, binary, mediaType); 98 | spinner.succeed(`${name} is uploaded`); 99 | } catch (err) { 100 | console.log(err); 101 | spinner.fail(`Unable to upload ${name}`); 102 | } 103 | } 104 | } 105 | 106 | publish().then(r => {}); -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "roots": [ 3 | "/src" 4 | ], 5 | "testMatch": [ 6 | "**/__tests__/**/*.+(ts|tsx|js)", 7 | "**/?(*.)+(spec|test).+(ts|tsx|js)" 8 | ], 9 | "transform": { 10 | "^.+\\.(ts|tsx)$": "ts-jest" 11 | }, 12 | } -------------------------------------------------------------------------------- /resources/docs/app-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/docs/app-arch.png -------------------------------------------------------------------------------- /resources/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/img/icon.png -------------------------------------------------------------------------------- /resources/img/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.png'; 2 | -------------------------------------------------------------------------------- /resources/img/logo-md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/img/logo-md.png -------------------------------------------------------------------------------- /resources/img/logo-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/img/logo-sm.png -------------------------------------------------------------------------------- /resources/img/sc-model-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/img/sc-model-editor.png -------------------------------------------------------------------------------- /resources/img/sc-process-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/img/sc-process-editor.png -------------------------------------------------------------------------------- /resources/img/sc-ui-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/resources/img/sc-ui-editor.png -------------------------------------------------------------------------------- /src/components/ConfirmPopover/ConfirmPopover.tsx: -------------------------------------------------------------------------------- 1 | // src/components/ConfirmPopover/ConfirmPopover.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import Button from "@material-ui/core/Button"; 6 | import Popover from "@material-ui/core/Popover"; 7 | import Paper from "@material-ui/core/Paper"; 8 | import Typography from "@material-ui/core/Typography"; 9 | 10 | const styles = createStyles({ 11 | root: {}, 12 | paper: { 13 | padding: 10, 14 | width: 300, 15 | }, 16 | }); 17 | 18 | export interface Props extends WithStyles { 19 | anchorEl: undefined | HTMLButtonElement, 20 | onClose: () => void, 21 | color?: "primary" | "secondary", 22 | message?: string, 23 | confirmButtonTitle?: string, 24 | cancelButtonTitle?: string, 25 | onConfirm: () => void, 26 | } 27 | 28 | interface State { 29 | 30 | } 31 | 32 | class ConfirmPopover extends React.Component { 33 | state: State = { 34 | }; 35 | 36 | componentDidMount(): void { 37 | 38 | } 39 | 40 | handleConfirmButtonClick = () => { 41 | this.props.onConfirm(); 42 | }; 43 | 44 | render() { 45 | const {classes, anchorEl, onClose, color, message, confirmButtonTitle, cancelButtonTitle} = this.props; 46 | return ( 47 | 60 | 61 | 62 | {message} 63 | 64 |
65 | 72 |      73 | 78 |
79 |
80 | ) 81 | } 82 | } 83 | 84 | export default withStyles(styles)(ConfirmPopover); 85 | -------------------------------------------------------------------------------- /src/components/ConfirmPopover/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ConfirmPopover'; -------------------------------------------------------------------------------- /src/components/ConfirmationDialog/ConfirmationDialog.tsx: -------------------------------------------------------------------------------- 1 | // src/components/ConfirmationDialog/ConfirmationDialog.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {ComponentsState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/components/actions"; 9 | import Dialog from "@material-ui/core/Dialog"; 10 | import DialogTitle from "@material-ui/core/DialogTitle"; 11 | import DialogContent from "@material-ui/core/DialogContent"; 12 | import DialogActions from "@material-ui/core/DialogActions"; 13 | import {Alert, AlertTitle} from "@material-ui/lab"; 14 | import Button from "@material-ui/core/Button"; 15 | 16 | const styles = createStyles({ 17 | root: { 18 | 19 | }, 20 | }); 21 | 22 | export interface Props extends WithStyles, ComponentsState { 23 | closeConfirmationDialog: () => void, 24 | } 25 | 26 | class ConfirmationDialog extends React.Component { 27 | state = { 28 | 29 | }; 30 | 31 | componentDidMount(): void { 32 | 33 | } 34 | 35 | handleSubmitClick = () => { 36 | const {onSubmit} = this.props.confirmationDialog; 37 | if (!!onSubmit) onSubmit(); 38 | this.props.closeConfirmationDialog(); 39 | }; 40 | 41 | render() { 42 | const {classes, confirmationDialog} = this.props; 43 | const {open, title, type, submitLabel, description} = confirmationDialog; 44 | return ( 45 |
46 | 52 | 53 | 54 | {title} 55 | {description} 56 | 57 | 58 | 59 | 60 | 67 | 68 | 69 |
70 | ) 71 | } 72 | } 73 | 74 | const mapStateToProps = (state: StoreState) => { 75 | return state.components; 76 | }; 77 | 78 | const mapDispatchToProps = (dispatch: Dispatch) => { 79 | return { 80 | closeConfirmationDialog: () => dispatch(actions.closeConfirmationDialog()), 81 | } 82 | }; 83 | 84 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ConfirmationDialog)); 85 | -------------------------------------------------------------------------------- /src/components/ConfirmationDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ConfirmationDialog'; -------------------------------------------------------------------------------- /src/components/DialogForm/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './DialogForm'; -------------------------------------------------------------------------------- /src/components/ErrorBoundary/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | // src/components/ErrorBoundary/ErrorBoundary.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import {Alert, AlertTitle} from "@material-ui/lab"; 6 | import Button from '@material-ui/core/Button'; 7 | 8 | const styles = createStyles({ 9 | root: { 10 | marginTop: 20, 11 | marginRight: 100, 12 | marginLeft: 100, 13 | }, 14 | }); 15 | 16 | export interface Props extends WithStyles{ 17 | 18 | } 19 | 20 | class ErrorBoundary extends React.Component { 21 | state = { 22 | hasError: false, 23 | errorMessage: '', 24 | }; 25 | 26 | componentDidMount(): void { 27 | 28 | } 29 | 30 | static getDerivedStateFromError(error: Error) { 31 | // Update state so the next render will show the fallback UI. 32 | return { 33 | hasError: true, 34 | errorMessage: error.message 35 | }; 36 | } 37 | 38 | handleReloadClick = () => { 39 | location.reload(); 40 | }; 41 | 42 | render() { 43 | const {classes} = this.props; 44 | if (this.state.hasError) { 45 | return ( 46 |
47 | 48 | {"Uncaught error occurs"} 49 | {this.state.errorMessage} 50 | 51 |
52 | 53 |
54 | ) 55 | } 56 | return this.props.children; 57 | } 58 | } 59 | 60 | export default withStyles(styles)(ErrorBoundary); 61 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ErrorBoundary'; -------------------------------------------------------------------------------- /src/components/Toast/Toast.tsx: -------------------------------------------------------------------------------- 1 | // src/components/Toast/Toast.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import Snackbar from '@material-ui/core/Snackbar'; 6 | import MuiAlert, { AlertProps } from '@material-ui/lab/Alert'; 7 | import { connect } from 'react-redux'; 8 | import { Dispatch } from "redux"; 9 | import * as actions from 'src/redux/modules/components/actions'; 10 | import {ComponentsState, StoreState} from "../../redux/state"; 11 | 12 | const styles = createStyles({ 13 | root: { 14 | 15 | }, 16 | }); 17 | 18 | 19 | export interface Props extends WithStyles, ComponentsState { 20 | toastClose: () => void, 21 | } 22 | 23 | function Alert(props: AlertProps) { 24 | return ; 25 | } 26 | 27 | class Toast extends React.Component { 28 | state = { 29 | 30 | }; 31 | 32 | componentDidMount(): void { 33 | 34 | } 35 | 36 | handleClose = (event?: React.SyntheticEvent, reason?: string) => { 37 | if (reason === 'clickaway') { 38 | return; 39 | } 40 | this.props.toastClose(); 41 | }; 42 | 43 | render() { 44 | const {classes, toast} = this.props; 45 | const {open, type, message} = toast; 46 | return ( 47 |
48 | 57 | 58 | {message} 59 | 60 | 61 |
62 | ) 63 | } 64 | } 65 | 66 | const mapStateToProps = (state: StoreState) => { 67 | return state.components; 68 | }; 69 | 70 | const mapDispatchToProps = (dispatch: Dispatch) => { 71 | return { 72 | toastClose: () => dispatch(actions.toastClose()), 73 | } 74 | }; 75 | 76 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Toast)); 77 | -------------------------------------------------------------------------------- /src/components/Toast/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './Toast'; -------------------------------------------------------------------------------- /src/components/interface.ts: -------------------------------------------------------------------------------- 1 | // src/components/interface.ts 2 | 3 | export type ToastType = 'success' | 'error' | 'warning' | 'info'; 4 | 5 | type FormType = 'input' | 'select'; 6 | type FormValueType = string|number; 7 | 8 | export interface Form { 9 | type: FormType, 10 | key: string, 11 | label: string, 12 | placeholder?: string, 13 | helperText?: string, 14 | required?: boolean, 15 | dataType?: string, 16 | autofocus?: boolean, 17 | defaultValue?: FormValueType, 18 | options?: Array 19 | } 20 | 21 | export interface DialogFormData { 22 | forms: Form[], 23 | title: string, 24 | description?: string, 25 | submitLabel?: string, 26 | cancelLabel?: string, 27 | } 28 | 29 | export type DialogFormSubmitFunc = (values: any) => void; 30 | 31 | export type ConfirmationDialogSubmitFunc = () => void; -------------------------------------------------------------------------------- /src/constants/data.ts: -------------------------------------------------------------------------------- 1 | // src/constants/data.ts 2 | 3 | export const UI_DATA_SCHEMA_VERSION = 2; -------------------------------------------------------------------------------- /src/constants/editor.ts: -------------------------------------------------------------------------------- 1 | // src/constants/editorWindow.ts 2 | 3 | import {Output} from "../controllers/process/processDataHandler"; 4 | 5 | export enum Page { 6 | Editor, 7 | Files, 8 | Plugins, 9 | Delivery, 10 | } 11 | 12 | export const NavigationPages = [ 13 | {key: Page.Editor, name: 'Editor'}, 14 | {key: Page.Files, name: 'Files'}, 15 | {key: Page.Plugins, name: 'Plugins'}, 16 | {key: Page.Delivery, name: 'Delivery'}, 17 | ]; 18 | 19 | export enum MVC { 20 | Model = 'model', 21 | View = 'view', 22 | Controller = 'controller' 23 | } 24 | 25 | export const MVCViews: Array = [ 26 | {key: MVC.View, name: 'User Interface'}, 27 | {key: MVC.Controller, name: 'Processes'}, 28 | {key: MVC.Model, name: 'Data Models'}, 29 | ]; 30 | 31 | export interface ModelView { 32 | key: MVC, 33 | name: string 34 | } 35 | 36 | export const AlwaysOutputs: Output[] = [ 37 | { 38 | name: "Always", 39 | condition: { 40 | key: "", 41 | operator: 'always', 42 | value: "" 43 | } 44 | } 45 | ]; 46 | 47 | export const ModelFieldDataTypes = ["string", "integer", "object", "boolean", "string[]", "integer[]", "$ref", "$ref[]"]; -------------------------------------------------------------------------------- /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | // src/constants/index.ts 2 | 3 | import {createMuiTheme} from "@material-ui/core/styles"; 4 | import {PluginData} from "../interface"; 5 | 6 | export const themeColor = { 7 | primary: '#416CED', 8 | secondary: '#ff4400', 9 | grey: 'grey', 10 | white: 'white', 11 | dimgrey: 'dimgrey', 12 | lightgrey: 'lightgrey', 13 | }; 14 | 15 | export const theme = createMuiTheme({ 16 | palette: { 17 | primary: { 18 | // light: will be calculated from palette.primary.main, 19 | main: themeColor.primary, 20 | // dark: will be calculated from palette.primary.main, 21 | // contrastText: will be calculated to contrast with palette.primary.main 22 | }, 23 | secondary: { 24 | main: themeColor.secondary, 25 | // dark: will be calculated from palette.secondary.main, 26 | }, 27 | contrastThreshold: 3, 28 | } 29 | }); 30 | 31 | export const BackgroundColor = { 32 | Editor: '#FFF' 33 | }; 34 | 35 | export enum LOADING_STATUS { 36 | NOT_STARTED, 37 | LOADING, 38 | COMPLETE, 39 | FAILED 40 | } 41 | 42 | export const CHANNEL = { 43 | OPEN_EDITOR_AND_CLOSE_STARTER: 'OPEN_EDITOR_AND_CLOSE_STARTER', 44 | SELECT_DIRECTORY: 'SELECT_DIRECTORY', 45 | SELECT_DIRECTORY_REPLY: 'SELECT_DIRECTORY_REPLY', 46 | SEND_PROJECT_DIR: 'SEND_PROJECT_DIR', 47 | CONSOLE: 'CONSOLE', 48 | START_DEBUGGING: 'START_DEBUGGING', 49 | EDITOR_WINDOW_ON_ACTIVE: 'EDITOR_WINDOW_ON_ACTIVE', 50 | PREINSTALL_PLUGINS: 'PREINSTALL_PLUGINS', 51 | PREINSTALL_PLUGINS_REPLY: 'PREINSTALL_PLUGINS_REPLY', 52 | STARTER_VIEW_LOADED: 'STARTER_VIEW_LOADED', 53 | NEW_PLUGIN_UPDATES: 'NEW_PLUGIN_UPDATES', 54 | INSTALL_PLUGIN: 'INSTALL_PLUGIN', 55 | INSTALL_PLUGIN_REPLY: 'INSTALL_PLUGIN_REPLY', 56 | RELAUNCH_EDITOR_WINDOW: 'RELAUNCH_EDITOR_WINDOW', 57 | GET_INSTALLED_PLUGIN: 'GET_INSTALLED_PLUGIN', 58 | GET_INSTALLED_PLUGIN_REPLY: 'GET_INSTALLED_PLUGIN_REPLY', 59 | FETCH_ALL_PLUGINS_REPLY: 'FETCH_ALL_PLUGINS_REPLY', 60 | FETCH_ALL_PLUGINS: 'FETCH_ALL_PLUGINS', 61 | REMOVE_PLUGIN_REPLY: 'REMOVE_PLUGIN_REPLY', 62 | REMOVE_PLUGIN: 'REMOVE_PLUGIN', 63 | GET_UNINSTALLED_DEPENDENT_PLUGINS: 'GET_UNINSTALLED_DEPENDENT_PLUGINS', 64 | GET_UNINSTALLED_DEPENDENT_PLUGINS_REPLY: 'GET_UNINSTALLED_DEPENDENT_PLUGINS_REPLY', 65 | GIT_COMMIT: 'GIT_COMMIT', 66 | GIT_COMMIT_REPLY: 'GIT_COMMIT_REPLY', 67 | GIT_LOG: 'GIT_LOG', 68 | GIT_LOG_REPLY: 'GIT_LOG_REPLY', 69 | GIT_RESET: 'GIT_RESET', 70 | GIT_RESET_REPLY: 'GIT_RESET_REPLY', 71 | }; 72 | 73 | export const PluginRegistry = { 74 | owner: 'flintdev', 75 | repo: 'plugin-registry', 76 | path: 'archive/plugins.json' 77 | } -------------------------------------------------------------------------------- /src/constants/starter.ts: -------------------------------------------------------------------------------- 1 | // src/constants/starterWindow.ts 2 | 3 | import {version} from "pjson"; 4 | 5 | export const StarterConfig = { 6 | ActionView: { 7 | title: 'Flint', 8 | description: `Version ${version}`, 9 | action: { 10 | create: 'Create New Project', 11 | open: 'Open Existing Project', 12 | checkout: 'Checkout from Github', 13 | } 14 | }, 15 | CreateProjectDialog: { 16 | location: { 17 | errorMessage: 'Please input valid location of new project', 18 | } 19 | } 20 | }; -------------------------------------------------------------------------------- /src/constants/styles.ts: -------------------------------------------------------------------------------- 1 | // src/constants/styles.ts 2 | 3 | export const NavigationSidebarConfig = { 4 | Width: 60, 5 | TabIndicator: { 6 | Width: 4, 7 | }, 8 | }; 9 | 10 | export const HeaderViewConfig = { 11 | Height: 48, 12 | ActionIconColorGreen: '#19913a', 13 | ActionIconColorGreenDark: '#106827', 14 | ActionIconColorBlue: '#0080bd', 15 | FabButtonRadius: 36, 16 | ViewButtonBgColor: '#f0f0f0', 17 | }; 18 | -------------------------------------------------------------------------------- /src/containers/editorWindow/EditorContainer.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/EditorContainer.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles, ThemeProvider} from '@material-ui/core/styles'; 5 | import NavigationSidebar from "./NavigationSidebar"; 6 | import {connect} from 'react-redux'; 7 | import {Dispatch} from "redux"; 8 | import {ConfigState, EditorState, StoreState} from "src/redux/state"; 9 | import * as actions from "src/redux/modules/config/actions"; 10 | import {Page} from "../../constants/editor"; 11 | import MVCEditor from "./MVCEditor"; 12 | import {BackgroundColor, theme, themeColor} from "../../constants"; 13 | import {MainProcessCommunicator} from "../../controllers/mainProcessCommunicator"; 14 | import {LocalStorageManager} from "../../controllers/localStoreManager"; 15 | import {NavigationSidebarConfig} from "../../constants/styles"; 16 | import FileBrowser from "./FileBrowser"; 17 | import Toast from "../../components/Toast"; 18 | import DialogForm from "../../components/DialogForm"; 19 | import ConfirmationDialog from "../../components/ConfirmationDialog"; 20 | 21 | const styles = createStyles({ 22 | root: { 23 | width: '100%', 24 | height: '100%', 25 | backgroundColor: BackgroundColor.Editor, 26 | display: 'flex' 27 | }, 28 | sider: { 29 | height: '100vh', 30 | borderRight: '1px solid #ddd', 31 | backgroundColor: themeColor.white 32 | }, 33 | contentContainer: { 34 | height: '100%', 35 | }, 36 | table: { 37 | width: '100%', 38 | height: '100vh', 39 | border: 0, 40 | cellSpacing: 0, 41 | cellPadding: 0, 42 | borderSpacing: 0, 43 | borderCollapse: 'collapse', 44 | }, 45 | tdLeft: { 46 | width: NavigationSidebarConfig.Width, 47 | borderRight: '1px solid #ddd', 48 | backgroundColor: themeColor.white 49 | } 50 | }); 51 | 52 | export interface Props extends WithStyles, EditorState, ConfigState { 53 | setProjectDir?: (projectDir: string) => void, 54 | } 55 | 56 | class EditorContainer extends React.Component { 57 | state = {}; 58 | 59 | componentDidMount(): void { 60 | this.initActions().then(r => {}); 61 | } 62 | 63 | componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any) { 64 | if (prevProps.projectDir !== this.props.projectDir) { 65 | this.forceUpdate(); 66 | } 67 | } 68 | 69 | initActions = async () => { 70 | let projectDir = new LocalStorageManager().getProjectDir(); 71 | if (!!projectDir) this.props.setProjectDir(projectDir); 72 | projectDir = await new MainProcessCommunicator().receiveProjectDir(); 73 | new LocalStorageManager().setProjectDir(projectDir); 74 | this.props.setProjectDir(projectDir); 75 | }; 76 | 77 | render() { 78 | const {classes, currentPageIndex, projectDir} = this.props; 79 | return ( 80 | 81 | 82 | {!!projectDir && 83 |
84 | 85 | 86 | 87 | 90 | 96 | 97 | 98 |
88 | 89 | 91 |
92 | {currentPageIndex === Page.Editor && } 93 | {currentPageIndex === Page.Files && } 94 |
95 |
99 | 100 | 101 | 102 | 103 |
104 | } 105 |
106 |
107 | ) 108 | } 109 | } 110 | 111 | const mapStateToProps = (state: StoreState) => { 112 | return {...state.editor, ...state.config}; 113 | }; 114 | 115 | const mapDispatchToProps = (dispatch: Dispatch) => { 116 | return { 117 | setProjectDir: (projectDir: string) => dispatch(actions.setProjectDir(projectDir)), 118 | } 119 | }; 120 | 121 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(EditorContainer)); 122 | -------------------------------------------------------------------------------- /src/containers/editorWindow/FileBrowser/CodeEditor/CodeEditor.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/FileBrowser/CodeEditor/CodeEditor.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {FilesState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/files/actions"; 9 | import AceEditor from "react-ace"; 10 | // import 'ace-builds/webpack-resolver'; 11 | import "ace-builds/src-noconflict/mode-yaml"; 12 | import "ace-builds/src-noconflict/mode-json"; 13 | import "ace-builds/src-noconflict/mode-golang"; 14 | import "ace-builds/src-noconflict/mode-javascript"; 15 | import "ace-builds/src-noconflict/mode-python"; 16 | import "ace-builds/src-noconflict/mode-html"; 17 | import "ace-builds/src-noconflict/mode-jsx"; 18 | import "ace-builds/src-noconflict/mode-text"; 19 | import "ace-builds/src-noconflict/theme-tomorrow"; 20 | 21 | const styles = createStyles({ 22 | root: { 23 | width: '100%', 24 | height: '100%', 25 | }, 26 | }); 27 | 28 | export interface Props extends WithStyles, FilesState{ 29 | 30 | } 31 | 32 | class CodeEditor extends React.Component { 33 | state = { 34 | 35 | }; 36 | 37 | componentDidMount(): void { 38 | 39 | } 40 | 41 | getModeByFile = (path: string): string => { 42 | const tempList = path.split('.'); 43 | const postfix: string = tempList[tempList.length - 1]; 44 | switch (postfix) { 45 | case 'yml': 46 | return 'yaml'; 47 | case 'js': 48 | return 'javascript'; 49 | case 'go': 50 | return 'golang'; 51 | case 'py': 52 | return 'python'; 53 | case 'babelrc': 54 | return 'json'; 55 | case 'npmrc': 56 | return 'text' 57 | default: 58 | return postfix; 59 | } 60 | }; 61 | 62 | render() { 63 | const {classes, nodeSelected, fileContent} = this.props; 64 | if (!fileContent) return
; 65 | return ( 66 |
67 | 81 |
82 | ) 83 | } 84 | } 85 | 86 | const mapStateToProps = (state: StoreState) => { 87 | return state.files; 88 | }; 89 | 90 | const mapDispatchToProps = (dispatch: Dispatch) => { 91 | return { 92 | 93 | } 94 | }; 95 | 96 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CodeEditor)); 97 | -------------------------------------------------------------------------------- /src/containers/editorWindow/FileBrowser/CodeEditor/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './CodeEditor'; -------------------------------------------------------------------------------- /src/containers/editorWindow/FileBrowser/FileTreeView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FileTreeView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/FileBrowser/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './FileBrowser'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/HeaderView/NotificationListView/NotificationListView.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/HeaderView/NotificationListView/NotificationListView.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {NavigationState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import Popover from "@material-ui/core/Popover"; 10 | import Paper from "@material-ui/core/Paper"; 11 | import List from '@material-ui/core/List'; 12 | import ListItem, { ListItemProps } from '@material-ui/core/ListItem'; 13 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 14 | import ListItemText from '@material-ui/core/ListItemText'; 15 | import {NotificationType, Notification} from "../../../../../interface"; 16 | import Avatar from "@material-ui/core/Avatar"; 17 | import NotificationsNoneIcon from '@material-ui/icons/NotificationsNone'; 18 | import NewReleasesIcon from '@material-ui/icons/NewReleases'; 19 | import {green} from "@material-ui/core/colors"; 20 | import Typography from "@material-ui/core/Typography"; 21 | 22 | const styles = createStyles({ 23 | root: { 24 | 25 | }, 26 | paper: { 27 | padding: 0, 28 | }, 29 | emptyText: { 30 | color: 'grey', 31 | marginTop: 10, 32 | marginLeft: 20, 33 | marginRight: 20, 34 | } 35 | }); 36 | 37 | export interface Props extends WithStyles, NavigationState { 38 | notificationPopoverClose: () => void, 39 | widgetUpdateDialogOpen: () => void, 40 | } 41 | 42 | class NotificationListView extends React.Component { 43 | state = { 44 | 45 | }; 46 | 47 | componentDidMount(): void { 48 | 49 | } 50 | 51 | getNotificationIcon = (type: NotificationType) => { 52 | if (type === "widget-update") { 53 | return ( 54 | 55 | 56 | 57 | ); 58 | } 59 | else return 60 | }; 61 | 62 | handleNotificationItemClick = (notification: Notification) => () => { 63 | if (notification.type === "widget-update") { 64 | this.props.widgetUpdateDialogOpen(); 65 | } 66 | }; 67 | 68 | render() { 69 | const {classes, notificationPopoverAnchorEl, notifications} = this.props; 70 | return ( 71 |
72 | 85 | 86 | {(!notifications || notifications.length === 0) && 87 | No new notifications 88 | } 89 | 90 | {notifications.map((notification, i) => { 91 | return ( 92 | 93 | 94 | {this.getNotificationIcon(notification.type)} 95 | 96 | 100 | 101 | ) 102 | })} 103 | 104 | 105 | 106 |
107 | ) 108 | } 109 | } 110 | 111 | const mapStateToProps = (state: StoreState) => { 112 | return state.editor.navigation; 113 | }; 114 | 115 | const mapDispatchToProps = (dispatch: Dispatch) => { 116 | return { 117 | notificationPopoverClose: () => dispatch(actions.navigation.notificationPopoverClose()), 118 | widgetUpdateDialogOpen: () => dispatch(actions.navigation.widgetUpdateDialogOpen()), 119 | } 120 | }; 121 | 122 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(NotificationListView)); 123 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/HeaderView/NotificationListView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './NotificationListView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/HeaderView/RevisionListView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './RevisionListView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/HeaderView/WidgetUpdateDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './WidgetUpdateDialog'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/MVCEditor.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/MVCEditor.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import {connect} from 'react-redux'; 6 | import {Dispatch} from "redux"; 7 | import {NavigationState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import HeaderView from "./HeaderView/HeaderView"; 10 | import {MVC} from "../../../constants/editor"; 11 | import ModelListView from "./ModelEditorView"; 12 | import ProcessEditorView from "./ProcessEditorView"; 13 | import UIEditorView from "./UIEditorView"; 14 | import SettingsView from "./SettingsView"; 15 | 16 | const styles = createStyles({ 17 | root: { 18 | display: 'flex', 19 | flexFlow: "column", 20 | height: "100%", 21 | }, 22 | content: { 23 | flexGrow: 1, 24 | width: '100%' 25 | }, 26 | }); 27 | 28 | export interface Props extends WithStyles, NavigationState { 29 | 30 | } 31 | 32 | class MVCEditor extends React.Component { 33 | state = {}; 34 | operations: any = {}; 35 | componentDidMount(): void { 36 | 37 | } 38 | 39 | handleBeforeGeneratingCode = async () => { 40 | if (!!this.operations.saveData) await this.operations.saveData(); 41 | }; 42 | 43 | render() { 44 | const {classes, currentView} = this.props; 45 | return ( 46 |
47 | 48 |
49 | {currentView === MVC.Model && } 50 | {currentView === MVC.Controller && } 51 | {currentView === MVC.View && } 52 |
53 | 54 |
55 | ) 56 | } 57 | } 58 | 59 | const mapStateToProps = (state: StoreState) => { 60 | return state.editor.navigation; 61 | }; 62 | 63 | const mapDispatchToProps = (dispatch: Dispatch) => { 64 | return {} 65 | }; 66 | 67 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(MVCEditor)); 68 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ModelEditorView/BlockEditDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './BlockEditDialog'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ModelEditorView/ModelListView/definition.ts: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ModelEditorView/ModelListView/definition.ts 2 | 3 | import {Form} from "../../../../../components/interface"; 4 | 5 | export const CreateModelParamsDef: Form[] = [ 6 | { 7 | type: "input", 8 | key: "name", 9 | label: "Name of data model", 10 | required: true, 11 | autofocus: true, 12 | } 13 | ]; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ModelEditorView/ModelListView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ModelListView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ModelEditorView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ModelEditorView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/EditView/EditView.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/EditView/EditView.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {ConfigState, ProcessEditorState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import {LOADING_STATUS} from "../../../../../constants"; 10 | import {ProcessManager} from "../../../../../controllers/process/processManager"; 11 | import {ToastType} from "../../../../../components/interface"; 12 | import * as componentsActions from "../../../../../redux/modules/components/actions"; 13 | import {stepOptions} from "../stepOptions"; 14 | import {ProcessEditor} from "@flintdev/process-editor"; 15 | import StepEditDialog from "../StepEditDialog/StepEditDialog"; 16 | 17 | const styles = createStyles({ 18 | root: { 19 | height: '100%' 20 | }, 21 | }); 22 | 23 | interface State { 24 | editorDataLoadingStatus: LOADING_STATUS, 25 | } 26 | 27 | export interface Props extends WithStyles, ProcessEditorState, ConfigState { 28 | stepEditDialogOpen: (stepData: any) => void, 29 | updateEditorData: (editorData: any) => void, 30 | toastOpen: (toastType: ToastType, message: string) => void, 31 | exitEditing: () => void, 32 | } 33 | 34 | class EditView extends React.Component { 35 | state: State = { 36 | editorDataLoadingStatus: LOADING_STATUS.NOT_STARTED, 37 | }; 38 | operations: any = {}; 39 | processManager: ProcessManager; 40 | 41 | componentDidMount(): void { 42 | const {projectDir} = this.props; 43 | this.processManager = new ProcessManager(projectDir); 44 | this.initAction().then(r => {}); 45 | } 46 | 47 | componentWillUnmount(): void { 48 | this.setState({ 49 | editorDataLoadingStatus: LOADING_STATUS.NOT_STARTED, 50 | }); 51 | this.props.updateEditorData(undefined); 52 | } 53 | 54 | initAction = async () => { 55 | this.setState({editorDataLoadingStatus: LOADING_STATUS.LOADING}); 56 | const {processSelected} = this.props; 57 | const editorData = await this.processManager.getEditorData(processSelected); 58 | this.props.updateEditorData(editorData); 59 | this.setState({editorDataLoadingStatus: LOADING_STATUS.COMPLETE}); 60 | }; 61 | 62 | handleToastOpen = () => { 63 | const {processSelected} = this.props; 64 | const message = `${processSelected} is saved successfully`; 65 | this.props.toastOpen('success', message); 66 | }; 67 | 68 | editorOnSaved = async (editorData: any) => { 69 | const {processSelected} = this.props; 70 | await this.processManager.saveEditorData(processSelected, editorData); 71 | this.props.updateEditorData(editorData); 72 | this.handleToastOpen(); 73 | }; 74 | 75 | editorStepDbClick = (stepData: any) => { 76 | this.props.stepEditDialogOpen(stepData); 77 | }; 78 | 79 | render() { 80 | const {classes, editorData, processSelected} = this.props; 81 | const {editorDataLoadingStatus} = this.state; 82 | return ( 83 |
84 | {editorDataLoadingStatus === LOADING_STATUS.COMPLETE && 85 | 94 | } 95 | 96 | 99 |
100 | ) 101 | } 102 | } 103 | 104 | const mapStateToProps = (state: StoreState) => { 105 | return {...state.editor.processEditor, ...state.config}; 106 | }; 107 | 108 | const mapDispatchToProps = (dispatch: Dispatch) => { 109 | return { 110 | updateEditorData: (editorData: any) => dispatch(actions.processEditor.updateEditorData(editorData)), 111 | toastOpen: (toastType: ToastType, message: string) => dispatch(componentsActions.toastOpen(toastType, message)), 112 | stepEditDialogOpen: (stepData: any) => dispatch(actions.processEditor.stepEditDialogOpen(stepData)), 113 | exitEditing: () => dispatch(actions.processEditor.exitEditing()), 114 | } 115 | }; 116 | 117 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(EditView)); 118 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/EditView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './EditView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/ProcessEditorView.tsx: -------------------------------------------------------------------------------- 1 | // 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import {connect} from 'react-redux'; 6 | import {Dispatch} from "redux"; 7 | import {ProcessEditorState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import ProcessListView from "./ProcessListView"; 10 | import EditView from "./EditView"; 11 | 12 | const styles = createStyles({ 13 | root: { 14 | flexGrow: 1, 15 | display: 'flex', 16 | height: '100%', 17 | flexDirection: 'column', 18 | }, 19 | content: { 20 | marginTop: 2, 21 | flexGrow: 1, 22 | }, 23 | }); 24 | 25 | export interface Props extends WithStyles, ProcessEditorState { 26 | 27 | } 28 | 29 | class ProcessEditorView extends React.Component { 30 | state = {}; 31 | 32 | componentDidMount(): void { 33 | 34 | } 35 | 36 | render() { 37 | const {classes, processSelected} = this.props; 38 | return ( 39 |
40 |
41 | {!processSelected && 42 | 43 | } 44 | {!!processSelected && 45 | 46 | } 47 |
48 |
49 | ) 50 | } 51 | } 52 | 53 | const mapStateToProps = (state: StoreState) => { 54 | return state.editor.processEditor; 55 | }; 56 | 57 | const mapDispatchToProps = (dispatch: Dispatch) => { 58 | return {} 59 | }; 60 | 61 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ProcessEditorView)); 62 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/ProcessListView/definition.ts: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/ProcessListView/definition.ts 2 | 3 | import {Form} from "../../../../../components/interface"; 4 | 5 | export const CreateProcessParamsDef: Form[] = [ 6 | { 7 | type: "input", 8 | key: "name", 9 | label: "Process Name", 10 | required: true, 11 | autofocus: true, 12 | } 13 | ]; 14 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/ProcessListView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ProcessListView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/CodeBlockPane.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/CodeBlockPane.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {ProcessEditorState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import Paper from '@material-ui/core/Paper'; 10 | import AceEditor from "react-ace"; 11 | import "ace-builds/src-noconflict/mode-python"; 12 | import "ace-builds/src-noconflict/theme-tomorrow"; 13 | import {TemplateMap} from "./templates/templateMap"; 14 | import {StepAttributes, StepType} from "../interface"; 15 | 16 | const styles = createStyles({ 17 | root: { 18 | 19 | }, 20 | paper: { 21 | padding: 10, 22 | }, 23 | }); 24 | 25 | export interface Props extends WithStyles, ProcessEditorState { 26 | attributes: StepAttributes, 27 | code: string, 28 | onUpdated: (code: string) => void, 29 | } 30 | 31 | class CodeBlockPane extends React.Component { 32 | state = { 33 | 34 | }; 35 | 36 | componentDidMount(): void { 37 | } 38 | 39 | handleCodeChange = (value: string) => { 40 | this.props.onUpdated(value); 41 | }; 42 | 43 | componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { 44 | if (prevProps.attributes !== this.props.attributes) { 45 | this.setDefaultCode(); 46 | } 47 | } 48 | 49 | setDefaultCode = () => { 50 | const {code, attributes, processSelected} = this.props; 51 | const {type, name} = attributes; 52 | if (!code || code === "") { 53 | let defaultCode; 54 | switch (type) { 55 | case StepType.CODE_BLOCK: 56 | defaultCode = TemplateMap[type](name); 57 | break; 58 | case StepType.TRIGGER: 59 | defaultCode = TemplateMap[type](processSelected); 60 | break; 61 | default: 62 | defaultCode = ""; 63 | break; 64 | } 65 | this.handleCodeChange(defaultCode); 66 | } 67 | }; 68 | 69 | render() { 70 | const {classes, code} = this.props; 71 | return ( 72 |
73 | 74 | 90 | 91 |
92 | ) 93 | } 94 | } 95 | 96 | const mapStateToProps = (state: StoreState) => { 97 | return state.editor.processEditor; 98 | }; 99 | 100 | const mapDispatchToProps = (dispatch: Dispatch) => { 101 | return { 102 | 103 | } 104 | }; 105 | 106 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CodeBlockPane)); 107 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './CodeBlockPane'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/templates/executeFunc.txt: -------------------------------------------------------------------------------- 1 | def execute(handler): 2 | pass -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/templates/templateMap.ts: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/templates/templateMap.ts 2 | 3 | import executeFuncTemplate from './executeFunc.txt'; 4 | import triggerFuncTemplate from './triggerFunc.txt'; 5 | import {StepType} from "../../interface"; 6 | import * as _ from 'lodash'; 7 | import * as Mustache from "mustache"; 8 | 9 | export const TemplateMap = { 10 | [StepType.CODE_BLOCK]: (stepName: string) => { 11 | return Mustache.render(executeFuncTemplate, {package: _.camelCase(stepName)}) 12 | }, 13 | [StepType.TRIGGER]: (processName: string) => { 14 | return Mustache.render(triggerFuncTemplate, {package: _.camelCase(processName)}) 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/CodeBlockPane/templates/triggerFunc.txt: -------------------------------------------------------------------------------- 1 | package {{package}} 2 | 3 | import ( 4 | workflowFramework "github.com/flintdev/workflow-engine/engine" 5 | ) 6 | 7 | func TriggerCondition(event workflowFramework.Event) bool { 8 | // return event.Model == "expense" && event.Type == "ADDED" 9 | } -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/ManualPane/ManualPane.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/ManualPane/ManualPane.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {ConfigState, ProcessEditorState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import {ManualData, TriggerEventType} from "../../../../../../interface"; 10 | import {ModelManager} from "../../../../../../controllers/model/modelManager"; 11 | import TextField from "@material-ui/core/TextField"; 12 | import MenuItem from "@material-ui/core/MenuItem"; 13 | import StepOutputsPane from "../StepOutputsPane"; 14 | import {Output} from "../../../../../../controllers/process/processDataHandler"; 15 | 16 | const styles = createStyles({ 17 | root: { 18 | 19 | }, 20 | form: { 21 | marginTop: 10, 22 | marginBottom: 10, 23 | } 24 | }); 25 | 26 | export interface Props extends WithStyles, ProcessEditorState, ConfigState { 27 | data: ManualData, 28 | onChange: (data: ManualData) => void, 29 | } 30 | 31 | interface State { 32 | modelList: string[], 33 | } 34 | 35 | const EventTypeList = [TriggerEventType.ADDED, TriggerEventType.MODIFIED, TriggerEventType.DELETED]; 36 | 37 | class ManualPane extends React.Component { 38 | state: State = { 39 | modelList: [], 40 | }; 41 | 42 | componentDidMount(): void { 43 | this.initAction().then(r => {}); 44 | } 45 | 46 | initAction = async () => { 47 | const {projectDir} = this.props; 48 | const modelList = await new ModelManager(projectDir).getModelList(); 49 | this.setState({modelList}); 50 | }; 51 | 52 | handleFormChange = (key: string) => (event: React.ChangeEvent) => { 53 | const {value} = event.target; 54 | let {data} = this.props; 55 | this.props.onChange({...data, [key]: value}); 56 | }; 57 | 58 | getFormValue = (key: string, data: any) => { 59 | if (!data[key]) return ""; 60 | return data[key]; 61 | }; 62 | 63 | handleOutputsUpdated = (outputs: Output[]) => { 64 | const {data} = this.props; 65 | this.props.onChange({...data, outputs}); 66 | }; 67 | 68 | render() { 69 | const {classes, data} = this.props; 70 | const {modelList} = this.state; 71 | return ( 72 |
73 | 83 | {modelList.map((model, i) => { 84 | return ( 85 | {model} 86 | ) 87 | })} 88 | 89 | 90 | 100 | {EventTypeList.map((eventType, i) => { 101 | return ( 102 | {eventType} 103 | ) 104 | })} 105 | 106 | 107 | 111 | 112 |
113 | ) 114 | } 115 | } 116 | 117 | const mapStateToProps = (state: StoreState) => { 118 | return {...state.editor.processEditor, ...state.config}; 119 | }; 120 | 121 | const mapDispatchToProps = (dispatch: Dispatch) => { 122 | return { 123 | 124 | } 125 | }; 126 | 127 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ManualPane)); 128 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/ManualPane/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ManualPane'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/StepAttributePane/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/StepAttributePane/index.ts -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/StepOutputsPane/formDefinition.ts: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/StepOutputsPane/formDefinition.ts 2 | 3 | import {Form} from "../../../../../../components/interface"; 4 | 5 | const OutputConditionOptions = [ 6 | '==', '>=', '<=', '>', '<', 'contains' 7 | ]; 8 | 9 | export const OutputParamsDef: Form[] = [ 10 | { 11 | type: 'input', 12 | key: 'name', 13 | label: "Output Name", 14 | required: true, 15 | autofocus: true, 16 | }, 17 | { 18 | type: 'input', 19 | key: 'key', 20 | label: 'Condition Key Path', 21 | required: true, 22 | }, 23 | { 24 | type: 'select', 25 | key: 'operator', 26 | label: 'Condition Operator', 27 | required: true, 28 | options: OutputConditionOptions 29 | }, 30 | { 31 | type: 'input', 32 | key: 'value', 33 | label: 'Condition Value', 34 | required: true, 35 | }, 36 | ]; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/StepOutputsPane/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './StepOutputsPane'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/TriggerPane/TriggerPane.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/TriggerPane/TriggerPane.tsx 2 | 3 | import * as React from 'react'; 4 | import {createStyles, WithStyles, withStyles} from '@material-ui/core/styles'; 5 | import {TriggerData, TriggerEventType} from "../../../../../../interface"; 6 | import TextField from "@material-ui/core/TextField"; 7 | import MenuItem from "@material-ui/core/MenuItem"; 8 | import {ModelManager} from "../../../../../../controllers/model/modelManager"; 9 | import {ConfigState, ProcessEditorState, StoreState} from "../../../../../../redux/state"; 10 | import {Dispatch} from "redux"; 11 | import * as actions from "../../../../../../redux/modules/editor/actions"; 12 | import {connect} from "react-redux"; 13 | 14 | const styles = createStyles({ 15 | root: { 16 | 17 | }, 18 | form: { 19 | marginTop: 10, 20 | marginBottom: 10, 21 | } 22 | }); 23 | 24 | export interface Props extends WithStyles, ProcessEditorState, ConfigState { 25 | data: TriggerData 26 | onChange: (data: TriggerData) => void, 27 | } 28 | 29 | interface State { 30 | modelList: string[], 31 | } 32 | 33 | const EventTypeList = [TriggerEventType.ADDED, TriggerEventType.MODIFIED, TriggerEventType.DELETED]; 34 | 35 | class TriggerPane extends React.Component { 36 | state: State = { 37 | modelList: [], 38 | }; 39 | 40 | componentDidMount(): void { 41 | this.initAction().then(r => {}); 42 | } 43 | 44 | initAction = async () => { 45 | const {projectDir} = this.props; 46 | const modelList = await new ModelManager(projectDir).getModelList(); 47 | this.setState({modelList}); 48 | }; 49 | 50 | handleFormChange = (key: string) => (event: React.ChangeEvent) => { 51 | const {value} = event.target; 52 | let {data} = this.props; 53 | this.props.onChange({...data, [key]: value}); 54 | }; 55 | 56 | getFormValue = (key: string, data: any) => { 57 | if (!data[key]) return ""; 58 | return data[key]; 59 | }; 60 | 61 | render() { 62 | const {classes} = this.props; 63 | const {data} = this.props; 64 | const {modelList} = this.state; 65 | return ( 66 |
67 | 77 | {modelList.map((model, i) => { 78 | return ( 79 | {model} 80 | ) 81 | })} 82 | 83 | 84 | 94 | {EventTypeList.map((eventType, i) => { 95 | return ( 96 | {eventType} 97 | ) 98 | })} 99 | 100 | 101 | 110 |
111 | ) 112 | } 113 | } 114 | 115 | const mapStateToProps = (state: StoreState) => { 116 | return {...state.editor.processEditor, ...state.config}; 117 | }; 118 | 119 | const mapDispatchToProps = (dispatch: Dispatch) => { 120 | return { 121 | 122 | } 123 | }; 124 | 125 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(TriggerPane)); 126 | 127 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/TriggerPane/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './TriggerPane'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './StepEditDialog'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/interface.ts: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/interface.ts 2 | 3 | export interface StepAttributes { 4 | name: string, 5 | group: string, 6 | category: string, 7 | type: string, 8 | } 9 | 10 | export enum StepType { 11 | CODE_BLOCK = 'Code Block', 12 | TRIGGER = 'Trigger', 13 | MANUAL = 'Manual', 14 | END = 'End', 15 | } -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ProcessEditorView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/ProcessEditorView/stepOptions.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/ProcessEditorView/stepOptions.tsx 2 | 3 | import * as React from 'react'; 4 | import CodeIcon from '@material-ui/icons/Code'; 5 | import EmailTwoToneIcon from '@material-ui/icons/EmailTwoTone'; 6 | import AssignmentTurnedInTwoToneIcon from '@material-ui/icons/AssignmentTurnedInTwoTone'; 7 | import AddCircleTwoToneIcon from '@material-ui/icons/AddCircleTwoTone'; 8 | import RadioButtonCheckedIcon from '@material-ui/icons/RadioButtonChecked'; 9 | import AccountTreeOutlinedIcon from '@material-ui/icons/AccountTreeOutlined'; 10 | 11 | export const stepOptions = [ 12 | { 13 | "type": "Code Block", 14 | "group": "Standard", 15 | "category": "Automation", 16 | "icon": 17 | }, 18 | { 19 | "type": "End", 20 | "group": "Standard", 21 | "category": "Automation", 22 | "icon": 23 | }, 24 | { 25 | "type": "Hub", 26 | "group": "Standard", 27 | "category": "Automation", 28 | "icon": 29 | }, 30 | { 31 | "type": "Manual", 32 | "group": "Standard", 33 | "category": "Manual", 34 | "icon": 35 | }, 36 | { 37 | "type": "Trigger", 38 | "group": "Standard", 39 | "category": "Automation", 40 | "icon": 41 | }, 42 | { 43 | "type": "Gmail", 44 | "group": "Third-party", 45 | "category": "Notification", 46 | "icon": 47 | } 48 | ]; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/SettingsView/LibraryPane/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './LibraryPane'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/SettingsView/SettingsView.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/MVCEditor/SettingsView/SettingsView.tsx 2 | 3 | import * as React from 'react'; 4 | import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; 5 | import { connect } from 'react-redux'; 6 | import { Dispatch } from "redux"; 7 | import {SettingsState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/editor/actions"; 9 | import Dialog from "@material-ui/core/Dialog"; 10 | import Paper from "@material-ui/core/Paper"; 11 | import Tabs from '@material-ui/core/Tabs'; 12 | import Tab from '@material-ui/core/Tab'; 13 | import WidgetsIcon from '@material-ui/icons/Widgets'; 14 | import LibraryPane from "./LibraryPane"; 15 | 16 | const styles = createStyles({ 17 | root: { 18 | 19 | }, 20 | paper: { 21 | 22 | }, 23 | content: { 24 | marginTop: 10, 25 | marginBottom: 10, 26 | marginLeft: 20, 27 | marginRight: 20, 28 | }, 29 | }); 30 | 31 | export interface Props extends WithStyles, SettingsState { 32 | settingsDialogClose: () => void, 33 | } 34 | 35 | const Views = [{ 36 | icon: , 37 | label: 'Library', 38 | }]; 39 | 40 | interface State { 41 | tabIndex: number 42 | } 43 | class SettingsView extends React.Component { 44 | state: State = { 45 | tabIndex: 0 46 | }; 47 | 48 | componentDidMount(): void { 49 | 50 | } 51 | 52 | onEnter = () => { 53 | 54 | }; 55 | 56 | handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => { 57 | this.setState({tabIndex: newValue}); 58 | } 59 | 60 | render() { 61 | const {classes, open} = this.props; 62 | const {tabIndex} = this.state; 63 | return ( 64 |
65 | 73 | 74 | 80 | {Views.map((item, i) => { 81 | return ( 82 | 83 | ) 84 | })} 85 | 86 | 87 |
88 | {tabIndex === 0 && } 89 |
90 |
91 |
92 | ) 93 | } 94 | } 95 | 96 | const mapStateToProps = (state: StoreState) => { 97 | return state.editor.settings; 98 | }; 99 | 100 | const mapDispatchToProps = (dispatch: Dispatch) => { 101 | return { 102 | settingsDialogClose: () => dispatch(actions.settings.settingsDialogClose()), 103 | } 104 | }; 105 | 106 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(SettingsView)); 107 | -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/SettingsView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './SettingsView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/UIEditorView/AddLibraryDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AddLibraryDialog'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/UIEditorView/AddWidgetDialog/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AddWidgetDialog'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/UIEditorView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './UIEditorView'; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/exampleData.ts: -------------------------------------------------------------------------------- 1 | import {EditorData} from "@flintdev/model-editor/dist/interface"; 2 | 3 | 4 | export const editorDataSample1: EditorData = { 5 | treeData: [ 6 | { 7 | id: 'root-creator', 8 | name: 'creator', 9 | type: 'object', 10 | params: {}, 11 | children: [ 12 | { 13 | id: 'root-creator-name', 14 | name: 'name', 15 | type: 'string', 16 | }, 17 | { 18 | id: 'root-creator-password', 19 | name: 'password', 20 | type: 'string', 21 | } 22 | ] 23 | }, 24 | { 25 | id: 'root-time', 26 | name: 'time', 27 | type: 'string', 28 | params: {}, 29 | }, 30 | { 31 | id: 'root-records', 32 | name: 'records', 33 | type: 'array', 34 | params: {}, 35 | children: [ 36 | { 37 | id: 'root-records-items', 38 | name: 'items', 39 | type: 'string', 40 | }, 41 | ] 42 | } 43 | ] 44 | }; -------------------------------------------------------------------------------- /src/containers/editorWindow/MVCEditor/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './MVCEditor'; -------------------------------------------------------------------------------- /src/containers/editorWindow/NavigationSidebar/NavigationSidebar.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/editorWindow/NavigationSidebar/NavigationSidebar.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import {connect} from 'react-redux'; 6 | import {Dispatch} from "redux"; 7 | import {ConfigState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/config/actions"; 9 | import {Page, NavigationPages} from "../../../constants/editor"; 10 | import {theme, themeColor} from "../../../constants"; 11 | import DashboardOutlinedIcon from '@material-ui/icons/DashboardOutlined'; 12 | import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined'; 13 | import AppsIcon from '@material-ui/icons/Apps'; 14 | import CloudUploadOutlinedIcon from '@material-ui/icons/CloudUploadOutlined'; 15 | import {NavigationSidebarConfig} from "../../../constants/styles"; 16 | import Tooltip from '@material-ui/core/Tooltip'; 17 | 18 | const styles = createStyles({ 19 | root: { 20 | textAlign: 'center', 21 | marginTop: -1, 22 | }, 23 | tabItem: { 24 | height: 70, 25 | width: NavigationSidebarConfig.Width - 4, 26 | textAlign: 'center', 27 | cursor: 'pointer', 28 | display: 'flex', 29 | alignItems: 'center', 30 | justifyContent: 'center', 31 | }, 32 | tabItemActive: { 33 | height: 70, 34 | width: NavigationSidebarConfig.Width - 4, 35 | textAlign: 'center', 36 | cursor: 'pointer', 37 | display: 'flex', 38 | alignItems: 'center', 39 | justifyContent: 'center', 40 | borderLeft: `${4}px solid ${themeColor.primary}`, 41 | borderRight: `${4}px solid white`, 42 | marginLeft: -1, 43 | }, 44 | icon: { 45 | fontSize: 28, 46 | marginRight: -2, 47 | color: themeColor.dimgrey, 48 | }, 49 | iconActive: { 50 | fontSize: 28, 51 | color: themeColor.primary, 52 | marginRight: 4, 53 | }, 54 | tooltip: { 55 | fontSize: 14 56 | } 57 | }); 58 | 59 | export interface Props extends WithStyles, ConfigState { 60 | setCurrentPage?: (pageIndex: number) => void, 61 | } 62 | 63 | const ItemIcon = (props: Props) => { 64 | const {classes, currentPageIndex} = props; 65 | return { 66 | [Page.Editor]: , 67 | [Page.Files]: , 68 | [Page.Plugins]: , 69 | [Page.Delivery]: , 71 | } 72 | }; 73 | 74 | class NavigationSidebar extends React.Component { 75 | state = {}; 76 | 77 | componentDidMount(): void { 78 | 79 | } 80 | 81 | handleTabItemClick = (pageIndex: number) => () => { 82 | this.props.setCurrentPage(pageIndex); 83 | }; 84 | 85 | render() { 86 | const {classes, currentPageIndex} = this.props; 87 | return ( 88 |
89 | {NavigationPages.map((page, i) => { 90 | return ( 91 |
96 | 97 | {ItemIcon(this.props)[page.key]} 98 | 99 |
100 | ) 101 | })} 102 |
103 | ) 104 | } 105 | } 106 | 107 | const mapStateToProps = (state: StoreState) => { 108 | return state.config; 109 | }; 110 | 111 | const mapDispatchToProps = (dispatch: Dispatch) => { 112 | return { 113 | setCurrentPage: (pageIndex: number) => dispatch(actions.setCurrentPage(pageIndex)), 114 | 115 | } 116 | }; 117 | 118 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(NavigationSidebar)); 119 | -------------------------------------------------------------------------------- /src/containers/editorWindow/NavigationSidebar/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './NavigationSidebar'; -------------------------------------------------------------------------------- /src/containers/starterWindow/ActionView/index.ts: -------------------------------------------------------------------------------- 1 | export {default} from './ActionView'; -------------------------------------------------------------------------------- /src/containers/starterWindow/CreateProjectDialog/CreateProjectDialog.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/starterWindow/CreateProjectDialog/CreateProjectDialog.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import {connect} from 'react-redux'; 6 | import {Dispatch} from "redux"; 7 | import {StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/starter/actions"; 9 | import {FSHelper} from "../../../controllers/utils/fsHelper"; 10 | import {MainProcessCommunicator} from "../../../controllers/mainProcessCommunicator"; 11 | import Dialog from "@material-ui/core/Dialog"; 12 | import DialogTitle from "@material-ui/core/DialogTitle"; 13 | import DialogContent from "@material-ui/core/DialogContent"; 14 | import DialogActions from "@material-ui/core/DialogActions"; 15 | import Button from "@material-ui/core/Button"; 16 | import ParamForm from "./ParamForm"; 17 | import {ProjectManager} from "../../../controllers/project/projectManager"; 18 | 19 | const styles = createStyles({ 20 | root: {}, 21 | }); 22 | 23 | export interface Props extends WithStyles { 24 | open?: boolean, 25 | createProjectDialogClose?: () => void, 26 | setRecentProjects: (projects: any[]) => void, 27 | } 28 | 29 | class CreateProjectDialog extends React.Component { 30 | state = { 31 | submitLoading: false, 32 | params: { 33 | location: '', 34 | }, 35 | }; 36 | 37 | componentDidMount(): void { 38 | 39 | } 40 | 41 | onEnter = () => { 42 | 43 | }; 44 | 45 | handleFormChange = (params: object) => { 46 | this.setState({params}); 47 | }; 48 | 49 | handleSubmitButtonClick = async () => { 50 | this.setState({submitLoading: true}); 51 | const {location} = this.state.params; 52 | const projectManager = new ProjectManager(location); 53 | try { 54 | await projectManager.createProjectDir(); 55 | // init .flint dir 56 | await projectManager.initializeProjectFiles(); 57 | this.setState({submitLoading: false}); 58 | this.props.createProjectDialogClose(); 59 | await new MainProcessCommunicator().switchFromStarterToEditorWindow(location); 60 | // update recent projects 61 | ProjectManager.UpdateRecentProjects(location); 62 | const recentProjects = ProjectManager.GetRecentProjects(); 63 | this.props.setRecentProjects(recentProjects); 64 | } catch (e) { 65 | console.log(e); 66 | } 67 | }; 68 | 69 | render() { 70 | const {classes, open} = this.props; 71 | const {submitLoading} = this.state; 72 | return ( 73 |
74 | 80 | New Project 81 | 82 | 83 | 84 | 85 | 86 | 94 | 95 | 96 |
97 | ) 98 | } 99 | } 100 | 101 | const mapStateToProps = (state: StoreState) => { 102 | return state.starter.createProjectDialog; 103 | }; 104 | 105 | const mapDispatchToProps = (dispatch: Dispatch) => { 106 | return { 107 | createProjectDialogClose: () => dispatch(actions.createProjectDialogClose()), 108 | setRecentProjects: (projects: any[]) => dispatch(actions.setRecentProjects(projects)), 109 | 110 | } 111 | }; 112 | 113 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(CreateProjectDialog)); 114 | -------------------------------------------------------------------------------- /src/containers/starterWindow/CreateProjectDialog/ParamForm.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/starterWindow/CreateProjectDialog/ParamForm.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import InputAdornment from '@material-ui/core/InputAdornment'; 6 | import TextField from '@material-ui/core/TextField'; 7 | import {FSHelper} from "../../../controllers/utils/fsHelper"; 8 | import FolderOpenIcon from '@material-ui/icons/FolderOpen'; 9 | 10 | const styles = createStyles({ 11 | root: {}, 12 | }); 13 | 14 | export interface Props extends WithStyles { 15 | onChange: (params: object) => void, 16 | } 17 | 18 | class ParamForm extends React.Component { 19 | state = { 20 | location: '', 21 | }; 22 | 23 | componentDidMount(): void { 24 | const defaultLocation = new FSHelper().getDefaultPath(); 25 | const params = {location: defaultLocation}; 26 | this.setState({...params}); 27 | this.handleFormChange(params); 28 | } 29 | 30 | handleFormChange = (params: object) => { 31 | this.props.onChange(params); 32 | }; 33 | 34 | private handleLocationInputChange = (event: React.ChangeEvent) => { 35 | const location = event.target.value; 36 | const params = {location}; 37 | this.setState({...params}); 38 | this.handleFormChange(params); 39 | }; 40 | 41 | render() { 42 | const {classes} = this.props; 43 | const {location} = this.state; 44 | return ( 45 |
46 | 52 | 53 | 54 | ), 55 | }} 56 | value={location} 57 | onChange={this.handleLocationInputChange} 58 | /> 59 |
60 | ) 61 | } 62 | } 63 | 64 | export default withStyles(styles)(ParamForm); 65 | -------------------------------------------------------------------------------- /src/containers/starterWindow/CreateProjectDialog/index.ts: -------------------------------------------------------------------------------- 1 | export {default} from './CreateProjectDialog'; -------------------------------------------------------------------------------- /src/containers/starterWindow/ProjectListView/ProjectListView.tsx: -------------------------------------------------------------------------------- 1 | // src/containers/starterWindow/ProjectListView/ProjectListView.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles} from '@material-ui/core/styles'; 5 | import {connect} from 'react-redux'; 6 | import {Dispatch} from "redux"; 7 | import {StarterState, StoreState} from "src/redux/state"; 8 | import * as actions from "src/redux/modules/starter/actions"; 9 | import {ProjectManager} from "../../../controllers/project/projectManager"; 10 | import List from '@material-ui/core/List'; 11 | import ListItem, {ListItemProps} from '@material-ui/core/ListItem'; 12 | import ListItemIcon from '@material-ui/core/ListItemIcon'; 13 | import ListItemText from '@material-ui/core/ListItemText'; 14 | import CloseIcon from '@material-ui/icons/Close'; 15 | import IconButton from "@material-ui/core/IconButton"; 16 | import ListSubheader from '@material-ui/core/ListSubheader'; 17 | 18 | const styles = createStyles({ 19 | root: { 20 | overflow: 'auto', 21 | paddingLeft: 10, 22 | paddingRight: 10, 23 | }, 24 | list: { 25 | overflow: 'auto' 26 | }, 27 | listItem: { 28 | paddingTop: 5, 29 | paddingBottom: 5, 30 | } 31 | }); 32 | 33 | export interface Props extends WithStyles, StarterState { 34 | validationDialogOpen: (projectDir: string) => void, 35 | setRecentProjects: (projects: any[]) => void, 36 | } 37 | 38 | class ProjectListView extends React.Component { 39 | 40 | componentDidMount(): void { 41 | const projects = ProjectManager.GetRecentProjects(); 42 | this.props.setRecentProjects(projects); 43 | } 44 | 45 | handleListItemClick = (project: any) => () => { 46 | const projectDir = project.path; 47 | this.props.validationDialogOpen(projectDir); 48 | ProjectManager.UpdateRecentProjects(projectDir); 49 | const recentProjects = ProjectManager.GetRecentProjects(); 50 | this.props.setRecentProjects(recentProjects); 51 | }; 52 | 53 | handleDeleteProjectClick = (project: any) => (event: React.MouseEvent) => { 54 | event.stopPropagation(); 55 | const projectDir = project.path; 56 | ProjectManager.RemoveRecentProject(projectDir); 57 | const recentProjects = ProjectManager.GetRecentProjects(); 58 | this.props.setRecentProjects(recentProjects); 59 | }; 60 | 61 | render() { 62 | const {classes} = this.props; 63 | const {recentProjects} = this.props; 64 | return ( 65 |
66 | 70 | Recent Projects 71 | 72 | } 73 | > 74 | {recentProjects.map((project, i) => { 75 | return ( 76 | 82 | 83 | 87 | 88 | 89 | 90 | ) 91 | })} 92 | 93 |
94 | ) 95 | } 96 | } 97 | 98 | const mapStateToProps = (state: StoreState) => { 99 | return state.starter; 100 | }; 101 | 102 | const mapDispatchToProps = (dispatch: Dispatch) => { 103 | return { 104 | validationDialogOpen: (projectDir: string) => dispatch(actions.validationDialogOpen(projectDir)), 105 | setRecentProjects: (projects: any[]) => dispatch(actions.setRecentProjects(projects)), 106 | } 107 | }; 108 | 109 | export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(ProjectListView)); 110 | -------------------------------------------------------------------------------- /src/containers/starterWindow/ProjectListView/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ProjectListView'; -------------------------------------------------------------------------------- /src/containers/starterWindow/StarterContainer.tsx: -------------------------------------------------------------------------------- 1 | // containers/starterWindow/StarterContainer.tsx 2 | 3 | import * as React from 'react'; 4 | import {withStyles, WithStyles, createStyles, ThemeProvider} from '@material-ui/core/styles'; 5 | import ActionView from "./ActionView"; 6 | import CreateProjectDialog from "./CreateProjectDialog"; 7 | import {theme} from "../../constants"; 8 | import ValidationDialog from "./ValidationDialog/ValidationDialog"; 9 | import ProjectListView from "./ProjectListView"; 10 | 11 | const styles = createStyles({ 12 | root: { 13 | width: '100%', 14 | height: '100%' 15 | }, 16 | projectsContainer: { 17 | width: '100%', 18 | height: '100%', 19 | }, 20 | grid: { 21 | height: '100%' 22 | }, 23 | table: { 24 | width: '100%', 25 | height: '100vh', 26 | border: 0, 27 | cellSpacing: 0, 28 | cellPadding: 0, 29 | borderSpacing: 0, 30 | borderCollapse: 'collapse', 31 | }, 32 | tdLeft: { 33 | width: '50%', 34 | height: '100%', 35 | borderRight: '1px solid lightgrey' 36 | }, 37 | tdRight: { 38 | width: '50%', 39 | height: '100%', 40 | }, 41 | tr: { 42 | border: 0, 43 | borderCollapse: 'collapse', 44 | } 45 | }); 46 | 47 | export interface Props extends WithStyles { 48 | 49 | } 50 | 51 | function StarterContainer(props: Props) { 52 | const {classes} = props; 53 | return ( 54 | 55 |
56 | 57 | 58 | 59 | 62 | 67 | 68 | 69 |
60 | 61 | 63 |
64 | 65 |
66 |
70 | 71 | 72 |
73 |
74 | ) 75 | } 76 | 77 | export default withStyles(styles)(StarterContainer); -------------------------------------------------------------------------------- /src/containers/starterWindow/ValidationDialog/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/src/containers/starterWindow/ValidationDialog/index.ts -------------------------------------------------------------------------------- /src/controllers/files/__test__/sourceFileManager.test.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/files/__test__/sourceFileManager.test.ts 2 | 3 | import {SourceFileManager} from "../sourceFileManager"; 4 | 5 | test('get tree data', () => { 6 | const rootDir = '/Users/naitianliu/Flint/test12'; 7 | const treeData = new SourceFileManager(rootDir).getTreeData(); 8 | console.log(treeData); 9 | }); 10 | -------------------------------------------------------------------------------- /src/controllers/files/sourceFileManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/files/sourceFileManager.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import {FileTreeNode} from "../../interface"; 5 | 6 | const excluded_path = [ 7 | 'src/ui/node_modules' 8 | ]; 9 | 10 | export class SourceFileManager { 11 | rootDir: string; 12 | fsHelper: FSHelper; 13 | 14 | constructor(rootDir: string) { 15 | this.rootDir = rootDir; 16 | this.fsHelper = new FSHelper(); 17 | } 18 | 19 | getTreeData = async () => { 20 | const treeData = await this.recurToChildren(this.rootDir); 21 | return treeData; 22 | }; 23 | 24 | getFileContent = async (path: string) => { 25 | try { 26 | return await this.fsHelper.readFile(path); 27 | } catch (e) { 28 | return null; 29 | } 30 | }; 31 | 32 | private recurToChildren = async (parentPath: string) => { 33 | const files = await this.fsHelper.readDir(parentPath); 34 | let nodes: FileTreeNode[] = []; 35 | for (let file of files) { 36 | const {name, type} = file; 37 | const path = `${parentPath}/${name}`; 38 | if (this.isExcludedPath(path)) continue; 39 | if (type === 'dir') { 40 | const children: FileTreeNode[] = await this.recurToChildren(path); 41 | const node = {name, type, path, children}; 42 | nodes.push(node); 43 | } else { 44 | const node = {name, type, path}; 45 | nodes.push(node); 46 | } 47 | } 48 | return nodes; 49 | }; 50 | 51 | private isExcludedPath = (absPath: string) => { 52 | for (const path of excluded_path) { 53 | if (absPath.includes(path)) return true; 54 | } 55 | return false; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /src/controllers/localStoreManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/localStoreManager.ts 2 | 3 | export class LocalStorageManager { 4 | setProjectDir(projectDir: string) { 5 | localStorage.projectDir = projectDir; 6 | } 7 | 8 | clearProjectDir() { 9 | localStorage.removeItem('projectDir'); 10 | } 11 | 12 | getProjectDir(): string { 13 | return localStorage.projectDir; 14 | } 15 | 16 | setRecentProjects = (projectDirs: string[]) => { 17 | localStorage.setItem('recentProjects', JSON.stringify(projectDirs)); 18 | }; 19 | 20 | getRecentProjects = (): string[] => { 21 | let projectDirs = localStorage.getItem('recentProjects'); 22 | if (!projectDirs) return []; 23 | return JSON.parse(projectDirs); 24 | }; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/controllers/model/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Config Path 3 | 4 | ``` 5 | .flint/models/config.json 6 | ``` 7 | 8 | ## Config data structure 9 | 10 | ```json 11 | { 12 | "models": ["m1", "m2", "m3"], 13 | "editorData": { 14 | "m1": {}, 15 | "m2": {}, 16 | "m3": {} 17 | }, 18 | "revision": { 19 | "m1": { 20 | "editor": 2, 21 | "source": 2 22 | }, 23 | "m2": { 24 | "editor": 2, 25 | "source": 1 26 | }, 27 | "m3": { 28 | "editor": 2, 29 | "source": 2 30 | } 31 | } 32 | } 33 | ``` -------------------------------------------------------------------------------- /src/controllers/model/__test__/specGenerator.test.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/model/__test__/specGenerator.test.ts 2 | 3 | import {SpecGenerator} from "../specGenerator"; 4 | 5 | test('render crd spec json', () => { 6 | const modelName = 'testmodel'; 7 | const schemaData = { 8 | 'f1': 'string', 9 | 'f2': 'string', 10 | "f3": { 11 | 'test': '123' 12 | } 13 | }; 14 | // const crdJson = new SpecGenerator().renderCRDSpecJson(modelName, schemaData); 15 | // console.log(crdJson); 16 | }); -------------------------------------------------------------------------------- /src/controllers/model/dataConverter.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/model/dataConverter.ts 2 | 3 | interface BlockItem { 4 | id: string, 5 | name: string, 6 | dataType: "string" | "integer" | "object" | "boolean" | "string[]" | "integer[]" | "$ref" | "$ref[]", 7 | required: boolean, 8 | indexed: boolean, 9 | } 10 | 11 | interface BlockData { 12 | name: string, 13 | items: BlockItem[], 14 | refs: { 15 | [key: string]: string 16 | } 17 | } 18 | 19 | interface BlockDataMap { 20 | [key: string]: BlockData 21 | } 22 | 23 | export class DataConverter { 24 | editorData: any; 25 | modelName: string; 26 | blockDataMap: BlockDataMap; 27 | constructor(editorData: any, modelName: string) { 28 | this.editorData = editorData; 29 | this.modelName = modelName; 30 | this.blockDataMap = this.getBlockDataMap(); 31 | } 32 | 33 | convertToOpenAPISchema = () => { 34 | return this.getBlockSchema(this.modelName); 35 | }; 36 | 37 | private getBlockDataMap = (): BlockDataMap => { 38 | const blockData: BlockData[] = this.editorData?.blockData; 39 | if (!blockData || blockData.length === 0) return {}; 40 | let blockDataMap: BlockDataMap = {}; 41 | blockData.forEach(data => { 42 | blockDataMap[data.name] = data; 43 | }); 44 | return blockDataMap; 45 | }; 46 | 47 | private getBlockSchema = (blockName: string) => { 48 | const data = this.blockDataMap[blockName]; 49 | let properties: any = {}; 50 | const {items, refs} = data; 51 | items.forEach(item => { 52 | if (item.dataType === 'string' || item.dataType === 'integer' || item.dataType === "boolean" || item.dataType === "object") { 53 | const type = item.dataType; 54 | properties[item.name] = {type}; 55 | } else if (item.dataType === "string[]") { 56 | properties[item.name] = { 57 | type: 'array', 58 | items: {type: 'string'} 59 | }; 60 | } else if (item.dataType === "integer[]") { 61 | properties[item.name] = { 62 | type: 'array', 63 | items: {type: 'integer'} 64 | }; 65 | } else if (item.dataType === "$ref") { 66 | const refName = refs[item.name]; 67 | properties[item.name] = this.getBlockSchema(refName); 68 | } else if (item.dataType === "$ref[]") { 69 | const refName = refs[item.name]; 70 | properties[item.name] = { 71 | type: 'array', 72 | items: this.getBlockSchema(refName) 73 | } 74 | } 75 | }); 76 | return {type: 'object', properties}; 77 | }; 78 | 79 | } -------------------------------------------------------------------------------- /src/controllers/model/modelManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/model/modelManager.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import * as _ from 'lodash'; 5 | import {getInitialEditorData} from '@flintdev/model-editor-canvas'; 6 | 7 | interface Config { 8 | models: Array 9 | } 10 | 11 | const INITIAL_CONFIG: Config = { 12 | models: [], 13 | }; 14 | 15 | export class ModelManager { 16 | rootDir: string; 17 | fsHelper: FSHelper; 18 | dirPath: string; 19 | configPath: string; 20 | sourceDirPath: string; 21 | constructor(rootDir: string) { 22 | this.rootDir = rootDir; 23 | this.fsHelper = new FSHelper(); 24 | this.dirPath = `${this.rootDir}/.flint/models`; 25 | this.configPath = `${this.rootDir}/.flint/models/config.json`; 26 | this.sourceDirPath = `${this.rootDir}/src/models`; 27 | } 28 | 29 | checkAndCreateModelDir = async () => { 30 | // should be called at models UI loaded 31 | try { 32 | await this.fsHelper.checkPathExists(this.dirPath); 33 | } catch (e) { 34 | await this.fsHelper.createDirByPath(this.dirPath); 35 | } 36 | }; 37 | 38 | checkAndCreateSourceDir = async () => { 39 | try { 40 | await this.fsHelper.checkPathExists(this.sourceDirPath); 41 | } catch (e) { 42 | await this.fsHelper.createDirByPath(this.sourceDirPath); 43 | } 44 | }; 45 | 46 | createModel = async (modelName: string) => { 47 | const result = await this.checkAndCreateModelConfigFile(); 48 | if (!result) return false; 49 | let configJson = await this.fetchConfigData(); 50 | configJson.models.push(modelName); 51 | await this.saveConfigData(configJson); 52 | }; 53 | 54 | deleteModel = async (modelName: string) => { 55 | let configJson = await this.fetchConfigData(); 56 | let {models, editorDataMap} = configJson; 57 | // remove from models 58 | _.remove(models, (model: string) => model === modelName); 59 | // remove from editorDataMap 60 | editorDataMap = _.omit(editorDataMap, [modelName]); 61 | _.set(configJson, ['editorDataMap'], editorDataMap); 62 | _.set(configJson, ['models'], models); 63 | await this.saveConfigData(configJson); 64 | }; 65 | 66 | getModelList = async (): Promise => { 67 | const modelList: string[] = []; 68 | const configJson = await this.fetchConfigData(); 69 | if (!configJson.models) return modelList; 70 | return configJson.models; 71 | }; 72 | 73 | getEditorData = async (modelName: string) => { 74 | const configJson = await this.fetchConfigData(); 75 | return _.get(configJson, ['editorDataMap', modelName]); 76 | }; 77 | 78 | saveEditorData = async (modelName: string, editorData: any) => { 79 | let configJson = await this.fetchConfigData(); 80 | _.set(configJson, ['editorDataMap', modelName], editorData); 81 | await this.saveConfigData(configJson); 82 | }; 83 | 84 | getInitialEditorData = (name: string) => { 85 | const canvasData = getInitialEditorData(name); 86 | const blockData: any = [{name, items:[]}]; 87 | return {canvasData, blockData}; 88 | }; 89 | 90 | private checkAndCreateModelConfigFile = async () => { 91 | try { 92 | await this.fsHelper.checkPathExists(this.configPath); 93 | return true; 94 | } catch (err) { 95 | await this.fsHelper.createFile(this.configPath, JSON.stringify(INITIAL_CONFIG)); 96 | return true; 97 | } 98 | }; 99 | 100 | private fetchConfigData = async () => { 101 | const data = await this.fsHelper.readFile(this.configPath); 102 | return JSON.parse(data); 103 | }; 104 | 105 | private saveConfigData = async (configJson: object) => { 106 | await this.fsHelper.createFile(this.configPath, JSON.stringify(configJson)); 107 | return true 108 | }; 109 | } -------------------------------------------------------------------------------- /src/controllers/model/sourceFileGenerator.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/model/sourceFileGenerator.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import {ModelManager} from "./modelManager"; 5 | import {SpecGenerator} from "./specGenerator"; 6 | import {DataConverter} from "./dataConverter"; 7 | 8 | interface File { 9 | path: string, 10 | content: string, 11 | } 12 | 13 | export class SourceFileGenerator { 14 | fsHelper = new FSHelper(); 15 | sourceDirPath: string; 16 | modelManager: ModelManager; 17 | constructor(rootDir: string) { 18 | this.sourceDirPath = `${rootDir}/src/models`; 19 | this.modelManager = new ModelManager(rootDir); 20 | } 21 | 22 | generate = async () => { 23 | await this.generateCRDSpecs(); 24 | }; 25 | 26 | private generateCRDSpecs = async () => { 27 | const models = await this.modelManager.getModelList(); 28 | let files: File[] = []; 29 | for (const modelName of models) { 30 | const editorData = await this.modelManager.getEditorData(modelName); 31 | if (!editorData) continue; 32 | const schemaData = new DataConverter(editorData, modelName).convertToOpenAPISchema(); 33 | const crdSpecYaml = new SpecGenerator().renderCRDSpecYaml(modelName, schemaData); 34 | files.push({ 35 | path: `${this.sourceDirPath}/${modelName}.yaml`, 36 | content: crdSpecYaml 37 | }); 38 | } 39 | await this.batchToCreateFiles(files); 40 | }; 41 | 42 | private removeSourceDir = async () => { 43 | await this.fsHelper.removeDir(this.sourceDirPath); 44 | }; 45 | 46 | private checkAndCreateDir = async (dir: string) => { 47 | try { 48 | await this.fsHelper.checkPathExists(dir); 49 | } catch (e) { 50 | await this.fsHelper.createDirByPath(dir); 51 | } 52 | }; 53 | 54 | private batchToCreateFiles = async (files: File[]) => { 55 | for (const file of files) { 56 | const {path, content} = file; 57 | await this.fsHelper.createFile(path, content); 58 | } 59 | }; 60 | } -------------------------------------------------------------------------------- /src/controllers/model/specGenerator.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/model/specGenerator.ts 2 | 3 | import * as yaml from 'js-yaml'; 4 | import * as _ from 'lodash'; 5 | import crdTemplate from './templates/crd.yaml'; 6 | import * as Mustache from "mustache"; 7 | 8 | export class SpecGenerator { 9 | constructor() { 10 | 11 | } 12 | 13 | renderCRDSpecYaml = (modelName: string, schemaData: any) => { 14 | // modelName must be lowercase 15 | const plural = `${modelName.toLowerCase()}s`; 16 | const singular = modelName.toLowerCase(); 17 | const camelCase = _.camelCase(modelName); 18 | const kind = _.capitalize(camelCase); 19 | const partialRenderedTemplate = Mustache.render(crdTemplate, { 20 | plural, singular, kind, 21 | }); 22 | let crdJsonStr = JSON.stringify(yaml.safeLoad(partialRenderedTemplate)); 23 | crdJsonStr = crdJsonStr.replace('"$schemaData$"', JSON.stringify(schemaData)); 24 | const crdJson = JSON.parse(crdJsonStr); 25 | return yaml.safeDump(crdJson); 26 | }; 27 | } -------------------------------------------------------------------------------- /src/controllers/model/templates/crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | # name must match the spec fields below, and be in the form: . 5 | name: {{plural}}.flintapp.io 6 | spec: 7 | # group name to use for REST API: /apis// 8 | group: flintapp.io 9 | # list of versions supported by this CustomResourceDefinition 10 | versions: 11 | - name: v1 12 | # Each version can be enabled/disabled by Served flag. 13 | served: true 14 | # One and only one version must be marked as the storage version. 15 | storage: true 16 | schema: 17 | openAPIV3Schema: $schemaData$ 18 | # either Namespaced or Cluster 19 | scope: Namespaced 20 | names: 21 | # plural name to be used in the URL: /apis/// 22 | plural: {{plural}} 23 | # singular name to be used as an alias on the CLI and for display 24 | singular: {{singular}} 25 | # kind is normally the CamelCased singular type. Your resource manifests use this. 26 | kind: {{kind}} 27 | -------------------------------------------------------------------------------- /src/controllers/process/processDataHandler.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/process/processDataHandler.ts 2 | 3 | import {StepAttributes} from "../../containers/editorWindow/MVCEditor/ProcessEditorView/StepEditDialog/interface"; 4 | 5 | export enum StepType { 6 | Trigger = "Trigger", 7 | End = "End", 8 | Hub = "Hub", 9 | Manual = "Manual", 10 | CodeBlock = "Code Block", 11 | } 12 | 13 | export interface StepData { 14 | name: string, 15 | inputs: any, 16 | outputs: any, 17 | position: any, 18 | id: number, 19 | data: { 20 | category: string, 21 | label: string, 22 | group: string, 23 | code: string, 24 | type: string, 25 | outputs: Output[] 26 | } 27 | } 28 | 29 | export interface OutputCondition { 30 | key: string, 31 | value: string, 32 | operator: 'always' | '==' | '>=' | '<=' | '>' | '<' | 'contains', 33 | } 34 | 35 | export interface Output { 36 | name: string, 37 | condition?: OutputCondition 38 | } 39 | 40 | export class ProcessDataHandler { 41 | constructor() { 42 | 43 | } 44 | 45 | parseStepData = (stepData: StepData) => { 46 | // convert step data to attributes, outputs and code 47 | const {data} = stepData; 48 | const {category, label, group, code, type, outputs} = data; 49 | const attributes = { 50 | category, type, group, name: label, 51 | }; 52 | return {attributes, code, outputs}; 53 | }; 54 | 55 | updateStepData = (stepData: StepData, attributes: StepAttributes, code: string, outputs: Output[]) => { 56 | stepData.data = { 57 | label: attributes.name, 58 | ...attributes, 59 | code, 60 | outputs 61 | }; 62 | return stepData; 63 | }; 64 | } -------------------------------------------------------------------------------- /src/controllers/process/processManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/process/processManager.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import * as _ from 'lodash'; 5 | 6 | interface Config { 7 | processes: object[] 8 | } 9 | 10 | const INITIAL_CONFIG: Config = { 11 | processes: [] 12 | }; 13 | 14 | export class ProcessManager { 15 | rootDir: string; 16 | fsHelper: FSHelper; 17 | dirPath: string; 18 | configPath: string; 19 | sourceDirPath: string; 20 | constructor(rootDir: string) { 21 | this.rootDir = rootDir; 22 | this.fsHelper = new FSHelper(); 23 | this.dirPath = `${this.rootDir}/.flint/processes`; 24 | this.configPath = `${this.rootDir}/.flint/processes/config.json`; 25 | this.sourceDirPath = `${this.rootDir}/src/controllers`; 26 | } 27 | 28 | checkAndCreateProcessDir = async () => { 29 | try { 30 | await this.fsHelper.checkPathExists(this.dirPath); 31 | } catch (e) { 32 | await this.fsHelper.createDirByPath(this.dirPath); 33 | } 34 | }; 35 | 36 | createProcess = async (processName: string) => { 37 | if (!await this.checkAndCreateProcessConfigFile()) return false; 38 | let configJson = await this.fetchConfigData(); 39 | configJson.processes.push(processName); 40 | await this.saveConfigData(configJson); 41 | }; 42 | 43 | deleteProcess = async (processName: string) => { 44 | let configJson = await this.fetchConfigData(); 45 | let {processes, editorDataMap, revision} = configJson; 46 | // remove from processes 47 | _.remove(processes, (process: string) => process === processName); 48 | // remove from editorDataMap 49 | editorDataMap = _.omit(editorDataMap, [processName]); 50 | revision = _.omit(revision, [processName]); 51 | _.set(configJson, ['editorDataMap'], editorDataMap); 52 | _.set(configJson, ['processes'], processes); 53 | _.set(configJson, ['revision'], revision); 54 | await this.saveConfigData(configJson); 55 | }; 56 | 57 | getProcessList = async () => { 58 | const processList: string[] = []; 59 | const configJson = await this.fetchConfigData(); 60 | if (!configJson.processes) return processList; 61 | return configJson.processes; 62 | }; 63 | 64 | getEditorData = async (processName: string) => { 65 | const configJson = await this.fetchConfigData(); 66 | return _.get(configJson, ['editorDataMap', processName]); 67 | }; 68 | 69 | getRevision = async (processName: string) => { 70 | const configJson = await this.fetchConfigData(); 71 | const revision = _.get(configJson, ['revision', processName]); 72 | const editor = !!revision?.editor ? revision.editor : 1; 73 | const source = !!revision?.source ? revision.source : 0; 74 | return {editor, source}; 75 | }; 76 | 77 | saveEditorData = async (processName: string, editorData: any) => { 78 | let configJson = await this.fetchConfigData(); 79 | _.set(configJson, ['editorDataMap', processName], editorData); 80 | let editorRevision = _.get(configJson, ['revision', processName, 'editor']); 81 | editorRevision = !!editorRevision ? editorRevision + 1 : 1; 82 | _.set(configJson, ['revision', processName, 'editor'], editorRevision); 83 | await this.saveConfigData(configJson); 84 | }; 85 | 86 | private checkAndCreateProcessConfigFile = async () => { 87 | try { 88 | await this.fsHelper.checkPathExists(this.configPath); 89 | return true; 90 | } catch (e) { 91 | await this.fsHelper.createFile(this.configPath, JSON.stringify(INITIAL_CONFIG)); 92 | return true; 93 | } 94 | }; 95 | 96 | private fetchConfigData = async () => { 97 | const data = await this.fsHelper.readFile(this.configPath); 98 | return JSON.parse(data); 99 | }; 100 | 101 | private saveConfigData = async (configJson: object) => { 102 | await this.fsHelper.createFile(this.configPath, JSON.stringify(configJson)); 103 | return true 104 | }; 105 | 106 | } -------------------------------------------------------------------------------- /src/controllers/process/templates/config-go.txt: -------------------------------------------------------------------------------- 1 | package workflows 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | workflowFramework "github.com/flintdev/workflow-engine/engine" 7 | ) 8 | 9 | func ParseConfig() workflowFramework.Config { 10 | var c workflowFramework.Config 11 | config := `{{{configJson}}}` 12 | err := json.Unmarshal([]byte(config), &c) 13 | if err != nil { 14 | fmt.Println(err.Error()) 15 | } 16 | return c 17 | } -------------------------------------------------------------------------------- /src/controllers/process/templates/definition-go.txt: -------------------------------------------------------------------------------- 1 | package {{package}} 2 | 3 | import ( 4 | "encoding/json" 5 | workflowFramework "github.com/flintdev/workflow-engine/engine" 6 | "go.uber.org/zap" 7 | ) 8 | 9 | func ParseDefinition() workflowFramework.Workflow { 10 | var w workflowFramework.Workflow 11 | definition := `{{{json}}}` 12 | err := json.Unmarshal([]byte(definition), &w) 13 | logger, _ := zap.NewProduction() 14 | defer logger.Sync() 15 | 16 | if err != nil { 17 | logger.Error(err.Error()) 18 | } 19 | 20 | return w 21 | } -------------------------------------------------------------------------------- /src/controllers/process/templates/executors/python/app-py.txt: -------------------------------------------------------------------------------- 1 | from flint import create_app 2 | from workflows import {{workflowNames}} 3 | 4 | workflows = { 5 | {{#workflows}} 6 | "{{name}}": { 7 | {{#steps}} 8 | "{{step}}": {{name}}.{{step}}.execute, 9 | {{/steps}} 10 | }, 11 | {{/workflows}} 12 | } 13 | 14 | app = create_app() 15 | app.register_workflows(workflows) 16 | 17 | if __name__ == "__main__": 18 | app.start() -------------------------------------------------------------------------------- /src/controllers/process/templates/executors/python/init-py.txt: -------------------------------------------------------------------------------- 1 | from . import {{stepNames}} 2 | 3 | __all__ = [ 4 | {{#steps}} 5 | '{{name}}', 6 | {{/steps}} 7 | ] -------------------------------------------------------------------------------- /src/controllers/process/templates/executors/python/manage-sh.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ $# -ne 1 ]]; then 4 | echo "Incorrect usage!" 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | opt=$1 10 | workflowEngineInstallerEP="https://raw.githubusercontent.com/flintdev/installer/master/pythonExecutor.sh" 11 | 12 | case $opt in 13 | "check") 14 | curl -s -S -L $workflowEngineInstallerEP | bash -s check 15 | ;; 16 | "install") 17 | curl -s -S -L $workflowEngineInstallerEP | bash -s install 18 | ;; 19 | "start") 20 | curl -s -S -L $workflowEngineInstallerEP | bash -s start 21 | ;; 22 | "upgrade") 23 | curl -s -S -L $workflowEngineInstallerEP | bash -s upgrade 24 | ;; 25 | *) 26 | echo "Incorrect usage!" 27 | echo "Usage: $0 " 28 | exit 1 29 | ;; 30 | esac -------------------------------------------------------------------------------- /src/controllers/process/templates/executors/python/requirements.txt: -------------------------------------------------------------------------------- 1 | cachetools==4.0.0 2 | certifi==2019.11.28 3 | chardet==3.0.4 4 | click==7.1.1 5 | Flask==1.1.1 6 | flint-python-executor==0.3.3 7 | flint-python-executor-handler==0.1.1 8 | google-auth==1.11.2 9 | idna==2.9 10 | itsdangerous==1.1.0 11 | Jinja2==2.11.1 12 | kubernetes==10.0.1 13 | MarkupSafe==1.1.1 14 | oauthlib==3.1.0 15 | pyasn1==0.4.8 16 | pyasn1-modules==0.2.8 17 | python-dateutil==2.8.1 18 | PyYAML==5.3 19 | requests==2.23.0 20 | requests-oauthlib==1.3.0 21 | rsa==4.0 22 | six==1.14.0 23 | urllib3==1.25.8 24 | websocket-client==0.57.0 25 | Werkzeug==1.0.0 -------------------------------------------------------------------------------- /src/controllers/process/templates/go-mod.txt: -------------------------------------------------------------------------------- 1 | module workflowEngine 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/flintdev/workflow-engine v0.3.4 7 | go.uber.org/zap v1.14.1 8 | ) -------------------------------------------------------------------------------- /src/controllers/process/templates/init-go.txt: -------------------------------------------------------------------------------- 1 | package {{package}} 2 | 3 | import ( 4 | workflowFramework "github.com/flintdev/workflow-engine/engine" 5 | ) 6 | 7 | func Definition() workflowFramework.Workflow { 8 | w := ParseDefinition() 9 | return w 10 | } 11 | -------------------------------------------------------------------------------- /src/controllers/process/templates/main-go.txt: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | workflowFramework "github.com/flintdev/workflow-engine/engine" 5 | "workflowEngine/workflows" 6 | {{#workflows}} 7 | {{name}} "workflowEngine/workflows/{{name}}" 8 | {{/workflows}} 9 | ) 10 | 11 | func main() { 12 | app := workflowFramework.CreateApp() 13 | {{#workflows}} 14 | app.RegisterWorkflow({{name}}.Definition) 15 | {{/workflows}} 16 | app.RegisterConfig(workflows.ParseConfig) 17 | app.Start() 18 | } -------------------------------------------------------------------------------- /src/controllers/process/templates/manage-sh.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ $# -ne 1 ]]; then 4 | echo "Incorrect usage!" 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | opt=$1 10 | workflowEngineInstallerEP="https://raw.githubusercontent.com/flintdev/installer/master/workflowEngine.sh" 11 | 12 | case $opt in 13 | "check") 14 | curl -s -S -L $workflowEngineInstallerEP | bash -s check 15 | ;; 16 | "install") 17 | curl -s -S -L $workflowEngineInstallerEP | bash -s install 18 | ;; 19 | "start") 20 | curl -s -S -L $workflowEngineInstallerEP | bash -s start 21 | ;; 22 | "upgrade") 23 | curl -s -S -L $workflowEngineInstallerEP | bash -s upgrade 24 | ;; 25 | *) 26 | echo "Incorrect usage!" 27 | echo "Usage: $0 " 28 | exit 1 29 | ;; 30 | esac -------------------------------------------------------------------------------- /src/controllers/project/projectManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/project/projectManager.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import {LocalStorageManager} from "../localStoreManager"; 5 | 6 | export class ProjectManager { 7 | rootDir: string; 8 | fsHelper: FSHelper; 9 | constructor(rootDir: string) { 10 | this.rootDir = rootDir; 11 | this.fsHelper = new FSHelper(); 12 | } 13 | 14 | createProjectDir = async () => { 15 | await this.fsHelper.createDirByPath(this.rootDir); 16 | }; 17 | 18 | initializeProjectFiles = async () => { 19 | const configDirPath = `${this.rootDir}/.flint`; 20 | await this.fsHelper.createDirByPath(configDirPath); 21 | return true; 22 | }; 23 | 24 | static UpdateRecentProjects = (projectDir: string) => { 25 | const projectDirs = new LocalStorageManager().getRecentProjects(); 26 | let newProjectDirs = [projectDir]; 27 | projectDirs.forEach(dir => { 28 | if (dir !== projectDir) newProjectDirs.push(dir); 29 | }); 30 | new LocalStorageManager().setRecentProjects(newProjectDirs); 31 | }; 32 | 33 | static GetRecentProjects = () => { 34 | const getProjectNameByPath = (projectDir: string) => { 35 | const tempList = projectDir.split('/'); 36 | return tempList[tempList.length - 1]; 37 | }; 38 | const projectDirs = new LocalStorageManager().getRecentProjects(); 39 | return projectDirs.map(dir => { 40 | return { 41 | name: getProjectNameByPath(dir), 42 | path: dir, 43 | } 44 | }); 45 | }; 46 | 47 | static RemoveRecentProject = (projectDir: string) => { 48 | let projectDirs = new LocalStorageManager().getRecentProjects(); 49 | const index = projectDirs.indexOf(projectDir); 50 | projectDirs.splice(index, 1); 51 | new LocalStorageManager().setRecentProjects(projectDirs); 52 | }; 53 | 54 | getProjectName = () => { 55 | const tempList = this.rootDir.split('/'); 56 | return tempList[tempList.length - 1]; 57 | }; 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/controllers/runtime/sourceFileGenerator.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/runtime/sourceFileGenerator.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import DockerComposeYaml from './templates/docker-compose-yaml.txt'; 5 | import * as Mustache from "mustache"; 6 | import * as _ from 'lodash'; 7 | 8 | interface File { 9 | path: string, 10 | content: string, 11 | } 12 | 13 | export class SourceFileGenerator { 14 | rootDir: string; 15 | fsHelper: FSHelper; 16 | constructor(rootDir: string) { 17 | this.rootDir = rootDir; 18 | this.fsHelper = new FSHelper(); 19 | } 20 | 21 | generate = async () => { 22 | await this.removeFiles(); 23 | await this.generateDockerComposeFile(); 24 | }; 25 | 26 | private removeFiles = async () => { 27 | const dirs = [ 28 | `${this.rootDir}/docker-compose.yaml`, 29 | ]; 30 | for (const dir of dirs) { 31 | await this.fsHelper.removeDir(dir) 32 | } 33 | }; 34 | 35 | private generateDockerComposeFile = async () => { 36 | const files: File[] = [ 37 | { 38 | path: `${this.rootDir}/docker-compose.yaml`, 39 | content: DockerComposeYaml 40 | } 41 | ]; 42 | await this.batchToCreateFiles(files); 43 | }; 44 | 45 | private batchToCreateFiles = async (files: File[]) => { 46 | for (const file of files) { 47 | const {path, content} = file; 48 | await this.fsHelper.createFile(path, content); 49 | } 50 | }; 51 | } -------------------------------------------------------------------------------- /src/controllers/runtime/templates/docker-compose-yaml.txt: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | kind: 5 | image: flintdev/flint-kind-dind 6 | privileged: true 7 | command: bash -c "while true; do echo hello; sleep 2; done" 8 | volumes: 9 | - root:/root 10 | expose: 11 | - 8000 12 | networks: 13 | - docker_network 14 | 15 | ui-install-build: 16 | image: flintdev/flint-ui 17 | command: bash -c "npm install && npm run build-dev" 18 | working_dir: /application/src/ui 19 | networks: 20 | - docker_network 21 | volumes: 22 | - ./:/application 23 | - ~/.kube/config:/root/.kube/config 24 | 25 | ui: 26 | image: flintdev/flint-ui 27 | command: ./wait-for-ui-install-builder.sh -- node /application/dist/server.js 28 | working_dir: /app 29 | networks: 30 | - docker_network 31 | volumes: 32 | - ./:/application 33 | - ~/.kube/config:/root/.kube/config 34 | depends_on: 35 | - ui-install-build 36 | ports: 37 | - 9090:8000 38 | 39 | workflow-engine: 40 | image: flintdev/flint-workflow-engine 41 | command: bash -c "go get github.com/githubnemo/CompileDaemon && CompileDaemon -directory=/application/src/controllers/workflowEngine -build='go build main.go' -command='./main'" 42 | working_dir: /application/src/controllers/workflowEngine 43 | networks: 44 | - docker_network 45 | volumes: 46 | - ./:/application 47 | - ~/.kube/config:/root/.kube/config 48 | 49 | python-executor: 50 | image: flintdev/flint-python-executor 51 | command: python app.py 52 | working_dir: /application/src/controllers/executors 53 | environment: 54 | DEBUG: "true" 55 | networks: 56 | - docker_network 57 | expose: 58 | - 8080 59 | volumes: 60 | - ./:/application 61 | - root:/root 62 | 63 | admin-service: 64 | image: flintdev/flint-admin-service 65 | command: python app.py 66 | working_dir: /application/src/controllers/admin 67 | environment: 68 | DEBUG: "true" 69 | networks: 70 | - docker_network 71 | expose: 72 | - 8080 73 | volumes: 74 | - ./:/application 75 | - root:/root 76 | 77 | health-check: 78 | image: flintdev/flint-service-health-check 79 | volumes: 80 | - ./:/application 81 | networks: 82 | - docker_network 83 | working_dir: /application 84 | command: python /app/watcher.py 85 | 86 | volumes: 87 | root: 88 | 89 | networks: 90 | docker_network: 91 | driver: bridge -------------------------------------------------------------------------------- /src/controllers/ui/templates/action-index-js.txt: -------------------------------------------------------------------------------- 1 | import {action} from "./action"; 2 | import {actionAdapter} from "@flintdev/action-kit"; 3 | import {reduxActionMap} from "../../redux/actions"; 4 | 5 | export const {{name}} = actionAdapter(action, reduxActionMap); 6 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/actions-root-index-js.txt: -------------------------------------------------------------------------------- 1 | {{#actions}} 2 | export { {{name}} } from './{{name}}'; 3 | {{/actions}} -------------------------------------------------------------------------------- /src/controllers/ui/templates/babelrc.txt: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties", 8 | "@babel/plugin-transform-spread" 9 | ] 10 | } -------------------------------------------------------------------------------- /src/controllers/ui/templates/component-with-children.txt: -------------------------------------------------------------------------------- 1 | <{{{name}}} 2 | {{#kvList}} 3 | {{{kv}}} 4 | {{/kvList}} 5 | > 6 | {{{children}}} 7 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/component-without-children.txt: -------------------------------------------------------------------------------- 1 | <{{{name}}} 2 | {{#kvList}} 3 | {{{kv}}} 4 | {{/kvList}} 5 | /> -------------------------------------------------------------------------------- /src/controllers/ui/templates/index-html.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{projectName}} 6 | {{#dependencies}} 7 | 8 | {{/dependencies}} 9 | 10 | 11 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/index-jsx.txt: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {render} from 'react-dom'; 3 | import {Provider} from 'react-redux'; 4 | import {store} from "./redux/store"; 5 | import Root from "./components/Root"; 6 | 7 | render( 8 | 9 | 10 | , 11 | document.getElementById('content') 12 | ); 13 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/npmrc.txt: -------------------------------------------------------------------------------- 1 | @flintdev:registry=https://npm.pkg.github.com/flintdev -------------------------------------------------------------------------------- /src/controllers/ui/templates/package-json.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{projectName}}", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "test", 8 | "start": "webpack-dev-server --progress --color --mode development --config webpack.config.js", 9 | "build-dev": "webpack --config webpack.config.js -w", 10 | "build-prod": "webpack --config webpack.config.js --production-only", 11 | "update-all": "npm install @flintdev/material-widgets@latest @flintdev/action-kit@latest --save" 12 | }, 13 | "devDependencies": { 14 | "@babel/cli": "^7.8.4", 15 | "@babel/core": "^7.8.7", 16 | "@babel/plugin-proposal-class-properties": "^7.8.3", 17 | "@babel/plugin-transform-modules-commonjs": "^7.8.3", 18 | "@babel/plugin-transform-runtime": "^7.8.3", 19 | "@babel/plugin-transform-spread": "^7.8.3", 20 | "@babel/preset-env": "^7.8.7", 21 | "@babel/preset-react": "^7.8.3", 22 | "@babel/runtime": "^7.8.7", 23 | "babel-jest": "^25.1.0", 24 | "babel-loader": "^8.0.6", 25 | "css-loader": "^3.4.0", 26 | "file-loader": "^5.1.0", 27 | "fsevents": "^2.1.2", 28 | "html-webpack-plugin": "^3.2.0", 29 | "jest": "^24.9.0", 30 | "raw-loader": "^4.0.0", 31 | "rimraf": "^3.0.0", 32 | "style-loader": "^1.1.2", 33 | "webpack": "^4.41.5", 34 | "webpack-cli": "^3.3.10", 35 | "webpack-dev-server": "^3.10.3" 36 | }, 37 | "publishConfig": { 38 | "registry": "https://npm.pkg.github.com/" 39 | }, 40 | "dependencies": { 41 | {{#packages}} 42 | "@flintdev/{{{name}}}": "latest", 43 | {{/packages}} 44 | {{#libraries}} 45 | "{{{name}}}": "{{{version}}}", 46 | {{/libraries}} 47 | "@flintdev/action-kit": "latest", 48 | "@flintdev/node-server": "latest", 49 | "@material-ui/core": "^4.9.7", 50 | "@material-ui/icons": "^4.9.1", 51 | "@material-ui/lab": "^4.0.0-alpha.46", 52 | "react": "^16.12.0", 53 | "react-dom": "^16.12.0", 54 | "react-redux": "^7.1.3", 55 | "redux": "^4.0.5", 56 | "redux-thunk": "^2.3.0", 57 | "lodash": "^4.17.15", 58 | "immutability-helper": "^3.0.1", 59 | "js-yaml": "^3.13.1" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/react-code.txt: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {connect} from 'react-redux'; 3 | // widgets 4 | {{#widgets}} 5 | import { {{{componentName}}} as {{{elementName}}} } from "@flintdev/{{{pluginId}}}"; 6 | {{/widgets}} 7 | // actions 8 | import * as actions from '../actions'; 9 | 10 | const RootStyle = {height: '100%'}; 11 | 12 | class Root extends React.Component { 13 | render() { 14 | const {state} = this.props; 15 | return ( 16 |
17 | {{{code}}} 18 |
19 | ) 20 | } 21 | } 22 | 23 | const mapStateToProps = (state) => { 24 | return {state}; 25 | }; 26 | 27 | const mapDispatchToProps = (dispatch) => { 28 | return { 29 | {{#actions}} 30 | {{name}}: (args) => dispatch(actions.{{name}}(args)), 31 | {{/actions}} 32 | } 33 | }; 34 | 35 | export default connect(mapStateToProps, mapDispatchToProps)(Root); 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/redux-actions-js.txt: -------------------------------------------------------------------------------- 1 | import * as types from './types'; 2 | 3 | {{#updaters}} 4 | function {{actionName}} (args) { 5 | return { 6 | type: types.{{name}}, 7 | args 8 | } 9 | } 10 | {{/updaters}} 11 | 12 | export const reduxActionMap = { 13 | {{#updaters}} 14 | [types.{{name}}]: {{actionName}}, 15 | {{/updaters}} 16 | }; 17 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/redux-reducer-js.txt: -------------------------------------------------------------------------------- 1 | import * as types from './types'; 2 | import update from 'immutability-helper'; 3 | 4 | export function reducer(state, action) { 5 | switch (action.type) { 6 | {{#updaters}} 7 | case types.{{name}}: 8 | return update(state, {{{data}}}); 9 | {{/updaters}} 10 | default: 11 | return state; 12 | } 13 | } -------------------------------------------------------------------------------- /src/controllers/ui/templates/redux-store-js.txt: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, createStore} from "redux"; 2 | import {initialState} from "./state"; 3 | import {reducer} from "./reducer"; 4 | import thunk from 'redux-thunk'; 5 | 6 | export const store = createStore( 7 | reducer, 8 | initialState, 9 | applyMiddleware(thunk) 10 | ); 11 | 12 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/redux-types-js.txt: -------------------------------------------------------------------------------- 1 | {{#updaters}} 2 | export const {{name}} = '{{name}}'; 3 | {{/updaters}} 4 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/repeat-component.txt: -------------------------------------------------------------------------------- 1 | {<%{path}%>.map((item, index) => { 2 | return ( 3 | <%{code}%> 4 | ) 5 | })} -------------------------------------------------------------------------------- /src/controllers/ui/templates/server-js.txt: -------------------------------------------------------------------------------- 1 | const {server} = require('@flintdev/node-server'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | 5 | const filePath = path.join(__dirname + '/index.html'); 6 | const html = fs.readFileSync(filePath).toString(); 7 | 8 | server.init({ 9 | port: 8000, 10 | host: '0.0.0.0', 11 | html: html, 12 | staticFiles: { 13 | url: '/index.js', 14 | path: path.join(__dirname + '/index.js') 15 | } 16 | }); 17 | 18 | server.start(); 19 | -------------------------------------------------------------------------------- /src/controllers/ui/templates/webpack-config.txt: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebPackPlugin = require("html-webpack-plugin"); 3 | 4 | const commonConfig = { 5 | context: __dirname, 6 | mode: 'development', 7 | devtool: 'inline-source-map', 8 | output: { 9 | path: path.resolve('../../dist/'), 10 | filename: "[name].js", 11 | }, 12 | resolve: { 13 | extensions: [".js", ".jsx"], 14 | alias: { 15 | ui: path.resolve('./ui'), 16 | }, 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(js|jsx)?$/, 22 | exclude: /node_modules/, 23 | loader: 'babel-loader', 24 | query: { 25 | //specify that we will be dealing with React code 26 | presets: [ 27 | "@babel/preset-env", 28 | "@babel/preset-react" 29 | ], 30 | plugins: [ 31 | "@babel/plugin-proposal-class-properties", 32 | "@babel/plugin-transform-spread", 33 | "@babel/plugin-transform-modules-commonjs", 34 | ["@babel/plugin-transform-runtime", 35 | { 36 | "regenerator": true 37 | } 38 | ] 39 | ] 40 | }, 41 | }, 42 | { 43 | test: /\.css$/, 44 | use: ['style-loader', 'css-loader'], 45 | }, 46 | { 47 | test: /\.(txt|yaml)$/, 48 | use: 'raw-loader', 49 | }, 50 | ], 51 | }, 52 | plugins: [ 53 | new HtmlWebPackPlugin({ 54 | template: "./index.html", 55 | filename: "./index.html" 56 | }) 57 | ], 58 | stats: { 59 | errorDetails: true, 60 | errors: true, 61 | source: true, 62 | } 63 | }; 64 | 65 | module.exports = env => { 66 | return [ 67 | { 68 | ...commonConfig, 69 | entry: { 70 | index: "./index.jsx", 71 | }, 72 | target: 'web', 73 | }, 74 | { 75 | ...commonConfig, 76 | entry: { 77 | server: "./server.js" 78 | }, 79 | target: 'node', 80 | node: { 81 | __filename: false, 82 | __dirname: false 83 | }, 84 | } 85 | ] 86 | }; -------------------------------------------------------------------------------- /src/controllers/ui/uiActionHandler.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/ui/uiActionHandler.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | 5 | const TEMP_ACTION_FILE_PATH = '/tmp/flint-action.js' 6 | 7 | export class UIActionHandler { 8 | fsHelper: FSHelper = new FSHelper(); 9 | writeToTempActionFile = async (code: string) => { 10 | await this.fsHelper.createFile(TEMP_ACTION_FILE_PATH, code); 11 | }; 12 | 13 | readTempActionFile = async () => { 14 | const code = await this.fsHelper.readFile(TEMP_ACTION_FILE_PATH); 15 | return code; 16 | }; 17 | 18 | getVSCodeURL = () => { 19 | return `vscode://file${TEMP_ACTION_FILE_PATH}`; 20 | }; 21 | } -------------------------------------------------------------------------------- /src/controllers/ui/uiDataManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/ui/uiDataManager.ts 2 | 3 | import {FSHelper} from "../utils/fsHelper"; 4 | import * as _ from 'lodash'; 5 | import { 6 | ActionData, 7 | ComponentData, 8 | PerspectiveData, 9 | SettingsData, 10 | StateUpdaterData 11 | } from "@flintdev/ui-editor/dist/interface"; 12 | import {UI_DATA_SCHEMA_VERSION} from "../../constants/data"; 13 | 14 | const INITIAL_CONFIG = {}; 15 | 16 | export interface UIData { 17 | actions: ActionData[], 18 | stateUpdaters: StateUpdaterData[], 19 | schemaEditorData: any, // the editor data to define data structure of global state. 20 | initialState: string, 21 | components: ComponentData[], 22 | settings: SettingsData, 23 | perspectives: PerspectiveData[], 24 | } 25 | 26 | export class UIDataManager { 27 | rootDir: string; 28 | fsHelper: FSHelper; 29 | dirPath: string; 30 | configPath: string; 31 | sourceDirPath: string; 32 | plugins: string[]; // for recurToGetPlugins function only 33 | constructor(rootDir: string) { 34 | this.rootDir = rootDir; 35 | this.fsHelper = new FSHelper(); 36 | this.dirPath = `${this.rootDir}/.flint/ui`; 37 | this.configPath = `${this.rootDir}/.flint/ui/config.json`; 38 | this.sourceDirPath = `${this.rootDir}/src/ui`; 39 | this.plugins = []; 40 | } 41 | 42 | checkAndCreateUIDir = async () => { 43 | try { 44 | await this.fsHelper.checkPathExists(this.dirPath); 45 | } catch (e) { 46 | await this.fsHelper.createDirByPath(this.dirPath); 47 | } 48 | await this.checkAndCreateUIConfigFile(); 49 | }; 50 | 51 | getUIData = async () => { 52 | return await this.getEditorData(); 53 | }; 54 | 55 | saveUIData = async (data: UIData) => { 56 | await this.saveEditorData(data); 57 | }; 58 | 59 | private getEditorData = async () => { 60 | const configJson = await this.fetchConfigData(); 61 | return _.get(configJson, ['editorDataMap', 'default']); 62 | }; 63 | 64 | private saveEditorData = async (editorData: any) => { 65 | let configJson = await this.fetchConfigData(); 66 | _.set(configJson, ['editorDataMap', 'default'], editorData); 67 | await this.saveConfigData(configJson); 68 | }; 69 | 70 | private checkAndCreateUIConfigFile = async () => { 71 | try { 72 | await this.fsHelper.checkPathExists(this.configPath); 73 | return true; 74 | } catch (e) { 75 | await this.fsHelper.createFile(this.configPath, JSON.stringify(INITIAL_CONFIG)); 76 | return true; 77 | } 78 | }; 79 | 80 | fetchConfigData = async () => { 81 | try { 82 | const data = await this.fsHelper.readFile(this.configPath); 83 | return JSON.parse(data); 84 | } catch (err) { 85 | return null; 86 | } 87 | }; 88 | 89 | saveConfigData = async (configJson: object) => { 90 | _.set(configJson, ['schemaVersion'], UI_DATA_SCHEMA_VERSION); 91 | await this.fsHelper.createFile(this.configPath, JSON.stringify(configJson)); 92 | return true 93 | }; 94 | 95 | getDependentPlugins = async () => { 96 | try { 97 | const uiData: UIData = await this.getUIData(); 98 | const components = uiData.components; 99 | this.plugins = []; 100 | this.recurToGetPlugins(components); 101 | return this.plugins; 102 | } catch (e) { 103 | return []; 104 | } 105 | }; 106 | 107 | private recurToGetPlugins = (children?: ComponentData[]) => { 108 | if (!children) return; 109 | children.forEach(component => { 110 | const pluginId = component.name.split('::')[0]; 111 | if (!this.plugins.includes(pluginId)) { 112 | this.plugins.push(pluginId); 113 | } 114 | this.recurToGetPlugins(component.children); 115 | }) 116 | }; 117 | 118 | 119 | } -------------------------------------------------------------------------------- /src/controllers/ui/widgetLibraryWrapper.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/ui/widgetLibraryWrapper.ts 2 | 3 | export function getWidget(name: string, props: any) { 4 | const tempList: any = name.split('::'); 5 | const widgetName = tempList[1]; 6 | const pluginId = tempList[0]; 7 | const library: any = window[pluginId] 8 | const getWidgetFunc = library['getWidget']; 9 | return getWidgetFunc(widgetName, props); 10 | } 11 | 12 | export function getWidgetConfiguration(name: string) { 13 | const tempList: any = name.split('::'); 14 | const widgetName = tempList[1]; 15 | const pluginId = tempList[0]; 16 | const library: any = window[pluginId] 17 | const getWidgetConfigurationFunc = library['getWidgetConfiguration']; 18 | return getWidgetConfigurationFunc(widgetName); 19 | } 20 | 21 | export function getWidgetInfo(pluginId: string) { 22 | const libraryName: any = pluginId; 23 | const library: any = window[libraryName] 24 | return library['widgetInfo']; 25 | } 26 | -------------------------------------------------------------------------------- /src/controllers/ui/widgetManager.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/ui/widgetManager.ts 2 | import {ComponentData} from "@flintdev/ui-editor/dist/interface"; 3 | import * as _ from 'lodash'; 4 | import {Param} from "@flintdev/ui-editor/dist/components/ParamFormGenerator/interface"; 5 | import {getWidgetInfo, getWidgetConfiguration} from "./widgetLibraryWrapper"; 6 | 7 | export class WidgetManager { 8 | constructor() { 9 | 10 | } 11 | 12 | getWidgetNameList = (pluginId: string) => { 13 | if (!pluginId || pluginId === "") return []; 14 | const widgetInfo = getWidgetInfo(pluginId); 15 | return Object.keys(widgetInfo).sort().map(name => `${pluginId}::${name}`); 16 | }; 17 | 18 | getWidgetData = (name: string): ComponentData => { 19 | const config = getWidgetConfiguration(name); 20 | // @ts-ignore 21 | const {params, canvas} = config; 22 | let values: any = {}; 23 | params.forEach((param: Param) => { 24 | param.items.forEach(item => { 25 | const {key, defaultValue} = item; 26 | values[key] = defaultValue; 27 | }) 28 | }); 29 | return { 30 | id: this.generateUniqueId(), 31 | name, 32 | params: values, 33 | overlay: !!canvas && !!canvas.overlay, 34 | children: [], 35 | canvas 36 | } 37 | }; 38 | 39 | private generateUniqueId = () => { 40 | const timestamp = (new Date()).getTime().toString(36); 41 | return `${timestamp}${_.uniqueId()}`; 42 | }; 43 | } -------------------------------------------------------------------------------- /src/controllers/utils/datetimeHelper.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/utils/datetimeHelper.ts 2 | 3 | import * as moment from "moment"; 4 | 5 | export class DatetimeHelper { 6 | 7 | getCurrentTimestamp = () => { 8 | return Math.floor(Date.now()).toString(); 9 | }; 10 | 11 | getDatetimeString = (timestamp: string) => { 12 | const date = moment(parseInt(timestamp)); 13 | return date.format('LLLL'); 14 | }; 15 | } -------------------------------------------------------------------------------- /src/controllers/utils/fsHelper.ts: -------------------------------------------------------------------------------- 1 | // controllers/utils/fsHelper.ts 2 | 3 | import ErrnoException = NodeJS.ErrnoException; 4 | import {Dirent} from "fs"; 5 | 6 | import * as fs from "fs"; 7 | import {homedir as homedirFunc} from "os"; 8 | import * as rimraf from "rimraf"; 9 | const homedir = homedirFunc(); 10 | 11 | const FILES_IGNORE = [ 12 | '.DS_Store' 13 | ]; 14 | 15 | export enum ErrorType { 16 | DirExists, 17 | DirNotExists, 18 | } 19 | 20 | export interface FileInfo { 21 | name: string, 22 | type: 'file' | 'dir', 23 | } 24 | 25 | export type ReadDirResolve = (files: FileInfo[]) => void; 26 | export type ReadFileResolve = (value: string) => void; 27 | 28 | export class FSHelper { 29 | createDirByPath = (path: string) => { 30 | return new Promise((resolve, reject) => { 31 | if (fs.existsSync(path)) { 32 | resolve(); 33 | } else { 34 | fs.mkdirSync(path, {recursive: true}); 35 | resolve(); 36 | } 37 | }); 38 | }; 39 | 40 | createFile = (path: string, content: string) => { 41 | return new Promise((resolve, reject) => { 42 | fs.writeFile(path, content, (err: ErrnoException) => { 43 | if (err) return reject(err); 44 | resolve(); 45 | }); 46 | }); 47 | }; 48 | 49 | readDir = (path: string) => { 50 | return new Promise((resolve: ReadDirResolve, reject) => { 51 | fs.readdir(path, {withFileTypes: true}, (err:ErrnoException, files:Dirent[]) => { 52 | if (!!err) return reject(err); 53 | const fileInfoList: FileInfo[] = files.filter(file => !FILES_IGNORE.includes(file.name)).map(file => { 54 | return { 55 | name: file.name, 56 | type: file.isDirectory() ? 'dir' : 'file' 57 | } 58 | }); 59 | resolve(fileInfoList); 60 | }) 61 | }); 62 | }; 63 | 64 | readDirSync = (path: string) => { 65 | const files = fs.readdirSync(path, {withFileTypes: true}); 66 | const fileInfoList: FileInfo[] = files.filter(file => !FILES_IGNORE.includes(file.name)).map(file => { 67 | return { 68 | name: file.name, 69 | type: file.isDirectory() ? 'dir' : 'file' 70 | } 71 | }); 72 | return fileInfoList; 73 | }; 74 | 75 | readFile = (path: string) => { 76 | return new Promise((resolve: ReadFileResolve, reject) => { 77 | fs.readFile(path, 'utf8', (err: ErrnoException, data: Buffer) => { 78 | if (err) return reject(err); 79 | resolve(data.toString()); 80 | }); 81 | }); 82 | }; 83 | 84 | checkPathExists = (path: string) => { 85 | return new Promise((resolve, reject) => { 86 | fs.stat(path, (err: object, stats: object) => { 87 | if (!err) resolve(); 88 | else reject(ErrorType.DirNotExists); 89 | }); 90 | }); 91 | }; 92 | 93 | removeDir = (path: string) => { 94 | return new Promise((resolve, reject) => { 95 | rimraf(path, {}, (err: Error) => { 96 | if (!err) { 97 | resolve(); 98 | } else { 99 | reject(); 100 | } 101 | }); 102 | }); 103 | }; 104 | 105 | getDefaultPath = () => { 106 | return `${homedir}/Flint/untitled`; 107 | }; 108 | 109 | checkAndCreateDirWithWriteAccess = (dirPath: string) => { 110 | return new Promise((resolve, reject) => { 111 | fs.access(dirPath, fs.constants.W_OK, err => { 112 | if (!!err) { 113 | fs.mkdir(dirPath, {recursive: true}, err1 => { 114 | if (!!err1) reject(err1) 115 | else resolve(); 116 | }) 117 | } else resolve(); 118 | }); 119 | }); 120 | }; 121 | 122 | getHomeDir = () => { 123 | return homedir; 124 | }; 125 | 126 | getFileTree = (rootDirPath: string) => { 127 | 128 | }; 129 | } -------------------------------------------------------------------------------- /src/controllers/utils/githubHelper.ts: -------------------------------------------------------------------------------- 1 | // src/controllers/utils/githubHelper.ts 2 | 3 | import fetch from 'node-fetch'; 4 | import * as Bluebird from 'bluebird'; 5 | // @ts-ignore 6 | fetch.Promise = Bluebird; 7 | const API_BASE_URL = `https://api.github.com`; 8 | const SITE_BASE_URL = 'https://github.com'; 9 | const atob = require('atob'); 10 | 11 | export class GithubHelper { 12 | headers: object = { 13 | 'User-Agent': 'node.js', 14 | 'Content-Type': 'application/json' 15 | }; 16 | constructor() { 17 | 18 | } 19 | 20 | getLatestRelease = async (owner: string, repo: string) => { 21 | const url = `${API_BASE_URL}/repos/${owner}/${repo}/releases/latest`; 22 | // @ts-ignore 23 | const response = await fetch(url, { 24 | headers: {...this.headers}, 25 | method: "GET" 26 | }); 27 | return await response.json(); 28 | }; 29 | 30 | getAssetDownloadURL = (owner: string, repo: string, releaseInfo: any, filename: string) => { 31 | const tag = releaseInfo.tag_name; 32 | return `${SITE_BASE_URL}/${owner}/${repo}/releases/download/${tag}/${filename}`; 33 | }; 34 | 35 | getFileContent = async (owner: string, repo: string, path: string) => { 36 | const url = `${API_BASE_URL}/repos/${owner}/${repo}/contents/${path}`; 37 | const response = await fetch(url, { 38 | headers: {...this.headers}, 39 | method: "GET" 40 | }); 41 | const result = await response.json(); 42 | const contentEncode = result.content; 43 | return atob(contentEncode); 44 | }; 45 | } -------------------------------------------------------------------------------- /src/electron/utils/debugHelper.ts: -------------------------------------------------------------------------------- 1 | // src/electron/utils/debugHelper.ts 2 | 3 | import {BrowserWindow} from 'electron'; 4 | 5 | export class DebugHelper { 6 | debugWindow: BrowserWindow; 7 | constructor(debugWindow: BrowserWindow) { 8 | this.debugWindow = debugWindow; 9 | } 10 | 11 | loadLocalStorage = async (localStorageItems: any[]) => { 12 | for (let item of localStorageItems) { 13 | const {key, value} = item; 14 | await this.debugWindow.webContents.executeJavaScript(` 15 | localStorage.setItem('${key}', '${value}'); 16 | `); 17 | } 18 | }; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/electron/utils/dev-app-update.yml: -------------------------------------------------------------------------------- 1 | provider: github 2 | host: github.com 3 | owner: flintdev 4 | repo: flint -------------------------------------------------------------------------------- /src/electron/utils/gitHelper.ts: -------------------------------------------------------------------------------- 1 | // src/electron/utils/gitHelper.ts 2 | 3 | import * as git from 'isomorphic-git' 4 | import * as fs from "fs"; 5 | import {FSHelper} from "../../controllers/utils/fsHelper"; 6 | import {DatetimeHelper} from "../../controllers/utils/datetimeHelper"; 7 | import {exec} from "child_process"; 8 | 9 | export class GitHelper { 10 | repoDir: string; 11 | projectDir: string; 12 | fsHelper = new FSHelper(); 13 | datetimeHelper = new DatetimeHelper(); 14 | 15 | constructor(projectDir: string) { 16 | this.projectDir = projectDir; 17 | this.repoDir = `${projectDir}/.flint`; 18 | } 19 | 20 | checkGitFiles = async () => { 21 | const gitPath = `${this.repoDir}/.git`; 22 | try { 23 | await this.fsHelper.checkPathExists(gitPath) 24 | return true; 25 | } catch (e) { 26 | return false; 27 | } 28 | }; 29 | 30 | initRepo = async () => { 31 | await git.init({fs, dir: this.repoDir}); 32 | }; 33 | 34 | private addFiles = async () => { 35 | const repo = {fs, dir: this.repoDir}; 36 | await git.statusMatrix(repo).then((status) => 37 | Promise.all( 38 | status.map(([filepath, , worktreeStatus]) => 39 | // isomorphic-git may report a changed file as unmodified, so always add if not removing 40 | worktreeStatus ? git.add({...repo, filepath}) : git.remove({...repo, filepath}) 41 | ) 42 | ) 43 | ); 44 | }; 45 | 46 | commitWithTimestamp = async () => { 47 | const gitExist = await this.checkGitFiles(); 48 | if (!gitExist) await this.initRepo(); 49 | await this.addFiles(); 50 | await git.commit({ 51 | fs, 52 | dir: this.repoDir, 53 | message: this.datetimeHelper.getCurrentTimestamp(), 54 | author: { 55 | name: 'flint', 56 | }, 57 | }); 58 | }; 59 | 60 | listCommits = async () => { 61 | const commits = await git.log({ 62 | fs, 63 | dir: this.repoDir, 64 | }); 65 | return commits; 66 | }; 67 | 68 | reset = async (commitId: string) => { 69 | return new Promise((resolve, reject) => { 70 | exec(`git reset --hard ${commitId}`, {cwd: this.repoDir}, (error, stdout, stderr) => { 71 | if (!error) resolve(); 72 | else reject(error); 73 | }); 74 | }); 75 | }; 76 | } -------------------------------------------------------------------------------- /src/electron/utils/startDebugging.ts: -------------------------------------------------------------------------------- 1 | // src/electron/utils/startDebugging.ts 2 | 3 | import {exec} from 'child_process'; 4 | 5 | export function startDebugging (dir: string) { 6 | return new Promise((resolve, reject) => { 7 | exec("npm install", {cwd: dir}, ((error, stdout, stderr) => { 8 | console.log(stdout); 9 | if (!!error) { 10 | console.log('error - npm install', error); 11 | reject(error); 12 | return false; 13 | } 14 | exec("npm start", {cwd: dir}, (error1, stdout1, stderr1) => { 15 | console.log(stdout1); 16 | if (!!error1) { 17 | console.log('error - npm start', error1); 18 | reject(error1); 19 | return false; 20 | } 21 | resolve(true); 22 | }) 23 | })); 24 | }); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/electron/utils/starterMenuBuilder.ts: -------------------------------------------------------------------------------- 1 | // src/electron/utils/starterMenuBuilder.ts 2 | 3 | import {app, Menu, dialog, BrowserWindow, shell} from 'electron'; 4 | const isMac = process.platform === 'darwin'; 5 | 6 | export class StarterMenuBuilder { 7 | window: BrowserWindow; 8 | constructor(window: BrowserWindow) { 9 | this.window = window; 10 | } 11 | 12 | build = () => { 13 | this.initMenu(); 14 | }; 15 | 16 | private initMenu = () => { 17 | const menuTemplate: any = [ 18 | this.getEditMenu(), 19 | this.getViewMenu(), 20 | this.getWindowMenu(), 21 | this.getHelpMenu(), 22 | ]; 23 | const menu = Menu.buildFromTemplate(menuTemplate); 24 | Menu.setApplicationMenu(menu); 25 | }; 26 | 27 | private getEditMenu = () => { 28 | return { 29 | label: 'Edit', 30 | submenu: [ 31 | { role: 'undo' }, 32 | { role: 'redo' }, 33 | { type: 'separator' }, 34 | { role: 'cut' }, 35 | { role: 'copy' }, 36 | { role: 'paste' }, 37 | ...(isMac ? [ 38 | { role: 'pasteAndMatchStyle' }, 39 | { role: 'delete' }, 40 | { role: 'selectAll' }, 41 | { type: 'separator' }, 42 | { 43 | label: 'Speech', 44 | submenu: [ 45 | { role: 'startspeaking' }, 46 | { role: 'stopspeaking' } 47 | ] 48 | } 49 | ] : [ 50 | { role: 'delete' }, 51 | { type: 'separator' }, 52 | { role: 'selectAll' } 53 | ]) 54 | ] 55 | } 56 | }; 57 | 58 | private getViewMenu = () => { 59 | return { 60 | label: 'View', 61 | submenu: [ 62 | { role: 'reload' }, 63 | { role: 'forcereload' }, 64 | { role: 'toggledevtools' }, 65 | { type: 'separator' }, 66 | { role: 'resetzoom' }, 67 | { role: 'zoomin' }, 68 | { role: 'zoomout' }, 69 | { type: 'separator' }, 70 | { role: 'togglefullscreen' } 71 | ] 72 | } 73 | }; 74 | 75 | private getWindowMenu = () => { 76 | return { 77 | label: 'Window', 78 | submenu: [ 79 | { role: 'minimize' }, 80 | { role: 'zoom' }, 81 | ...(isMac ? [ 82 | { type: 'separator' }, 83 | { role: 'front' }, 84 | { type: 'separator' }, 85 | { role: 'window' } 86 | ] : [ 87 | { role: 'close' } 88 | ]) 89 | ] 90 | } 91 | }; 92 | 93 | private getHelpMenu = () => { 94 | return { 95 | role: 'help', 96 | submenu: [ 97 | { 98 | label: 'Learn More', 99 | click: async () => { 100 | await shell.openExternal('https://github.com/flintdev/flint') 101 | } 102 | }, 103 | { type: 'separator' }, 104 | { 105 | label: 'Open Dev Tools', 106 | click: () => { 107 | this.window.webContents.openDevTools(); 108 | } 109 | } 110 | ] 111 | } 112 | }; 113 | 114 | } -------------------------------------------------------------------------------- /src/electron/views/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Flint 7 | 8 | 9 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | {{#plugins}} 31 | 32 | {{/plugins}} 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/electron/views/starter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Flint 7 | 8 | 9 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/entries/Editor.tsx: -------------------------------------------------------------------------------- 1 | // entries - starterWindow 2 | 3 | import * as React from "react"; 4 | import {render} from "react-dom"; 5 | import {Provider} from 'react-redux'; 6 | import {store} from "src/redux/store"; 7 | import EditorContainer from "src/containers/editorWindow/EditorContainer"; 8 | import 'typeface-roboto'; 9 | import {MainProcessCommunicator} from "../controllers/mainProcessCommunicator"; 10 | import ErrorBoundary from "../components/ErrorBoundary"; 11 | import 'normalize.css'; 12 | 13 | render( 14 | 15 | 16 | 17 | 18 | , 19 | document.getElementById('rootContainer'), 20 | ); 21 | 22 | new MainProcessCommunicator().initGlobalListeners(); -------------------------------------------------------------------------------- /src/entries/Starter.tsx: -------------------------------------------------------------------------------- 1 | // entries - starterWindow 2 | 3 | import * as React from "react"; 4 | import {render} from "react-dom"; 5 | import {Provider} from 'react-redux'; 6 | import {store} from "src/redux/store"; 7 | import StarterContainer from "../containers/starterWindow/StarterContainer"; 8 | 9 | render( 10 | 11 | 12 | , 13 | document.getElementById('rootContainer'), 14 | ); 15 | 16 | -------------------------------------------------------------------------------- /src/interface.ts: -------------------------------------------------------------------------------- 1 | // src/interface.ts 2 | 3 | import {Output} from "./controllers/process/processDataHandler"; 4 | 5 | export interface FileTreeNode { 6 | path: string, 7 | name: string, 8 | type: 'file' | 'dir', 9 | children?: FileTreeNode[] 10 | } 11 | 12 | export interface PluginData { 13 | type: 'widget' | 'step', 14 | id: string, 15 | name: string, 16 | owner: string, 17 | repo: string, 18 | preinstalled?: boolean, 19 | currentVersion?: string, 20 | newVersion?: string, 21 | libraryName?: string, 22 | description?: string, 23 | } 24 | 25 | export type NotificationType = 'widget-update'; 26 | 27 | export interface Notification { 28 | type: NotificationType, 29 | title: string, 30 | subtitle: string, 31 | } 32 | 33 | export enum TriggerEventType { 34 | 'ADDED'= "ADDED", 35 | 'MODIFIED' = 'MODIFIED', 36 | 'DELETED' = 'DELETED' 37 | } 38 | 39 | export interface TriggerData { 40 | model: string, 41 | eventType: TriggerEventType, 42 | when: string 43 | } 44 | 45 | export interface ManualData { 46 | model: string, 47 | eventType: TriggerEventType, 48 | outputs: Output[], 49 | } -------------------------------------------------------------------------------- /src/migrations/migrationHandler.ts: -------------------------------------------------------------------------------- 1 | // src/migrations/migrationHandler.ts 2 | 3 | import {UIDataManager} from "../controllers/ui/uiDataManager"; 4 | import {revise as reviseUI} from './ui/revise'; 5 | 6 | export class MigrationHandler { 7 | uiDataManager: UIDataManager; 8 | constructor(projectDir: string) { 9 | this.uiDataManager = new UIDataManager(projectDir); 10 | } 11 | 12 | private migrateUIConfigData = async () => { 13 | let configData = await this.uiDataManager.fetchConfigData(); 14 | if (!configData) return; 15 | configData = await reviseUI(configData); 16 | if (!configData) return; 17 | await this.uiDataManager.saveConfigData(configData); 18 | }; 19 | 20 | migrate = async () => { 21 | await this.migrateUIConfigData(); 22 | }; 23 | } -------------------------------------------------------------------------------- /src/migrations/ui/revise.ts: -------------------------------------------------------------------------------- 1 | // src/migrations/ui/revise.ts 2 | 3 | import {revise as v2} from "./v2"; 4 | 5 | export async function revise(configData: any) { 6 | let {schemaVersion} = configData; 7 | schemaVersion = !!schemaVersion ? schemaVersion : 1; 8 | if (schemaVersion < 2) configData = await v2(configData); 9 | if (!configData) return; 10 | // add following versions 11 | return configData; 12 | } -------------------------------------------------------------------------------- /src/migrations/ui/v2.ts: -------------------------------------------------------------------------------- 1 | // src/migrations/ui/version_2.ts 2 | 3 | import * as _ from 'lodash'; 4 | import {UIData} from "../../controllers/ui/uiDataManager"; 5 | import {ComponentData} from "@flintdev/ui-editor/dist/interface"; 6 | 7 | const DefaultPluginId = 'material-widgets'; 8 | 9 | export async function revise(configJson: any) { 10 | if (!configJson) return; 11 | const uiData: UIData = _.get(configJson, ['editorDataMap', 'default']); 12 | if (!uiData) return; 13 | let {components} = uiData; 14 | if (!components) return; 15 | components = components.map(item => recurToUpdateComponent(item)); 16 | uiData.components = components; 17 | _.set(configJson, ['editorDataMap', 'default'], uiData); 18 | return configJson; 19 | } 20 | 21 | function recurToUpdateComponent(componentData: ComponentData): ComponentData { 22 | let {name, children} = componentData; 23 | name = `${DefaultPluginId}::${name}`; 24 | componentData.name = name; 25 | if (!!children && children.length > 0) { 26 | componentData.children = children.map((item: ComponentData) => recurToUpdateComponent(item)); 27 | } 28 | return componentData; 29 | } -------------------------------------------------------------------------------- /src/redux/modules/components/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/components/actions.ts 2 | 3 | import * as types from './types'; 4 | import { 5 | ConfirmationDialogSubmitFunc, 6 | DialogFormData, 7 | DialogFormSubmitFunc, 8 | ToastType 9 | } from "../../../components/interface"; 10 | 11 | // functions 12 | 13 | export function toastOpen(toastType: ToastType, message: string): ToastOpen { 14 | return { type: types.TOAST_OPEN, toastType, message } 15 | } 16 | 17 | export function toastClose(): ToastClose { 18 | return { type: types.TOAST_CLOSE } 19 | } 20 | 21 | export function openDialogForm( 22 | initValues: any, 23 | data: DialogFormData, 24 | onSubmit: DialogFormSubmitFunc 25 | ): OpenDialogForm { 26 | return { type: types.OPEN_DIALOG_FORM, initValues, data, onSubmit } 27 | } 28 | 29 | export function closeDialogForm(): CloseDialogForm { 30 | return { type: types.CLOSE_DIALOG_FORM } 31 | } 32 | 33 | export function openConfirmationDialog( 34 | messageType: string, 35 | title: string, 36 | description?: string, 37 | submitLabel?: string, 38 | onSubmit?: ConfirmationDialogSubmitFunc, 39 | ): OpenConfirmationDialog { 40 | return { type: types.OPEN_CONFIRMATION_DIALOG, messageType, title, description, submitLabel, onSubmit} 41 | } 42 | 43 | export function closeConfirmationDialog(): CloseConfirmationDialog { 44 | return { type: types.CLOSE_CONFIRMATION_DIALOG } 45 | } 46 | 47 | // interfaces 48 | 49 | export interface CloseConfirmationDialog { 50 | type: typeof types.CLOSE_CONFIRMATION_DIALOG, 51 | } 52 | 53 | export interface OpenConfirmationDialog { 54 | type: typeof types.OPEN_CONFIRMATION_DIALOG, 55 | messageType: string, 56 | title: string, 57 | submitLabel?: string, 58 | description?: string, 59 | onSubmit?: ConfirmationDialogSubmitFunc, 60 | } 61 | 62 | export interface ToastOpen { 63 | type: typeof types.TOAST_OPEN, 64 | toastType: ToastType, 65 | message: string 66 | } 67 | 68 | export interface ToastClose { 69 | type: typeof types.TOAST_CLOSE, 70 | } 71 | 72 | export interface OpenDialogForm { 73 | type: typeof types.OPEN_DIALOG_FORM, 74 | initValues: any, 75 | data: DialogFormData, 76 | onSubmit: DialogFormSubmitFunc 77 | } 78 | 79 | export interface CloseDialogForm { 80 | type: typeof types.CLOSE_DIALOG_FORM, 81 | } 82 | 83 | export type ComponentsAction = 84 | OpenConfirmationDialog | 85 | CloseConfirmationDialog | 86 | OpenDialogForm | 87 | CloseDialogForm | 88 | ToastOpen | 89 | ToastClose; -------------------------------------------------------------------------------- /src/redux/modules/components/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/components/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {ComponentsAction} from "./actions"; 6 | import {ComponentsState} from "../../state"; 7 | 8 | export function reducer(state: ComponentsState, action: ComponentsAction) { 9 | switch (action.type) { 10 | case types.TOAST_OPEN: 11 | return update(state, { 12 | toast: { 13 | open: {$set: true}, 14 | type: {$set: action.toastType}, 15 | message: {$set: action.message} 16 | } 17 | }); 18 | case types.TOAST_CLOSE: 19 | return update(state, { 20 | toast: { 21 | open: {$set: false} 22 | } 23 | }); 24 | case types.OPEN_DIALOG_FORM: 25 | return update(state, { 26 | dialogForm: { 27 | open: {$set: true}, 28 | initValues: {$set: action.initValues}, 29 | data: {$set: action.data}, 30 | onSubmit: {$set: action.onSubmit} 31 | } 32 | }); 33 | case types.CLOSE_DIALOG_FORM: 34 | return update(state, { 35 | dialogForm: { 36 | open: {$set: false} 37 | } 38 | }); 39 | case types.OPEN_CONFIRMATION_DIALOG: 40 | return update(state, { 41 | confirmationDialog: { 42 | open: {$set: true}, 43 | type: {$set: action.messageType}, 44 | title: {$set: action.title}, 45 | description: {$set: action.description}, 46 | submitLabel: {$set: action.submitLabel}, 47 | onSubmit: {$set: action.onSubmit}, 48 | } 49 | }); 50 | case types.CLOSE_CONFIRMATION_DIALOG: 51 | return update(state, { 52 | confirmationDialog: { 53 | open: {$set: false} 54 | } 55 | }); 56 | 57 | default: 58 | return state; 59 | } 60 | } 61 | 62 | export {ComponentsAction}; -------------------------------------------------------------------------------- /src/redux/modules/components/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/components/types.ts 2 | 3 | // toast 4 | export const TOAST_OPEN = 'components/TOAST_OPEN'; 5 | export const TOAST_CLOSE = 'components/TOAST_CLOSE'; 6 | // dialog form 7 | export const OPEN_DIALOG_FORM = 'components/OPEN_DIALOG_FORM'; 8 | export const CLOSE_DIALOG_FORM = 'components/CLOSE_DIALOG_FORM'; 9 | // confirmation dialog 10 | export const OPEN_CONFIRMATION_DIALOG = 'components/OPEN_CONFIRMATION_DIALOG'; 11 | export const CLOSE_CONFIRMATION_DIALOG = 'components/CLOSE_CONFIRMATION_DIALOG'; 12 | -------------------------------------------------------------------------------- /src/redux/modules/config/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/config/actions.ts 2 | 3 | import * as types from './types'; 4 | 5 | // functions 6 | 7 | export function setCurrentPage(pageIndex: number): SetCurrentPage { 8 | return { type: types.SET_CURRENT_PAGE, pageIndex } 9 | } 10 | 11 | export function setProjectDir(value: string): SetProjectDir { 12 | return { 13 | type: types.SET_PROJECT_DIR, 14 | value 15 | } 16 | } 17 | 18 | // interfaces 19 | 20 | export interface SetCurrentPage { 21 | type: typeof types.SET_CURRENT_PAGE, 22 | pageIndex: number 23 | } 24 | 25 | export interface SetProjectDir { 26 | type: typeof types.SET_PROJECT_DIR, 27 | value: string 28 | } 29 | 30 | export type ConfigAction = 31 | SetCurrentPage | 32 | SetProjectDir; 33 | -------------------------------------------------------------------------------- /src/redux/modules/config/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/config/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {ConfigAction} from "./actions"; 6 | 7 | export function reducer(state: object, action: ConfigAction) { 8 | switch (action.type) { 9 | case types.SET_CURRENT_PAGE: 10 | return update(state, { 11 | currentPageIndex: {$set: action.pageIndex} 12 | }); 13 | case types.SET_PROJECT_DIR: 14 | return update(state, { 15 | projectDir: {$set: action.value} 16 | }); 17 | default: 18 | return state; 19 | } 20 | } 21 | 22 | export {ConfigAction}; -------------------------------------------------------------------------------- /src/redux/modules/config/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/config/types.ts 2 | 3 | export const SET_PROJECT_DIR = 'editorWindow/SET_PROJECT_DIR'; 4 | export const SET_CURRENT_PAGE = 'editorWindow/SET_CURRENT_PAGE'; -------------------------------------------------------------------------------- /src/redux/modules/editor/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/actions.ts 2 | 3 | import * as modelEditor from './modelEditor/actions'; 4 | import * as processEditor from './processEditor/actions'; 5 | import * as uiEditor from './uiEditor/actions'; 6 | import * as navigation from './navigation/actions'; 7 | import * as settings from './settings/actions'; 8 | 9 | export type EditorAction = 10 | modelEditor.ModelEditorAction | 11 | processEditor.ProcessEditorAction | 12 | uiEditor.UIEditorAction | 13 | navigation.NavigationAction | 14 | settings.SettingsAction; 15 | 16 | 17 | export {modelEditor, processEditor, uiEditor, navigation, settings}; 18 | -------------------------------------------------------------------------------- /src/redux/modules/editor/modelEditor/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/actions/modelEditor/actions.ts 2 | 3 | import * as types from './types'; 4 | 5 | // functions 6 | 7 | export function setModelList(modelList: string[]): SetModelList { 8 | return { type: types.SET_MODEL_LIST, modelList } 9 | } 10 | 11 | export function selectModel(value: string): SelectModel { 12 | return { type: types.SELECT_MODEL, value } 13 | } 14 | 15 | export function setEditorData (editorData: any): SetEditorData { 16 | return { type: types.SET_EDITOR_DATA, editorData } 17 | } 18 | 19 | export function deleteModel (modelName: string): DeleteModel { 20 | return { type: types.DELETE_MODEL, modelName } 21 | } 22 | 23 | export function blockEditDialogOpen(blockData: any): BlockEditDialogOpen { 24 | return { type: types.BLOCK_EDIT_DIALOG_OPEN, blockData } 25 | } 26 | 27 | export function blockEditDialogClose(): BlockEditDialogClose { 28 | return { type: types.BLOCK_EDIT_DIALOG_CLOSE } 29 | } 30 | 31 | // interfaces 32 | 33 | export interface BlockEditDialogClose { 34 | type: typeof types.BLOCK_EDIT_DIALOG_CLOSE, 35 | } 36 | 37 | export interface BlockEditDialogOpen { 38 | type: typeof types.BLOCK_EDIT_DIALOG_OPEN, 39 | blockData: any, 40 | } 41 | 42 | export interface SetModelList { 43 | type: typeof types.SET_MODEL_LIST, 44 | modelList: string[] 45 | } 46 | 47 | export interface SelectModel { 48 | type: typeof types.SELECT_MODEL, 49 | value: string, 50 | } 51 | 52 | export interface SetEditorData { 53 | type: typeof types.SET_EDITOR_DATA, 54 | editorData: any, 55 | } 56 | 57 | export interface DeleteModel { 58 | type: typeof types.DELETE_MODEL, 59 | modelName: string 60 | } 61 | 62 | export type ModelEditorAction = 63 | BlockEditDialogOpen | 64 | BlockEditDialogClose | 65 | SetModelList | 66 | SelectModel | 67 | SetEditorData | 68 | DeleteModel ; -------------------------------------------------------------------------------- /src/redux/modules/editor/modelEditor/reducer.ts: -------------------------------------------------------------------------------- 1 | // 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {ModelEditorState} from "../../../state"; 6 | import {ModelEditorAction} from "./actions"; 7 | 8 | export function reducer(state: ModelEditorState, action: ModelEditorAction) { 9 | switch (action.type) { 10 | case types.SET_MODEL_LIST: 11 | return update(state, { 12 | modelList: {$set: action.modelList} 13 | }); 14 | case types.SELECT_MODEL: 15 | return update(state, { 16 | modelSelected: {$set: action.value} 17 | }); 18 | case types.SET_EDITOR_DATA: 19 | return update(state, { 20 | editorData: {$set: action.editorData}, 21 | _mark: {$set: state._mark + 1} 22 | }); 23 | case types.DELETE_MODEL: 24 | return update(state, { 25 | editorData: {$set: undefined}, 26 | modelSelected: {$set: undefined}, 27 | modelList: {$splice: [[state.modelList.indexOf(action.modelName), 1]]}, 28 | }); 29 | case types.BLOCK_EDIT_DIALOG_OPEN: 30 | return update(state, { 31 | blockEditDialog: { 32 | open: {$set: true}, 33 | blockData: {$set: action.blockData}, 34 | } 35 | }); 36 | case types.BLOCK_EDIT_DIALOG_CLOSE: 37 | return update(state, { 38 | blockEditDialog: { 39 | open: {$set: false} 40 | } 41 | }); 42 | 43 | default: 44 | return state; 45 | } 46 | } 47 | 48 | export {ModelEditorAction}; -------------------------------------------------------------------------------- /src/redux/modules/editor/modelEditor/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/modelEditor/types.ts 2 | 3 | export const SET_MODEL_LIST = 'editorWindow/modelEditor/SET_MODEL_LIST'; 4 | export const SELECT_MODEL = 'editorWindow/modelEditor/SELECT_MODEL'; 5 | export const SET_EDITOR_DATA = 'editorWindow/modelEditor/SET_EDITOR_DATA'; 6 | export const SET_DEFAULT_EDITOR_DATA = 'editorWindow/modelEditor/SET_DEFAULT_EDITOR_DATA'; 7 | export const SET_SCHEMA_DATA = 'editorWindow/modelEditor/SET_SCHEMA_DATA'; 8 | export const DELETE_MODEL = 'editorWindow/modelEditor/DELETE_MODEL'; 9 | export const SET_CURRENT_REVISION = 'editorWindow/modelEditor/SET_CURRENT_REVISION'; 10 | // 11 | export const BLOCK_EDIT_DIALOG_OPEN = 'editorWindow/modelEditor/BLOCK_EDIT_DIALOG_OPEN'; 12 | export const BLOCK_EDIT_DIALOG_CLOSE = 'editorWindow/modelEditor/BLOCK_EDIT_DIALOG_CLOSE'; 13 | // -------------------------------------------------------------------------------- /src/redux/modules/editor/navigation/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/navigation/actions.ts 2 | 3 | import * as types from './types'; 4 | import {Notification} from "../../../../interface"; 5 | 6 | // functions 7 | 8 | export function setCurrentView(value: string): SetCurrentView { 9 | return { type: types.SET_CURRENT_VIEW, value } 10 | } 11 | 12 | export function notificationPopoverOpen(anchorEl: HTMLButtonElement): NotificationPopoverOpen { 13 | return { type: types.NOTIFICATION_POPOVER_OPEN, anchorEl } 14 | } 15 | 16 | export function notificationPopoverClose(): NotificationPopoverClose { 17 | return { type: types.NOTIFICATION_POPOVER_CLOSE } 18 | } 19 | 20 | export function addNotification(notification: Notification): AddNotification { 21 | return { type: types.ADD_NOTIFICATION, notification } 22 | } 23 | 24 | export function resetNotifications(): ResetNotifications { 25 | return { type: types.RESET_NOTIFICATIONS } 26 | } 27 | 28 | export function widgetUpdateDialogOpen(): WidgetUpdateDialogOpen { 29 | return { type: types.WIDGET_UPDATE_DIALOG_OPEN } 30 | } 31 | 32 | export function widgetUpdateDialogClose(): WidgetUpdateDialogClose { 33 | return { type: types.WIDGET_UPDATE_DIALOG_CLOSE } 34 | } 35 | 36 | export function revisionPopoverOpen(anchorEl: HTMLButtonElement): RevisionPopoverOpen { 37 | return { type: types.REVISION_POPOVER_OPEN, anchorEl } 38 | } 39 | 40 | export function revisionPopoverClose(): RevisionPopoverClose { 41 | return { type: types.REVISION_POPOVER_CLOSE } 42 | } 43 | 44 | // interfaces 45 | 46 | export interface RevisionPopoverOpen { 47 | type: typeof types.REVISION_POPOVER_OPEN, 48 | anchorEl: HTMLButtonElement 49 | } 50 | 51 | export interface RevisionPopoverClose { 52 | type: typeof types.REVISION_POPOVER_CLOSE, 53 | } 54 | 55 | export interface ResetNotifications { 56 | type: typeof types.RESET_NOTIFICATIONS, 57 | } 58 | 59 | export interface AddNotification { 60 | type: typeof types.ADD_NOTIFICATION, 61 | notification: Notification, 62 | } 63 | 64 | export interface NotificationPopoverClose { 65 | type: typeof types.NOTIFICATION_POPOVER_CLOSE, 66 | } 67 | 68 | export interface NotificationPopoverOpen { 69 | type: typeof types.NOTIFICATION_POPOVER_OPEN, 70 | anchorEl: HTMLButtonElement 71 | } 72 | 73 | export interface SetCurrentView { 74 | type: typeof types.SET_CURRENT_VIEW, 75 | value: string 76 | } 77 | 78 | export interface WidgetUpdateDialogOpen { 79 | type: typeof types.WIDGET_UPDATE_DIALOG_OPEN, 80 | } 81 | 82 | export interface WidgetUpdateDialogClose { 83 | type: typeof types.WIDGET_UPDATE_DIALOG_CLOSE, 84 | } 85 | 86 | export type NavigationAction = 87 | RevisionPopoverOpen | 88 | RevisionPopoverClose | 89 | WidgetUpdateDialogOpen | 90 | WidgetUpdateDialogClose | 91 | AddNotification | 92 | ResetNotifications | 93 | NotificationPopoverOpen | 94 | NotificationPopoverClose | 95 | SetCurrentView; 96 | 97 | -------------------------------------------------------------------------------- /src/redux/modules/editor/navigation/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/navigation/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {NavigationAction} from "./actions"; 6 | import {NavigationState} from "../../../state"; 7 | 8 | export function reducer(state: NavigationState, action: NavigationAction) { 9 | switch (action.type) { 10 | case types.SET_CURRENT_VIEW: 11 | return update(state, { 12 | currentView: {$set: action.value} 13 | }); 14 | case types.NOTIFICATION_POPOVER_OPEN: 15 | return update(state, { 16 | notificationPopoverAnchorEl: {$set: action.anchorEl} 17 | }); 18 | case types.NOTIFICATION_POPOVER_CLOSE: 19 | return update(state, { 20 | notificationPopoverAnchorEl: {$set: null} 21 | }); 22 | case types.ADD_NOTIFICATION: 23 | return update(state, { 24 | notifications: {$push: [action.notification]} 25 | }); 26 | case types.RESET_NOTIFICATIONS: 27 | return update(state, { 28 | notifications: {$set: []} 29 | }); 30 | case types.WIDGET_UPDATE_DIALOG_OPEN: 31 | return update(state, { 32 | widgetUpdateDialog: { 33 | open: {$set: true} 34 | } 35 | }); 36 | case types.WIDGET_UPDATE_DIALOG_CLOSE: 37 | return update(state, { 38 | widgetUpdateDialog: { 39 | open: {$set: false} 40 | } 41 | }); 42 | case types.REVISION_POPOVER_OPEN: 43 | return update(state, { 44 | revisionPopoverAnchorEl: {$set: action.anchorEl} 45 | }); 46 | case types.REVISION_POPOVER_CLOSE: 47 | return update(state, { 48 | revisionPopoverAnchorEl: {$set: null} 49 | }); 50 | default: 51 | return state; 52 | } 53 | } 54 | 55 | export {NavigationAction}; -------------------------------------------------------------------------------- /src/redux/modules/editor/navigation/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/navigation/types.ts 2 | 3 | export const SET_CURRENT_VIEW = 'editorWindow/mvcEditor/navigation/SET_CURRENT_VIEW'; 4 | export const NOTIFICATION_POPOVER_OPEN = 'editorWindow/mvcEditor/navigation/NOTIFICATION_POPOVER_OPEN'; 5 | export const NOTIFICATION_POPOVER_CLOSE = 'editorWindow/mvcEditor/navigation/NOTIFICATION_POPOVER_CLOSE'; 6 | export const ADD_NOTIFICATION = 'editorWindow/mvcEditor/navigation/ADD_NOTIFICATION'; 7 | export const RESET_NOTIFICATIONS = 'editorWindow/mvcEditor/navigation/RESET_NOTIFICATIONS'; 8 | // widgetUpdateDialog 9 | export const WIDGET_UPDATE_DIALOG_OPEN = 'editorWindow/mvcEditor/navigation/WIDGET_UPDATE_DIALOG_OPEN'; 10 | export const WIDGET_UPDATE_DIALOG_CLOSE = 'editorWindow/mvcEditor/navigation/WIDGET_UPDATE_DIALOG_CLOSE'; 11 | // 12 | export const REVISION_POPOVER_OPEN = 'editorWindow/mvcEditor/navigation/REVISION_POPOVER_OPEN'; 13 | export const REVISION_POPOVER_CLOSE = 'editorWindow/mvcEditor/navigation/REVISION_POPOVER_CLOSE'; 14 | // -------------------------------------------------------------------------------- /src/redux/modules/editor/processEditor/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/actions/processEditor/actions.ts 2 | 3 | import * as types from './types'; 4 | 5 | // functions 6 | 7 | export function setProcessList(processList: string[]): SetProcessList { 8 | return { type: types.SET_PROCESS_LIST, processList } 9 | } 10 | 11 | export function selectProcess(value: string): SelectProcess { 12 | return { type: types.SELECT_PROCESS, value } 13 | } 14 | 15 | export function processEditorDialogOpen(): ProcessEditorDialogOpen { 16 | return { type: types.PROCESS_EDITOR_DIALOG_OPEN } 17 | } 18 | 19 | export function processEditorDialogClose(): ProcessEditorDialogClose { 20 | return { type: types.PROCESS_EDITOR_DIALOG_CLOSE } 21 | } 22 | 23 | export function stepEditDialogOpen(stepData: any): StepEditDialogOpen { 24 | return { type: types.STEP_EDIT_DIALOG_OPEN, stepData } 25 | } 26 | 27 | export function stepEditDialogClose(): StepEditDialogClose { 28 | return { type: types.STEP_EDIT_DIALOG_CLOSE } 29 | } 30 | 31 | export function updateEditorData(editorData: any): UpdateEditorData { 32 | return { type: types.UPDATE_EDITOR_DATA, editorData } 33 | } 34 | 35 | export function editProcess(processName: string): EditProcess { 36 | return { type: types.EDIT_PROCESS, processName } 37 | } 38 | 39 | export function exitEditing(): ExitEditing { 40 | return { type: types.EXIT_EDITING } 41 | } 42 | 43 | // interfaces 44 | 45 | export interface SetProcessList { 46 | type: typeof types.SET_PROCESS_LIST, 47 | processList: string[] 48 | } 49 | 50 | export interface SelectProcess { 51 | type: typeof types.SELECT_PROCESS, 52 | value: string 53 | } 54 | 55 | export interface ProcessEditorDialogOpen { 56 | type: typeof types.PROCESS_EDITOR_DIALOG_OPEN, 57 | } 58 | 59 | export interface ProcessEditorDialogClose { 60 | type: typeof types.PROCESS_EDITOR_DIALOG_CLOSE, 61 | } 62 | 63 | export interface StepEditDialogOpen { 64 | type: typeof types.STEP_EDIT_DIALOG_OPEN, 65 | stepData: any 66 | } 67 | 68 | export interface StepEditDialogClose { 69 | type: typeof types.STEP_EDIT_DIALOG_CLOSE, 70 | } 71 | 72 | export interface UpdateEditorData { 73 | type: typeof types.UPDATE_EDITOR_DATA, 74 | editorData: any 75 | } 76 | 77 | export interface EditProcess { 78 | type: typeof types.EDIT_PROCESS, 79 | processName: string, 80 | } 81 | 82 | export interface ExitEditing { 83 | type: typeof types.EXIT_EDITING, 84 | } 85 | 86 | export type ProcessEditorAction = 87 | ExitEditing | 88 | EditProcess | 89 | UpdateEditorData | 90 | SelectProcess | 91 | ProcessEditorDialogOpen | 92 | ProcessEditorDialogClose | 93 | StepEditDialogOpen | 94 | StepEditDialogClose | 95 | SetProcessList; 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/redux/modules/editor/processEditor/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/processEditor/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {ProcessEditorAction} from "./actions"; 6 | import {ProcessEditorState} from "../../../state"; 7 | 8 | export function reducer(state: ProcessEditorState, action: ProcessEditorAction) { 9 | switch (action.type) { 10 | case types.SET_PROCESS_LIST: 11 | return update(state, { 12 | processList: {$set: action.processList} 13 | }); 14 | case types.SELECT_PROCESS: 15 | return update(state, { 16 | processSelected: {$set: action.value} 17 | }); 18 | case types.STEP_EDIT_DIALOG_OPEN: 19 | return update(state, { 20 | stepEditDialog: { 21 | open: {$set: true}, 22 | stepData: {$set: action.stepData} 23 | } 24 | }); 25 | case types.STEP_EDIT_DIALOG_CLOSE: 26 | return update(state, { 27 | stepEditDialog: { 28 | open: {$set: false} 29 | } 30 | }); 31 | case types.UPDATE_EDITOR_DATA: 32 | return update(state, { 33 | editorData: {$set: action.editorData} 34 | }); 35 | case types.EDIT_PROCESS: 36 | return update(state, { 37 | processSelected: {$set: action.processName} 38 | }); 39 | case types.EXIT_EDITING: 40 | return update(state, { 41 | processSelected: {$set: undefined}, 42 | }); 43 | 44 | default: 45 | return state; 46 | } 47 | } 48 | 49 | export {ProcessEditorAction}; -------------------------------------------------------------------------------- /src/redux/modules/editor/processEditor/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/processEditor/types.ts 2 | 3 | export const SET_PROCESS_LIST = 'editorWindow/processEditor/SET_PROCESS_LIST'; 4 | export const SELECT_PROCESS = 'editorWindow/processEditor/SELECT_PROCESS'; 5 | // 6 | export const PROCESS_EDITOR_DIALOG_OPEN = 'editorWindow/processEditor/PROCESS_EDITOR_DIALOG_OPEN'; 7 | export const PROCESS_EDITOR_DIALOG_CLOSE = 'editorWindow/processEditor/PROCESS_EDITOR_DIALOG_CLOSE'; 8 | // 9 | export const STEP_EDIT_DIALOG_OPEN = 'editorWindow/processEditor/STEP_EDIT_DIALOG_OPEN'; 10 | export const STEP_EDIT_DIALOG_CLOSE = 'editorWindow/processEditor/STEP_EDIT_DIALOG_CLOSE'; 11 | // 12 | export const UPDATE_EDITOR_DATA = 'editorWindow/processEditor/UPDATE_EDITOR_DATA'; 13 | // 14 | export const EDIT_PROCESS = 'editorWindow/processEditor/EDIT_PROCESS'; 15 | export const EXIT_EDITING = 'editorWindow/processEditor/EXIT_EDITING'; 16 | // 17 | -------------------------------------------------------------------------------- /src/redux/modules/editor/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editorWindow/reducer.ts 2 | 3 | import {EditorState} from "../../state"; 4 | import {reducer as modelEditorReducer, ModelEditorAction} from "./modelEditor/reducer"; 5 | import {reducer as navigationReducer, NavigationAction} from "./navigation/reducer"; 6 | import {reducer as processEditorReducer, ProcessEditorAction} from "./processEditor/reducer"; 7 | import {reducer as uiEditorReducer, UIEditorAction} from './uiEditor/reducer'; 8 | import {reducer as settingsReducer, SettingsAction} from './settings/reducer'; 9 | 10 | export type Action = NavigationAction & ModelEditorAction & ProcessEditorAction & UIEditorAction & SettingsAction; 11 | 12 | export function reducer(state: EditorState, action: Action) { 13 | return { 14 | navigation: navigationReducer(state.navigation, action), 15 | modelEditor: modelEditorReducer(state.modelEditor, action), 16 | processEditor: processEditorReducer(state.processEditor, action), 17 | uiEditor: uiEditorReducer(state.uiEditor, action), 18 | settings: settingsReducer(state.settings, action), 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/redux/modules/editor/settings/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/settings/actions.ts 2 | 3 | import * as types from './types'; 4 | 5 | // functions 6 | 7 | export function settingsDialogOpen(): SettingsDialogOpen { 8 | return { type: types.SETTINGS_DIALOG_OPEN } 9 | } 10 | 11 | export function settingsDialogClose(): SettingsDialogClose { 12 | return { type: types.SETTINGS_DIALOG_CLOSE } 13 | } 14 | 15 | // interfaces 16 | 17 | export interface SettingsDialogOpen { 18 | type: typeof types.SETTINGS_DIALOG_OPEN, 19 | } 20 | 21 | export interface SettingsDialogClose { 22 | type: typeof types.SETTINGS_DIALOG_CLOSE, 23 | } 24 | 25 | export type SettingsAction = 26 | SettingsDialogOpen | 27 | SettingsDialogClose; 28 | -------------------------------------------------------------------------------- /src/redux/modules/editor/settings/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/settings/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {SettingsAction} from "./actions"; 6 | import {SettingsState} from "../../../state"; 7 | 8 | export function reducer(state: SettingsState, action: SettingsAction) { 9 | switch (action.type) { 10 | case types.SETTINGS_DIALOG_OPEN: 11 | return update(state, { 12 | open: {$set: true} 13 | }); 14 | case types.SETTINGS_DIALOG_CLOSE: 15 | return update(state, { 16 | open: {$set: false} 17 | }); 18 | default: 19 | return state; 20 | } 21 | } 22 | 23 | export {SettingsAction} -------------------------------------------------------------------------------- /src/redux/modules/editor/settings/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/settings/types.ts 2 | 3 | export const SETTINGS_DIALOG_OPEN = 'settings/SETTINGS_DIALOG_OPEN'; 4 | export const SETTINGS_DIALOG_CLOSE = 'settings/SETTINGS_DIALOG_CLOSE'; 5 | -------------------------------------------------------------------------------- /src/redux/modules/editor/types.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flintdev/flint/2876286da2d069094e96beedb70bbb757926c0e7/src/redux/modules/editor/types.ts -------------------------------------------------------------------------------- /src/redux/modules/editor/uiEditor/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/uiEditor/actions.ts 2 | 3 | import * as types from './types'; 4 | 5 | // functions 6 | 7 | export function addWidgetDialogOpen (): AddWidgetDialogOpen { 8 | return { type: types.ADD_WIDGET_DIALOG_OPEN } 9 | } 10 | 11 | export function addWidgetDialogClose(): AddWidgetDialogClose { 12 | return { type: types.ADD_WIDGET_DIALOG_CLOSE } 13 | } 14 | 15 | export function increaseMark(): IncreaseMark { 16 | return { type: types.INCREASE_MARK } 17 | } 18 | 19 | export function addLibraryDialogClose(): AddLibraryDialogClose { 20 | return { type: types.ADD_LIBRARY_DIALOG_CLOSE } 21 | } 22 | 23 | export function addLibraryDialogOpen(): AddLibraryDialogOpen { 24 | return { type: types.ADD_LIBRARY_DIALOG_OPEN } 25 | } 26 | 27 | // interfaces 28 | 29 | export interface AddLibraryDialogOpen { 30 | type: typeof types.ADD_LIBRARY_DIALOG_OPEN, 31 | } 32 | 33 | export interface AddLibraryDialogClose { 34 | type: typeof types.ADD_LIBRARY_DIALOG_CLOSE, 35 | } 36 | 37 | export interface AddWidgetDialogClose { 38 | type: typeof types.ADD_WIDGET_DIALOG_CLOSE, 39 | } 40 | 41 | export interface AddWidgetDialogOpen { 42 | type: typeof types.ADD_WIDGET_DIALOG_OPEN, 43 | } 44 | 45 | export interface IncreaseMark { 46 | type: typeof types.INCREASE_MARK, 47 | } 48 | 49 | export type UIEditorAction = 50 | IncreaseMark | 51 | AddLibraryDialogOpen | 52 | AddLibraryDialogClose | 53 | AddWidgetDialogOpen | 54 | AddWidgetDialogClose; 55 | 56 | -------------------------------------------------------------------------------- /src/redux/modules/editor/uiEditor/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/uiEditor/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {UIEditorAction} from "./actions"; 6 | import {UIEditorState} from "../../../state"; 7 | 8 | export function reducer(state: UIEditorState, action: UIEditorAction) { 9 | switch (action.type) { 10 | case types.ADD_WIDGET_DIALOG_OPEN: 11 | return update(state, { 12 | addWidgetDialog: { 13 | open: {$set: true} 14 | } 15 | }); 16 | case types.ADD_WIDGET_DIALOG_CLOSE: 17 | return update(state, { 18 | addWidgetDialog: { 19 | open: {$set: false} 20 | } 21 | }); 22 | case types.ADD_LIBRARY_DIALOG_OPEN: 23 | return update(state, { 24 | addLibraryDialog: { 25 | open: {$set: true} 26 | } 27 | }); 28 | case types.ADD_LIBRARY_DIALOG_CLOSE: 29 | return update(state, { 30 | addLibraryDialog: { 31 | open: {$set: false} 32 | } 33 | }); 34 | case types.INCREASE_MARK: 35 | return update(state, { 36 | _mark: {$set: state._mark + 1} 37 | }); 38 | 39 | default: 40 | return state; 41 | } 42 | } 43 | 44 | export {UIEditorAction}; -------------------------------------------------------------------------------- /src/redux/modules/editor/uiEditor/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/editor/uiEditor/types.ts 2 | 3 | export const ADD_WIDGET_DIALOG_OPEN = 'uiEditor/ADD_WIDGET_DIALOG_OPEN'; 4 | export const ADD_WIDGET_DIALOG_CLOSE = 'uiEditor/ADD_WIDGET_DIALOG_CLOSE'; 5 | export const INCREASE_MARK = 'uiEditor/INCREASE_MARK'; 6 | // 7 | export const ADD_LIBRARY_DIALOG_OPEN = 'uiEditor/ADD_LIBRARY_DIALOG_OPEN'; 8 | export const ADD_LIBRARY_DIALOG_CLOSE= 'uiEditor/ADD_LIBRARY_DIALOG_CLOSE'; 9 | -------------------------------------------------------------------------------- /src/redux/modules/files/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/files/actions.ts 2 | 3 | import * as types from './types'; 4 | import {FileTreeNode} from "../../../interface"; 5 | 6 | // functions 7 | 8 | export function setProjectDir(projectDir: string): SetProjectDir { 9 | return { type: types.SET_PROJECT_DIR, projectDir }; 10 | } 11 | 12 | export function setTreeData(treeData: FileTreeNode[]): SetTreeData { 13 | return { type: types.SET_TREE_DATA, treeData } 14 | } 15 | 16 | export function selectNode(node: FileTreeNode): SelectNode { 17 | return { type: types.SELECT_NODE, node } 18 | } 19 | 20 | export function setFileContent(value: string|null): SetFileContent { 21 | return { type: types.SET_FILE_CONTENT, value } 22 | } 23 | 24 | // interfaces 25 | 26 | export interface SetProjectDir { 27 | type: typeof types.SET_PROJECT_DIR, 28 | projectDir: string, 29 | } 30 | 31 | export interface SetTreeData { 32 | type: typeof types.SET_TREE_DATA, 33 | treeData: FileTreeNode[], 34 | } 35 | 36 | export interface SelectNode { 37 | type: typeof types.SELECT_NODE, 38 | node: FileTreeNode, 39 | } 40 | 41 | export interface SetFileContent { 42 | type: typeof types.SET_FILE_CONTENT, 43 | value: string|null, 44 | } 45 | 46 | export type FilesAction = 47 | SetTreeData | 48 | SelectNode | 49 | SetFileContent | 50 | SetProjectDir; 51 | -------------------------------------------------------------------------------- /src/redux/modules/files/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/files/reducer.ts 2 | 3 | import * as types from './types'; 4 | import update from 'immutability-helper'; 5 | import {FilesAction} from "./actions"; 6 | import {FilesState} from "../../state"; 7 | 8 | export function reducer(state: FilesState, action: FilesAction) { 9 | switch (action.type) { 10 | case types.SET_PROJECT_DIR: 11 | return update(state, { 12 | projectDir: {$set: action.projectDir} 13 | }); 14 | case types.SET_TREE_DATA: 15 | return update(state, { 16 | treeData: {$set: action.treeData} 17 | }); 18 | case types.SELECT_NODE: 19 | return update(state, { 20 | nodeSelected: {$set: action.node} 21 | }); 22 | case types.SET_FILE_CONTENT: 23 | return update(state, { 24 | fileContent: {$set: action.value} 25 | }); 26 | 27 | default: 28 | return state; 29 | } 30 | } 31 | 32 | export {FilesAction}; -------------------------------------------------------------------------------- /src/redux/modules/files/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/files/types.ts 2 | 3 | export const SET_PROJECT_DIR = 'files/SET_PROJECT_DIR'; 4 | export const SET_TREE_DATA = 'files/SET_TREE_DATA'; 5 | export const SELECT_NODE = 'files/SELECT_NODE'; 6 | export const SET_FILE_CONTENT = 'files/SET_FILE_CONTENT'; 7 | -------------------------------------------------------------------------------- /src/redux/modules/starter/actions.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/starterWindow/actions.ts 2 | 3 | import * as types from './types'; 4 | 5 | // functions 6 | 7 | export function createProjectDialogOpen(): CreateProjectDialogOpen { 8 | return { type: types.CREATE_PROJECT_DIALOG_OPEN } 9 | } 10 | 11 | export function createProjectDialogClose(): CreateProjectDialogClose { 12 | return { type: types.CREATE_PROJECT_DIALOG_CLOSE }; 13 | } 14 | 15 | export function validationDialogOpen(projectDirSelected: string): ValidationDialogOpen { 16 | return { type: types.VALIDATION_DIALOG_OPEN, projectDirSelected } 17 | } 18 | 19 | export function validationDialogClose(): ValidationDialogClose { 20 | return { type: types.VALIDATION_DIALOG_CLOSE } 21 | } 22 | 23 | export function setRecentProjects(projects: any[]): SetRecentProjects { 24 | return { type: types.SET_RECENT_PROJECTS, projects } 25 | } 26 | 27 | // interfaces 28 | 29 | export interface SetRecentProjects { 30 | type: typeof types.SET_RECENT_PROJECTS, 31 | projects: any[], 32 | } 33 | 34 | export interface ValidationDialogClose { 35 | type: typeof types.VALIDATION_DIALOG_CLOSE, 36 | } 37 | 38 | export interface ValidationDialogOpen { 39 | type: typeof types.VALIDATION_DIALOG_OPEN, 40 | projectDirSelected: string, 41 | } 42 | 43 | export interface CreateProjectDialogOpen { 44 | type: typeof types.CREATE_PROJECT_DIALOG_OPEN 45 | } 46 | 47 | export interface CreateProjectDialogClose { 48 | type: typeof types.CREATE_PROJECT_DIALOG_CLOSE 49 | } 50 | 51 | export type StarterAction = 52 | SetRecentProjects | 53 | ValidationDialogOpen | 54 | ValidationDialogClose | 55 | CreateProjectDialogOpen | 56 | CreateProjectDialogClose; -------------------------------------------------------------------------------- /src/redux/modules/starter/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/starterWindow/reducer.ts 2 | 3 | import * as types from "./types"; 4 | import update from 'immutability-helper'; 5 | import {StarterAction} from "./actions"; 6 | 7 | export function reducer(state: object, action: StarterAction) { 8 | switch (action.type) { 9 | case types.CREATE_PROJECT_DIALOG_OPEN: 10 | return update(state, { 11 | createProjectDialog: { 12 | open: {$set: true} 13 | } 14 | }); 15 | case types.CREATE_PROJECT_DIALOG_CLOSE: 16 | return update(state, { 17 | createProjectDialog: { 18 | open: {$set: false} 19 | } 20 | }); 21 | case types.VALIDATION_DIALOG_OPEN: 22 | return update(state, { 23 | validationDialog: { 24 | open: {$set: true}, 25 | projectDirSelected: {$set: action.projectDirSelected} 26 | } 27 | }); 28 | case types.VALIDATION_DIALOG_CLOSE: 29 | return update(state, { 30 | validationDialog: { 31 | open: {$set: false} 32 | } 33 | }); 34 | case types.SET_RECENT_PROJECTS: 35 | return update(state, { 36 | recentProjects: {$set: action.projects} 37 | }); 38 | 39 | default: 40 | return state; 41 | } 42 | } 43 | 44 | export {StarterAction}; -------------------------------------------------------------------------------- /src/redux/modules/starter/types.ts: -------------------------------------------------------------------------------- 1 | // src/redux/modules/starterWindow/types.ts 2 | 3 | export const CREATE_PROJECT_DIALOG_OPEN = 'starterWindow/CREATE_PROJECT_DIALOG_OPEN'; 4 | export const CREATE_PROJECT_DIALOG_CLOSE = 'starterWindow/CREATE_PROJECT_DIALOG_CLOSE'; 5 | // 6 | export const VALIDATION_DIALOG_OPEN = 'starterWindow/VALIDATION_DIALOG_OPEN'; 7 | export const VALIDATION_DIALOG_CLOSE = 'starterWindow/VALIDATION_DIALOG_CLOSE'; 8 | // 9 | export const SET_RECENT_PROJECTS = 'starterWindow/SET_RECENT_PROJECTS'; 10 | -------------------------------------------------------------------------------- /src/redux/reducer.ts: -------------------------------------------------------------------------------- 1 | // src/redux/reducer.ts 2 | 3 | import {StoreState} from "./state"; 4 | import {reducer as starterReducer, StarterAction} from "./modules/starter/reducer"; 5 | import {reducer as editorReducer, Action as EditorAction} from "./modules/editor/reducer"; 6 | import {reducer as filesReducer, FilesAction} from "./modules/files/reducer"; 7 | import {reducer as configReducer, ConfigAction} from "./modules/config/reducer"; 8 | import {reducer as componentsReducer, ComponentsAction} from "./modules/components/reducer"; 9 | 10 | export type Action = StarterAction & ConfigAction & EditorAction & FilesAction & ComponentsAction; 11 | 12 | // @ts-ignore 13 | export function reducer(state: StoreState, action: Action) { 14 | return { 15 | starter: starterReducer(state.starter, action), 16 | config: configReducer(state.config, action), 17 | editor: editorReducer(state.editor, action), 18 | files: filesReducer(state.files, action), 19 | components: componentsReducer(state.components, action), 20 | } 21 | } -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, createStore} from "redux"; 2 | import {initState} from "./state"; 3 | import {reducer} from "./reducer"; 4 | import thunk from 'redux-thunk'; 5 | 6 | // @ts-ignore 7 | export const store = createStore( 8 | reducer, 9 | initState, 10 | applyMiddleware(thunk) 11 | ); 12 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.json"; 2 | declare module '*.yaml'; 3 | declare module '*.txt'; 4 | declare module 'request-progress'; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "commonjs", 7 | "target": "es2017", 8 | "resolveJsonModule": true, 9 | "jsx": "react", 10 | "baseUrl": "./", 11 | "paths": { 12 | "src": ["src"], 13 | "resources": ["resources"], 14 | "pjson": ["package.json"] 15 | } 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "dist" 20 | ], 21 | "include": [ 22 | "**/*.ts", 23 | "**/*.tsx", 24 | "**/*.d.ts", 25 | "**/*.yaml", 26 | "**/*.txt" 27 | ] 28 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | 3 | const path = require('path'); 4 | const webpack = require('webpack'); 5 | const BundleTracker = require('webpack-bundle-tracker'); 6 | const CopyPlugin = require('copy-webpack-plugin'); 7 | 8 | const environment = process.env.NODE_ENV; 9 | let filename, processEnv, filePath, mode, target; 10 | if (environment === 'development') { 11 | filePath = './dist/bundles/development'; 12 | filename = '[name].js'; 13 | processEnv = { 14 | NODE_ENV: 'development', 15 | CLIENT_APP: 'flint', 16 | }; 17 | mode = 'development'; 18 | target = 'electron-renderer'; 19 | } else if (environment === 'production') { 20 | filePath = './dist/bundles/production'; 21 | filename = '[name].js'; 22 | processEnv = { 23 | NODE_ENV: 'production', 24 | CLIENT_APP: 'flint', 25 | }; 26 | mode = 'production'; 27 | target = 'electron-renderer'; 28 | } 29 | 30 | const commonConfig = { 31 | mode: mode, 32 | devtool: 'inline-source-map', 33 | resolve: { 34 | extensions: ['.ts', '.tsx', '.js'], 35 | modules: [ 36 | 'node_modules', 37 | ], 38 | alias: { 39 | src: path.resolve(__dirname, 'src'), 40 | resources: path.resolve(__dirname, 'resources'), 41 | pjson: path.resolve(__dirname, 'package.json'), 42 | } 43 | }, 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.ts(x?)$/, 48 | exclude: /node_modules/, 49 | use: [ 50 | { 51 | loader: "ts-loader" 52 | } 53 | ] 54 | }, 55 | { 56 | enforce: "pre", 57 | test: /\.js$/, 58 | loader: "source-map-loader", 59 | exclude: /node_modules\/@atlaskit\/tree/ 60 | }, 61 | { 62 | test: /\.(txt|yaml)$/, 63 | use: 'raw-loader' 64 | }, 65 | { 66 | test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/, 67 | use: 'url-loader?limit=30720' 68 | }, 69 | { 70 | test: /\.css$/, 71 | use: ['style-loader', 'css-loader'] 72 | } 73 | ] 74 | }, 75 | stats: { 76 | errorDetails: true, 77 | errors: true, 78 | source: true, 79 | } 80 | }; 81 | 82 | const rendererConfig = { 83 | ...commonConfig, 84 | entry: { 85 | starter: './src/entries/Starter.tsx', 86 | editor: './src/entries/Editor.tsx', 87 | }, 88 | output: { 89 | path: path.resolve(filePath), 90 | filename: filename, 91 | libraryTarget: 'umd', 92 | }, 93 | plugins: [ 94 | new BundleTracker({ 95 | filename: './webpack-stats.json' 96 | }), 97 | new webpack.EnvironmentPlugin(processEnv), 98 | ], 99 | target: 'electron-renderer', 100 | externals: { 101 | 'react': 'React', 102 | 'react-dom': 'ReactDOM' 103 | } 104 | }; 105 | 106 | const mainConfig = { 107 | ...commonConfig, 108 | entry: { 109 | main: './src/electron/main.ts', 110 | }, 111 | output: { 112 | path: path.resolve(`${filePath}/electron`), 113 | filename: filename, 114 | libraryTarget: "umd", 115 | library: 'flintlib' 116 | }, 117 | node: { 118 | __filename: false, 119 | __dirname: false 120 | }, 121 | target: 'electron-main', 122 | plugins: [ 123 | new BundleTracker({ 124 | filename: './webpack-stats.json' 125 | }), 126 | new webpack.EnvironmentPlugin(processEnv), 127 | new CopyPlugin([ 128 | { from: './src/electron/views/starter.html', to: `./` }, 129 | { from: './src/electron/views/editor.html', to: `./` }, 130 | { from: './resources/img/icon.png', to: `./` }, 131 | { from: `./resources/scripts/${environment}/react.js`, to: `./` }, 132 | { from: `./resources/scripts/${environment}/react-dom.js`, to: `./` }, 133 | { from: './src/electron/utils/dev-app-update.yml', to: `./` }, 134 | ]), 135 | ], 136 | }; 137 | 138 | module.exports = env => { 139 | return [rendererConfig, mainConfig] 140 | }; --------------------------------------------------------------------------------