├── .vscode
└── settings.json
├── src
├── features
│ ├── common
│ │ ├── redux
│ │ │ ├── actions.js
│ │ │ ├── constants.js
│ │ │ ├── initialState.js
│ │ │ └── reducer.js
│ │ ├── OpenLink.less
│ │ ├── FolderPicker.less
│ │ ├── PageNotFound.less
│ │ ├── style.less
│ │ ├── WebView.less
│ │ ├── route.js
│ │ ├── index.js
│ │ ├── PageNotFound.js
│ │ ├── OpenLink.js
│ │ ├── FormBuilder.less
│ │ ├── WebView.js
│ │ └── FolderPicker.js
│ ├── home
│ │ ├── DialogPlace.less
│ │ ├── App.less
│ │ ├── style.less
│ │ ├── route.js
│ │ ├── index.js
│ │ ├── redux
│ │ │ ├── actions.js
│ │ │ ├── initialState.js
│ │ │ ├── showWelcomePage.js
│ │ │ ├── hideNewProjectDialog.js
│ │ │ ├── showNewProjectDialog.js
│ │ │ ├── reducer.js
│ │ │ ├── constants.js
│ │ │ ├── closeProject.js
│ │ │ ├── openProject.js
│ │ │ ├── getInitialState.js
│ │ │ └── getMainState.js
│ │ ├── DialogPlace.js
│ │ ├── NewProjectDialog.less
│ │ ├── RekitStudioPage.less
│ │ ├── RecentProjects.less
│ │ ├── WelcomePage.less
│ │ ├── RecentProjects.js
│ │ ├── App.js
│ │ ├── NewProjectDialog.js
│ │ ├── utils.js
│ │ └── WelcomePage.js
│ ├── plugin-manager
│ │ ├── PluginIcon.less
│ │ ├── PluginDetail.less
│ │ ├── style.less
│ │ ├── route.js
│ │ ├── index.js
│ │ ├── redux
│ │ │ ├── actions.js
│ │ │ ├── initialState.js
│ │ │ ├── reducer.js
│ │ │ ├── constants.js
│ │ │ ├── enablePlugin.js
│ │ │ ├── disablePlugin.js
│ │ │ ├── fetchOnlinePlugins.js
│ │ │ ├── fetchInstalledPlugins.js
│ │ │ └── uninstallPlugin.js
│ │ ├── PluginIcon.js
│ │ ├── PluginHeader.js
│ │ ├── InstallButton.less
│ │ ├── MainPage.less
│ │ ├── PluginHeader.less
│ │ ├── PluginList.less
│ │ ├── PluginDetail.js
│ │ ├── MainPage.js
│ │ └── InstallButton.js
│ └── new-project
│ │ ├── NewProjectForm.less
│ │ ├── ProjectProperties.less
│ │ ├── CreatingStatusView.less
│ │ ├── style.less
│ │ ├── redux
│ │ ├── actions.js
│ │ ├── constants.js
│ │ ├── clearCreateAppStatus.js
│ │ ├── initialState.js
│ │ ├── reducer.js
│ │ ├── createApp.js
│ │ └── fetchAppTypes.js
│ │ ├── route.js
│ │ ├── NewProjectDialog.less
│ │ ├── index.js
│ │ ├── ProjectProperties.js
│ │ ├── AppTypeSelect.less
│ │ ├── NewProjectForm.js
│ │ ├── CreatingStatusView.js
│ │ └── AppTypeSelect.js
├── styles
│ ├── index.scss
│ ├── index.less
│ ├── mixins.less
│ └── global.less
├── favicon.png
├── images
│ ├── logo.png
│ ├── logo2.png
│ ├── plugin-logo.png
│ ├── discord.svg
│ ├── react-logo.svg
│ └── rekit-logo.svg
├── fonts
│ ├── roboto-v19-latin-100.eot
│ ├── roboto-v19-latin-100.ttf
│ ├── roboto-v19-latin-100.woff
│ ├── roboto-v19-latin-100.woff2
│ ├── roboto-v19-latin-300.eot
│ ├── roboto-v19-latin-300.ttf
│ ├── roboto-v19-latin-300.woff
│ └── roboto-v19-latin-300.woff2
├── common
│ ├── history.js
│ ├── store.js
│ ├── rootReducer.js
│ ├── configStore.js
│ └── routeConfig.js
├── index.js
├── Root.js
└── logo.svg
├── tools
└── index.js
├── icon.png
├── icon.icns
├── scripts
├── release.js
├── publish.js
├── copyPlugins.js
├── buildElectron.js
├── build.js
├── test.js
└── startRekitStudio.js
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── node
├── store.js
├── logger.js
├── log.js
├── bridge.js
├── studio.js
├── ua.js
├── utils.js
├── checkUpdate.js
├── init.js
├── taskRunner.js
└── main.js
├── .editorconfig
├── .prettierrc
├── README.md
├── .babelrc
├── rekit.json
├── tests
├── index.test.js
├── features
│ ├── common
│ │ ├── OpenLink.test.js
│ │ ├── FolderPicker.test.js
│ │ ├── FormBuilder.test.js
│ │ └── PageNotFound.test.js
│ ├── plugin-manager
│ │ ├── PluginIcon.test.js
│ │ ├── PluginHeader.test.js
│ │ ├── MainPage.test.js
│ │ ├── PluginList.test.js
│ │ ├── InstallButton.test.js
│ │ ├── PluginDetail.test.js
│ │ └── redux
│ │ │ ├── enablePlugin.test.js
│ │ │ ├── disablePlugin.test.js
│ │ │ ├── installPlugin.test.js
│ │ │ ├── uninstallPlugin.test.js
│ │ │ ├── fetchOnlinePlugins.test.js
│ │ │ └── fetchInstalledPlugins.test.js
│ ├── home
│ │ ├── App.test.js
│ │ ├── DefaultPage.test.js
│ │ ├── RecentProjects.test.js
│ │ └── redux
│ │ │ └── showWelcomePage.test.js
│ └── new-project
│ │ ├── NewProjectForm.test.js
│ │ ├── AppTypeSelect.test.js
│ │ ├── CreatingStatusView.test.js
│ │ └── redux
│ │ ├── clearCreateAppStatus.test.js
│ │ ├── createApp.test.js
│ │ └── fetchAppTypes.test.js
├── setup.js
└── Root.test.js
├── test.js
├── config
├── jest
│ ├── fileTransform.js
│ └── cssTransform.js
├── polyfills.js
├── paths.js
└── env.js
└── .gitignore
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/src/features/common/redux/actions.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/features/common/redux/constants.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tools/index.js:
--------------------------------------------------------------------------------
1 | // Just a placeholder.
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | img {width: 100px;}
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/icon.png
--------------------------------------------------------------------------------
/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/icon.icns
--------------------------------------------------------------------------------
/scripts/release.js:
--------------------------------------------------------------------------------
1 | const { spawn } = require('child_process');
2 | spawn('npx', [])
--------------------------------------------------------------------------------
/src/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/favicon.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/node/store.js:
--------------------------------------------------------------------------------
1 | const Store = require('electron-store');
2 | module.exports = new Store();
3 |
--------------------------------------------------------------------------------
/src/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/images/logo.png
--------------------------------------------------------------------------------
/src/images/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/images/logo2.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | {
2 | indent_style: 'space',
3 | indent_size: 2,
4 | tab_width: 2
5 | };
6 |
--------------------------------------------------------------------------------
/node/logger.js:
--------------------------------------------------------------------------------
1 | module.exports = require('rekit-core').core.logger.child({ label: 'app' });
2 |
--------------------------------------------------------------------------------
/src/images/plugin-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/images/plugin-logo.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 100
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/common/OpenLink.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .common-open-link {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/home/DialogPlace.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .home-dialog-place {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | # Develop
4 | How to publish:
5 | ```
6 | npx electron-builder -p always
7 | ```
8 |
9 |
--------------------------------------------------------------------------------
/src/features/common/FolderPicker.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .common-folder-picker {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-100.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-100.eot
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-100.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-100.ttf
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-100.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-100.woff
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-100.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-100.woff2
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-300.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-300.eot
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-300.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-300.ttf
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-300.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-300.woff
--------------------------------------------------------------------------------
/src/fonts/roboto-v19-latin-300.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rekit/rekit-app/HEAD/src/fonts/roboto-v19-latin-300.woff2
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginIcon.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .plugin-manager-plugin-icon {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react-app"],
3 | "plugins": ["react-hot-loader/babel", "syntax-dynamic-import", "lodash"]
4 | }
5 |
--------------------------------------------------------------------------------
/src/features/new-project/NewProjectForm.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .new-project-new-project-form {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginDetail.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .plugin-manager-plugin-detail {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/new-project/ProjectProperties.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .new-project-project-properties {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/features/common/PageNotFound.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .common-page-not-found {
4 | color: #fff;
5 | padding: 10px;
6 | }
7 |
--------------------------------------------------------------------------------
/rekit.json:
--------------------------------------------------------------------------------
1 | {
2 | "devPort": 6093,
3 | "studioPort": 3002,
4 | "appType": "rekit-react",
5 | "rekitTest": true,
6 | "exclude": ["dist"],
7 | "css": "less"
8 | }
9 |
--------------------------------------------------------------------------------
/src/features/new-project/CreatingStatusView.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .new-project-creating-status-view {
4 | .ant-timeline {
5 | margin: 20px;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/common/history.js:
--------------------------------------------------------------------------------
1 | import { createHashHistory } from 'history';
2 |
3 | // A singleton history object for easy API navigation
4 | const history = createHashHistory();
5 | export default history;
6 |
--------------------------------------------------------------------------------
/src/features/common/style.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 | @import './WebView';
3 | @import './FormBuilder';
4 | @import './FolderPicker';
5 | @import './OpenLink';
6 | @import './PageNotFound';
7 |
--------------------------------------------------------------------------------
/src/features/home/App.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .home-app {
4 | .page-container {
5 | z-index: 0;
6 | .abs-fullsize();
7 | top: @header-height;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/node/log.js:
--------------------------------------------------------------------------------
1 | const log = require('electron-log');
2 | // Config electron log
3 | log.transports.console.level = 'info';
4 | log.transports.file.level = 'info';
5 | log.info('electron log set up.');
6 |
7 | module.exports = log;
--------------------------------------------------------------------------------
/src/features/common/WebView.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .common-web-view {
4 | position: relative;
5 | width: 100%;
6 | height: 100%;
7 | webview {
8 | .abs-fullsize;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/features/new-project/style.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 | @import './ProjectProperties';
3 | @import './NewProjectDialog';
4 | @import './NewProjectForm';
5 | @import './AppTypeSelect';
6 | @import './CreatingStatusView';
7 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/style.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 | @import './MainPage';
3 | @import './PluginList';
4 | @import './PluginHeader';
5 | @import './PluginDetail';
6 | @import './PluginIcon';
7 | @import './InstallButton';
8 |
--------------------------------------------------------------------------------
/tests/index.test.js:
--------------------------------------------------------------------------------
1 | // index.js should run without errors.
2 | describe('index', () => {
3 | it('index.js has no error', () => {
4 | document.body.innerHTML = '
';
5 | require('../src/index');
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/src/features/new-project/redux/actions.js:
--------------------------------------------------------------------------------
1 | export { fetchAppTypes, dismissFetchAppTypesError } from './fetchAppTypes';
2 | export { createApp, dismissCreateAppError } from './createApp';
3 | export { clearCreateAppStatus } from './clearCreateAppStatus';
4 |
--------------------------------------------------------------------------------
/src/styles/index.less:
--------------------------------------------------------------------------------
1 | // index is the entry for all styles.
2 | @import './global';
3 | @import '../features/home/style';
4 | @import '../features/common/style';
5 | @import '../features/new-project/style';
6 | @import '../features/plugin-manager/style';
7 |
--------------------------------------------------------------------------------
/src/features/home/style.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 | @import './App';
3 | @import './WelcomePage';
4 | @import './RekitStudioPage';
5 | @import './TitleBar';
6 | @import './NewProjectDialog';
7 | @import './DialogPlace';
8 | @import './RecentProjects';
9 |
--------------------------------------------------------------------------------
/src/features/new-project/route.js:
--------------------------------------------------------------------------------
1 | // This is the JSON way to define React Router rules in a Rekit app.
2 | // Learn more from: http://rekit.js.org/docs/routing.html
3 |
4 |
5 | export default {
6 | path: 'new-project',
7 | childRoutes: [
8 | ],
9 | };
10 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | // const chokidar = require('chokidar');
2 | // const watcher = chokidar.watch(__dirname, { ignored: /node_modules/ });
3 | // watcher.on('all', (...args) => console.log('file changed', args));
4 | // setInterval(() => {
5 | console.log(new Date());
6 | // }, 1000);
7 |
--------------------------------------------------------------------------------
/src/features/common/route.js:
--------------------------------------------------------------------------------
1 | // This is the JSON way to define React Router rules in a Rekit app.
2 | // Learn more from: http://rekit.js.org/docs/routing.html
3 |
4 | export default {
5 | path: 'common',
6 | name: 'Common',
7 | childRoutes: [
8 | ],
9 | };
10 |
--------------------------------------------------------------------------------
/src/features/home/route.js:
--------------------------------------------------------------------------------
1 | import { WelcomePage } from './';
2 |
3 | export default {
4 | path: '/',
5 | name: 'Home',
6 | childRoutes: [
7 | {
8 | path: 'welcome',
9 | component: WelcomePage,
10 | isIndex: true,
11 | },
12 | ],
13 | };
14 |
--------------------------------------------------------------------------------
/src/features/new-project/NewProjectDialog.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .new-project-new-project-dialog {
4 | .ant-modal-body {
5 | height: 500px;
6 | text-align:justify;
7 | position: relative;
8 | }
9 |
10 | .btn-back {
11 | float: left;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/scripts/publish.js:
--------------------------------------------------------------------------------
1 | /*
2 | * 1. Build Rekit Studio: yarn build
3 | * 2. Build Rekit Plugins: yarn build
4 | * 3. Build Rekit App static server: yarn build This will copy built-in plugins to build server
5 | * 4. Build Electron app: yarn dist
6 | *
7 | * To test:
8 | * 1. Remove /.rekit folders
9 | */
--------------------------------------------------------------------------------
/src/features/common/index.js:
--------------------------------------------------------------------------------
1 | export { default as PageNotFound } from './PageNotFound';
2 | export { default as WebView } from './WebView';
3 | export { default as FormBuilder } from './FormBuilder';
4 | export { default as FolderPicker } from './FolderPicker';
5 | export { default as OpenLink } from './OpenLink';
6 |
--------------------------------------------------------------------------------
/tests/features/common/OpenLink.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { OpenLink } from '../../../src/features/common';
4 |
5 | it('renders node with correct class name', () => {
6 | const renderedComponent = shallow();
7 | expect(renderedComponent.find('.common-open-link').length).toBe(1);
8 | });
9 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/route.js:
--------------------------------------------------------------------------------
1 | import { MainPage } from './';
2 | // This is the JSON way to define React Router rules in a Rekit app.
3 | // Learn more from: http://rekit.js.org/docs/routing.html
4 |
5 |
6 | export default {
7 | path: 'plugins',
8 | childRoutes: [
9 | { path: '/plugins/:plugin?', component: MainPage },
10 | ],
11 | };
12 |
--------------------------------------------------------------------------------
/src/features/new-project/index.js:
--------------------------------------------------------------------------------
1 | export { default as ProjectProperties } from './ProjectProperties';
2 | export { default as NewProjectDialog } from './NewProjectDialog';
3 | export { default as NewProjectForm } from './NewProjectForm';
4 | export { default as AppTypeSelect } from './AppTypeSelect';
5 | export { default as CreatingStatusView } from './CreatingStatusView';
6 |
--------------------------------------------------------------------------------
/tests/features/common/FolderPicker.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { FolderPicker } from '../../../src/features/common';
4 |
5 | it('renders node with correct class name', () => {
6 | const renderedComponent = shallow();
7 | expect(renderedComponent.find('.common-folder-picker').length).toBe(1);
8 | });
9 |
--------------------------------------------------------------------------------
/tests/features/common/FormBuilder.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { FormBuilder } from '../../../src/features/common';
4 |
5 | it('renders node with correct class name', () => {
6 | const renderedComponent = shallow();
7 | expect(renderedComponent.find('.common-form-builder-js').length).toBe(1);
8 | });
9 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/index.js:
--------------------------------------------------------------------------------
1 | export { default as MainPage } from './MainPage';
2 | export { default as PluginList } from './PluginList';
3 | export { default as PluginHeader } from './PluginHeader';
4 | export { default as PluginDetail } from './PluginDetail';
5 | export { default as PluginIcon } from './PluginIcon';
6 | export { default as InstallButton } from './InstallButton';
7 |
--------------------------------------------------------------------------------
/tests/features/plugin-manager/PluginIcon.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { PluginIcon } from '../../../src/features/plugin-manager';
4 |
5 | it('renders node with correct class name', () => {
6 | const renderedComponent = shallow();
7 | expect(renderedComponent.find('.plugin-manager-plugin-icon').length).toBe(1);
8 | });
9 |
--------------------------------------------------------------------------------
/tests/features/home/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { App } from '../../../src/features/home';
4 |
5 | describe('home/App', () => {
6 | it('renders node with correct class name', () => {
7 | const renderedComponent = shallow();
8 |
9 | expect(renderedComponent.find('.home-app').length).toBe(1);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/tests/features/new-project/NewProjectForm.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { NewProjectForm } from '../../../src/features/new-project';
4 |
5 | it('renders node with correct class name', () => {
6 | const renderedComponent = shallow();
7 | expect(renderedComponent.find('.new-project-new-project-form').length).toBe(1);
8 | });
9 |
--------------------------------------------------------------------------------
/tests/features/plugin-manager/PluginHeader.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { PluginHeader } from '../../../src/features/plugin-manager';
4 |
5 | it('renders node with correct class name', () => {
6 | const renderedComponent = shallow();
7 | expect(renderedComponent.find('.plugin-manager-plugin-header').length).toBe(1);
8 | });
9 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/node/bridge.js:
--------------------------------------------------------------------------------
1 | const { ipcRenderer, shell, remote } = require('electron');
2 | const fs = require('fs');
3 | const path = require('path');
4 | const ua = require('./ua');
5 |
6 | window.bridge = {
7 | ipcRenderer,
8 | isWin: process.platform === 'win32',
9 | shell,
10 | remote,
11 | fs,
12 | path,
13 | ua,
14 | openUrl(url) {
15 | shell.openExternal(url);
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/node/studio.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const PATH_APP_NODE_MODULES = path.join(__dirname, '../node-app/nms');
4 | require('module').globalPaths.push(PATH_APP_NODE_MODULES);
5 | module.paths.push(PATH_APP_NODE_MODULES);
6 | console.log(PATH_APP_NODE_MODULES);
7 | require('rekit-studio/lib/start')({
8 | projectRoot: '/Users/pwang7/workspace/rekit-studio',
9 | port: '9001',
10 | });
11 |
--------------------------------------------------------------------------------
/scripts/copyPlugins.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const path = require('path');
3 | const srcDir = path.join(__dirname, '../../rekit-studio/src/features');
4 | const destDir = path.join(__dirname, '../build/plugins');
5 | fs.ensureDirSync(destDir);
6 | [].forEach(plugin => {
7 | fs.copySync(path.join(srcDir, plugin), path.join(destDir, plugin));
8 | });
9 | console.log('Built-in plugin copied.');
10 |
--------------------------------------------------------------------------------
/scripts/buildElectron.js:
--------------------------------------------------------------------------------
1 | // Copy files before build electron app
2 | const path = require('path');
3 | const fs = require('fs-extra');
4 |
5 | const buildDir = path.join(__dirname, '../build');
6 | console.log('copy files');
7 | ['rekit-react', 'ebay-node'].forEach(name => {
8 | fs.copySync(
9 | path.join(__dirname, '../../rekit-studio/src/features', name),
10 | path.join(buildDir, 'plugins', name),
11 | );
12 | });
13 |
--------------------------------------------------------------------------------
/src/features/home/index.js:
--------------------------------------------------------------------------------
1 | export { default as App } from './App';
2 | export { default as WelcomePage } from './WelcomePage';
3 | export { default as RekitStudioPage } from './RekitStudioPage';
4 | export { default as TitleBar } from './TitleBar';
5 | export { default as NewProjectDialog } from './NewProjectDialog';
6 | export { default as DialogPlace } from './DialogPlace';
7 | export { default as RecentProjects } from './RecentProjects';
8 |
--------------------------------------------------------------------------------
/tests/features/common/PageNotFound.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { PageNotFound } from '../../../src/features/common';
4 |
5 | describe('common/PageNotFound', () => {
6 | it('renders node with correct class name', () => {
7 | const renderedComponent = shallow();
8 |
9 | expect(renderedComponent.find('.common-page-not-found').length).toBe(1);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/features/common/PageNotFound.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 |
3 | export default class PageNotFound extends PureComponent {
4 | render() {
5 | return null;
6 | }
7 | // render() {
8 | // return (
9 | //
10 | // Ops, page not found.
11 | //
12 | // Back to welcome page.
13 | //
14 | //
15 | // );
16 | // }
17 | }
18 |
--------------------------------------------------------------------------------
/src/features/common/OpenLink.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class OpenLink extends Component {
4 | handleClick = evt => {
5 | evt.preventDefault();
6 | evt.stopPropagation();
7 | window.bridge.shell.openExternal(this.props.href);
8 | };
9 |
10 | render() {
11 | return (
12 |
13 | {this.props.children}
14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/features/home/redux/actions.js:
--------------------------------------------------------------------------------
1 | export { openProject, dismissOpenProjectError } from './openProject';
2 | export { closeProject, dismissCloseProjectError } from './closeProject';
3 | export { getInitialState, dismissGetInitialStateError } from './getInitialState';
4 | export { getMainState, dismissGetMainStateError } from './getMainState';
5 | export { showNewProjectDialog } from './showNewProjectDialog';
6 | export { hideNewProjectDialog } from './hideNewProjectDialog';
7 | export { showWelcomePage } from './showWelcomePage';
8 |
--------------------------------------------------------------------------------
/src/features/home/redux/initialState.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | openProjectPending: false,
3 | openProjectError: null,
4 | closeProjectPending: false,
5 | closeProjectError: null,
6 |
7 | studios: [],
8 | studioById: {},
9 | getInitialStatePending: false,
10 | getInitialStateError: null,
11 |
12 | initializing: true,
13 | getMainStatePending: false,
14 | getMainStateError: null,
15 |
16 | newProjectDialogVisible: false,
17 | welcomePageVisible: false,
18 | };
19 |
20 | export default initialState;
21 |
--------------------------------------------------------------------------------
/tests/features/home/DefaultPage.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { DefaultPage } from '../../../src/features/home/DefaultPage';
4 |
5 | describe('home/DefaultPage', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | home: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow();
12 |
13 | expect(renderedComponent.find('.home-default-page').length).toBe(1);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/redux/actions.js:
--------------------------------------------------------------------------------
1 | export { fetchInstalledPlugins, dismissFetchInstalledPluginsError } from './fetchInstalledPlugins';
2 | export { enablePlugin, dismissEnablePluginError } from './enablePlugin';
3 | export { disablePlugin, dismissDisablePluginError } from './disablePlugin';
4 | export { installPlugin, dismissInstallPluginError } from './installPlugin';
5 | export { uninstallPlugin, dismissUninstallPluginError } from './uninstallPlugin';
6 | export { fetchOnlinePlugins, dismissFetchOnlinePluginsError } from './fetchOnlinePlugins';
7 |
--------------------------------------------------------------------------------
/src/common/store.js:
--------------------------------------------------------------------------------
1 | import configStore from './configStore';
2 |
3 | export default {
4 | store: null,
5 | getStore() {
6 | if (!this.store) this.store = configStore();
7 | return this.store;
8 | },
9 | getState() {
10 | return this.getStore().getState();
11 | },
12 | dispatch(action) {
13 | return this.getStore().dispatch(action);
14 | },
15 | subscribe(listener) {
16 | return this.getStore().subscribe(listener);
17 | },
18 | replaceReducer(reducer) {
19 | return this.getStore().replaceReducer(reducer);
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/tests/setup.js:
--------------------------------------------------------------------------------
1 | // This module will be executed before all other tests are executed,
2 | // so import all necessary modules which should be included for webpack compiling.
3 | import axios from 'axios';
4 | import httpAdapter from 'axios/lib/adapters/http';
5 | import { configure } from 'enzyme';
6 | import Adapter from 'enzyme-adapter-react-16';
7 |
8 | configure({ adapter: new Adapter(), disableLifecycleMethods: true });
9 |
10 | if (process.env.NODE_ENV === 'test') {
11 | axios.defaults.baseURL = 'http://localhost';
12 | axios.defaults.adapter = httpAdapter;
13 | }
14 |
--------------------------------------------------------------------------------
/tests/features/home/RecentProjects.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { RecentProjects } from '../../../src/features/home/RecentProjects';
4 |
5 | describe('home/RecentProjects', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | home: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.home-recent-projects').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/features/plugin-manager/MainPage.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { MainPage } from '../../../src/features/plugin-manager/MainPage';
4 |
5 | describe('plugin-manager/MainPage', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | pluginManager: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.plugin-manager-main-page').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/features/plugin-manager/PluginList.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { PluginList } from '../../../src/features/plugin-manager/PluginList';
4 |
5 | describe('plugin-manager/PluginList', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | pluginManager: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.plugin-manager-plugin-list').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/features/new-project/AppTypeSelect.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { AppTypeSelect } from '../../../src/features/new-project/AppTypeSelect';
4 |
5 | describe('new-project/AppTypeSelect', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | newProject: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.new-project-app-type-select').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/features/plugin-manager/InstallButton.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { InstallButton } from '../../../src/features/plugin-manager/PluginButton';
4 |
5 | describe('plugin-manager/InstallButton', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | pluginManager: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.plugin-manager-plugin-button').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/features/plugin-manager/PluginDetail.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { PluginDetail } from '../../../src/features/plugin-manager/PluginDetail';
4 |
5 | describe('plugin-manager/PluginDetail', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | pluginManager: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.plugin-manager-plugin-detail').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/features/new-project/CreatingStatusView.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import { CreatingStatusView } from '../../../src/features/new-project/CreatingStatusView';
4 |
5 | describe('new-project/CreatingStatusView', () => {
6 | it('renders node with correct class name', () => {
7 | const props = {
8 | newProject: {},
9 | actions: {},
10 | };
11 | const renderedComponent = shallow(
12 |
13 | );
14 |
15 | expect(
16 | renderedComponent.find('.new-project-creating-status-view').length
17 | ).toBe(1);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/scripts/build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const paths = require('../config/paths');
3 |
4 | const buildStatic = require('./buildStatic');
5 |
6 | console.log('Building static assets...');
7 | // buildStatic().then(() => {
8 | // console.log('Build static assets done.');
9 |
10 | // // Copy node files
11 | // console.log('Copy node files');
12 |
13 | // // Process index.html
14 | // });
15 |
16 | function buildElectron() {
17 | fs.removeSync(paths.resolveApp('app/node'));
18 | fs.copySync(paths.resolveApp('node'), paths.resolveApp('app/node'), {
19 | dereference: true,
20 | });
21 | }
22 |
23 | // buildElectron();
24 | buildStatic();
25 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginIcon.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | const defaultPluginLogo = require('../../images/plugin-logo.png');
3 |
4 | export default class PluginIcon extends Component {
5 | static propTypes = {};
6 |
7 | render() {
8 | const { item } = this.props;
9 | return (
10 |
{
19 | evt.target.src = defaultPluginLogo;
20 | }}
21 | />
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/node/ua.js:
--------------------------------------------------------------------------------
1 | const store = require('./store');
2 | const uuidv4 = require('uuid/v4');
3 | const log = require('./log');
4 | const analytics = require('universal-analytics');
5 |
6 | let uuid = store.get('uuid');
7 | if (!uuid) {
8 | uuid = uuidv4();
9 | log.info('uuid created: ', uuid);
10 | store.set('uuid', uuid);
11 | } else {
12 | log.info('uuid exists: ', uuid);
13 | }
14 |
15 | const ua = analytics('UA-132547525-1', uuid, { http: true });
16 | ua.set('uid', uuid);
17 | ua.set('source', 'rekit-app');
18 |
19 | ua.screenview('start-page', 'rekit-app', '3.0.0', err => {
20 | if (err) {
21 | log.warn('ga screenview start-page failed');
22 | log.warn(err);
23 | }
24 | }).send();
25 |
26 | module.exports = ua;
27 |
--------------------------------------------------------------------------------
/src/features/home/redux/showWelcomePage.js:
--------------------------------------------------------------------------------
1 | // Rekit uses a new approach to organizing actions and reducers. That is
2 | // putting related actions and reducers in one file. See more at:
3 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
4 |
5 | import {
6 | HOME_SHOW_WELCOME_PAGE,
7 | } from './constants';
8 |
9 | export function showWelcomePage() {
10 | return {
11 | type: HOME_SHOW_WELCOME_PAGE,
12 | };
13 | }
14 |
15 | export function reducer(state, action) {
16 | switch (action.type) {
17 | case HOME_SHOW_WELCOME_PAGE:
18 | return {
19 | ...state,
20 | welcomePageVisible: true,
21 | };
22 |
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/features/common/redux/initialState.js:
--------------------------------------------------------------------------------
1 | // Initial state is the place you define all initial values for the Redux store of the feature.
2 | // In the 'standard' way, initialState is defined in reducers: http://redux.js.org/docs/basics/Reducers.html
3 | // But when application grows, there will be multiple reducers files, it's not intuitive what data is managed by the whole store.
4 | // So Rekit extracts the initial state definition into a separate module so that you can have
5 | // a quick view about what data is used for the feature, at any time.
6 |
7 | // NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions.
8 |
9 | const initialState = {
10 | };
11 |
12 | export default initialState;
13 |
--------------------------------------------------------------------------------
/src/features/home/redux/hideNewProjectDialog.js:
--------------------------------------------------------------------------------
1 | // Rekit uses a new approach to organizing actions and reducers. That is
2 | // putting related actions and reducers in one file. See more at:
3 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
4 |
5 | import {
6 | HOME_HIDE_NEW_PROJECT_DIALOG,
7 | } from './constants';
8 |
9 | export function hideNewProjectDialog() {
10 | return {
11 | type: HOME_HIDE_NEW_PROJECT_DIALOG,
12 | };
13 | }
14 |
15 | export function reducer(state, action) {
16 | switch (action.type) {
17 | case HOME_HIDE_NEW_PROJECT_DIALOG:
18 | return {
19 | ...state,
20 | newProjectDialogVisible: false,
21 | };
22 |
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/features/home/redux/showNewProjectDialog.js:
--------------------------------------------------------------------------------
1 | // Rekit uses a new approach to organizing actions and reducers. That is
2 | // putting related actions and reducers in one file. See more at:
3 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
4 |
5 | import {
6 | HOME_SHOW_NEW_PROJECT_DIALOG,
7 | } from './constants';
8 |
9 | export function showNewProjectDialog() {
10 | return {
11 | type: HOME_SHOW_NEW_PROJECT_DIALOG,
12 | };
13 | }
14 |
15 | export function reducer(state, action) {
16 | switch (action.type) {
17 | case HOME_SHOW_NEW_PROJECT_DIALOG:
18 | return {
19 | ...state,
20 | newProjectDialogVisible: true,
21 | };
22 |
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/features/common/FormBuilder.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .form-builder-row {
4 | .ant-form-item {
5 | margin-bottom: 5px;
6 | }
7 | }
8 |
9 | .form-builder-row-view-mode {
10 | .ant-form-item-label {
11 | text-align: left;
12 | line-height: 150%;
13 | font-weight: normal;
14 | label {
15 | font-weight: bold;
16 | padding-left: 5px;
17 | }
18 | }
19 | .ant-form-item-control {
20 | line-height: 150%;
21 | overflow: hidden;
22 | text-overflow: ellipsis;
23 | }
24 | .ant-form-item {
25 | padding: 6px 0;
26 | white-space: nowrap;
27 | &:hover {
28 | background-color: #f7f7f7;
29 | }
30 |
31 | }
32 | }
33 |
34 | .ant-form-item-children {
35 | .ant-input-number {
36 | width: 100%;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/features/home/DialogPlace.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { connect } from 'react-redux';
4 | import { NewProjectDialog } from '../new-project';
5 |
6 | export class DialogPlace extends Component {
7 | static propTypes = {
8 | newProjectDialogVisible: PropTypes.bool.isRequired,
9 | };
10 |
11 | render() {
12 | return (
13 |
14 | {this.props.newProjectDialogVisible && }
15 |
16 | );
17 | }
18 | }
19 |
20 | /* istanbul ignore next */
21 | function mapStateToProps(state) {
22 | return {
23 | newProjectDialogVisible: state.home.newProjectDialogVisible,
24 | };
25 | }
26 |
27 | export default connect(
28 | mapStateToProps,
29 | )(DialogPlace);
30 |
--------------------------------------------------------------------------------
/src/features/new-project/redux/constants.js:
--------------------------------------------------------------------------------
1 | export const NEW_PROJECT_FETCH_APP_TYPES_BEGIN = 'NEW_PROJECT_FETCH_APP_TYPES_BEGIN';
2 | export const NEW_PROJECT_FETCH_APP_TYPES_SUCCESS = 'NEW_PROJECT_FETCH_APP_TYPES_SUCCESS';
3 | export const NEW_PROJECT_FETCH_APP_TYPES_FAILURE = 'NEW_PROJECT_FETCH_APP_TYPES_FAILURE';
4 | export const NEW_PROJECT_FETCH_APP_TYPES_DISMISS_ERROR = 'NEW_PROJECT_FETCH_APP_TYPES_DISMISS_ERROR';
5 | export const NEW_PROJECT_CREATE_APP_BEGIN = 'NEW_PROJECT_CREATE_APP_BEGIN';
6 | export const NEW_PROJECT_CREATE_APP_SUCCESS = 'NEW_PROJECT_CREATE_APP_SUCCESS';
7 | export const NEW_PROJECT_CREATE_APP_FAILURE = 'NEW_PROJECT_CREATE_APP_FAILURE';
8 | export const NEW_PROJECT_CREATE_APP_DISMISS_ERROR = 'NEW_PROJECT_CREATE_APP_DISMISS_ERROR';
9 | export const NEW_PROJECT_CLEAR_CREATE_APP_STATUS = 'NEW_PROJECT_CLEAR_CREATE_APP_STATUS';
10 |
--------------------------------------------------------------------------------
/src/features/new-project/redux/clearCreateAppStatus.js:
--------------------------------------------------------------------------------
1 | // Rekit uses a new approach to organizing actions and reducers. That is
2 | // putting related actions and reducers in one file. See more at:
3 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da
4 |
5 | import {
6 | NEW_PROJECT_CLEAR_CREATE_APP_STATUS,
7 | } from './constants';
8 |
9 | export function clearCreateAppStatus() {
10 | return {
11 | type: NEW_PROJECT_CLEAR_CREATE_APP_STATUS,
12 | };
13 | }
14 |
15 | export function reducer(state, action) {
16 | switch (action.type) {
17 | case NEW_PROJECT_CLEAR_CREATE_APP_STATUS:
18 | return {
19 | ...state,
20 | createAppStatus: [],
21 | createAppPending: false,
22 | createAppError: null,
23 | };
24 |
25 | default:
26 | return state;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 | const jest = require('jest');
19 | const argv = process.argv.slice(2);
20 |
21 | // Watch unless on CI or in coverage mode
22 | if (!process.env.CI && argv.indexOf('--coverage') < 0 && argv.indexOf('--no-watch') < 0) {
23 | argv.push('--watch');
24 | }
25 |
26 | jest.run(argv);
27 |
--------------------------------------------------------------------------------
/src/features/new-project/redux/initialState.js:
--------------------------------------------------------------------------------
1 | // Initial state is the place you define all initial values for the Redux store of the feature.
2 | // In the 'standard' way, initialState is defined in reducers: http://redux.js.org/docs/basics/Reducers.html
3 | // But when application grows, there will be multiple reducers files, it's not intuitive what data is managed by the whole store.
4 | // So Rekit extracts the initial state definition into a separate module so that you can have
5 | // a quick view about what data is used for the feature, at any time.
6 |
7 | // NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions.
8 | const initialState = {
9 | fetchAppTypesPending: false,
10 | fetchAppTypesError: null,
11 | appTypes: null,
12 | createAppPending: false,
13 | createAppError: null,
14 | createAppStatus: [],
15 | };
16 |
17 | export default initialState;
18 |
--------------------------------------------------------------------------------
/tests/features/home/redux/showWelcomePage.test.js:
--------------------------------------------------------------------------------
1 | import {
2 | HOME_SHOW_WELCOME_PAGE,
3 | } from '../../../../src/features/home/redux/constants';
4 |
5 | import {
6 | showWelcomePage,
7 | reducer,
8 | } from '../../../../src/features/home/redux/showWelcomePage';
9 |
10 | describe('home/redux/showWelcomePage', () => {
11 | it('returns correct action by showWelcomePage', () => {
12 | expect(showWelcomePage()).toHaveProperty('type', HOME_SHOW_WELCOME_PAGE);
13 | });
14 |
15 | it('handles action type HOME_SHOW_WELCOME_PAGE correctly', () => {
16 | const prevState = {};
17 | const state = reducer(
18 | prevState,
19 | { type: HOME_SHOW_WELCOME_PAGE }
20 | );
21 | // Should be immutable
22 | expect(state).not.toBe(prevState);
23 |
24 | // TODO: use real case expected value instead of {}.
25 | const expectedState = {};
26 | expect(state).toEqual(expectedState);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (typeof Promise === 'undefined') {
4 | // Rejection tracking prevents a common issue where React gets into an
5 | // inconsistent state due to an error, but it gets swallowed by a Promise,
6 | // and the user has no idea what causes React's erratic future behavior.
7 | require('promise/lib/rejection-tracking').enable();
8 | window.Promise = require('promise/lib/es6-extensions.js');
9 | }
10 |
11 | // fetch() polyfill for making API calls.
12 | require('whatwg-fetch');
13 |
14 | // Object.assign() is commonly used with React.
15 | // It will use the native implementation if it's present and isn't buggy.
16 | Object.assign = require('object-assign');
17 |
18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
19 | // We don't polyfill it in the browser--this is user's responsibility.
20 | if (process.env.NODE_ENV === 'test') {
21 | require('raf').polyfill(global);
22 | }
23 |
--------------------------------------------------------------------------------
/src/features/common/WebView.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | export default class WebView extends Component {
5 | static propTypes = {
6 | src: PropTypes.string.isRequired,
7 | visible: PropTypes.bool.isRequired,
8 | onload: PropTypes.func,
9 | };
10 |
11 | static defaultProps = {
12 | onload() {},
13 | };
14 | componentDidMount() {
15 | const wv = document.createElement('webview');
16 | this.node.appendChild(wv);
17 | wv.src = this.props.src;
18 | // wv.onload = this.props.onload;
19 | wv.addEventListener('did-finish-load', this.props.onload);
20 | }
21 |
22 | assignRef = node => {
23 | this.node = node;
24 | };
25 |
26 | render() {
27 | return (
28 |
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/common/rootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 | import homeReducer from '../features/home/redux/reducer';
4 | import commonReducer from '../features/common/redux/reducer';
5 | import newProjectReducer from '../features/new-project/redux/reducer';
6 | import pluginManagerReducer from '../features/plugin-manager/redux/reducer';
7 |
8 | // NOTE 1: DO NOT CHANGE the 'reducerMap' name and the declaration pattern.
9 | // This is used for Rekit cmds to register new features, remove features, etc.
10 | // NOTE 2: always use the camel case of the feature folder name as the store branch name
11 | // So that it's easy for others to understand it and Rekit could manage them.
12 |
13 | const reducerMap = {
14 | router: routerReducer,
15 | home: homeReducer,
16 | common: commonReducer,
17 | newProject: newProjectReducer,
18 | pluginManager: pluginManagerReducer,
19 | };
20 |
21 | export default combineReducers(reducerMap);
22 |
--------------------------------------------------------------------------------
/src/features/common/redux/reducer.js:
--------------------------------------------------------------------------------
1 | // This is the root reducer of the feature. It is used for:
2 | // 1. Load reducers from each action in the feature and process them one by one.
3 | // Note that this part of code is mainly maintained by Rekit, you usually don't need to edit them.
4 | // 2. Write cross-topic reducers. If a reducer is not bound to some specific action.
5 | // Then it could be written here.
6 | // Learn more from the introduction of this approach:
7 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da.
8 |
9 | import initialState from './initialState';
10 |
11 | const reducers = [
12 | ];
13 |
14 | export default function reducer(state = initialState, action) {
15 | let newState;
16 | switch (action.type) {
17 | // Handle cross-topic actions here
18 | default:
19 | newState = state;
20 | break;
21 | }
22 | /* istanbul ignore next */
23 | return reducers.reduce((s, r) => r(s, action), newState);
24 | }
25 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | React App
8 |
9 |
10 |
13 |
14 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/node/utils.js:
--------------------------------------------------------------------------------
1 | const { BrowserWindow } = require('electron');
2 | const logger = require('./logger');
3 |
4 | function toggleWindowMaximize() {
5 | const win = BrowserWindow.getFocusedWindow();
6 | if (!win) return;
7 | if (win.isMaximized()) win.unmaximize();
8 | else win.maximize();
9 | }
10 |
11 | module.exports = {
12 | toggleWindowMaximize,
13 | notifyMainStateChange() {
14 | const win = BrowserWindow.getAllWindows()[0];
15 | if (win) {
16 | win.webContents.send('state-changed');
17 | } else {
18 | logger.warn('No window found when notifiyMainStateChange in utils.js, retry in 2 seconds...');
19 | if (!this.pending)
20 | this.pending = setTimeout(() => {
21 | delete this.pending;
22 | this.notifyMainStateChange();
23 | }, 2000);
24 | }
25 | },
26 | reduxAction(action) {
27 | const win = BrowserWindow.getAllWindows()[0];
28 | if (win) {
29 | win.webContents.send('redux-action', action);
30 | }
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginHeader.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Row, Col } from 'antd';
3 | import PropTypes from 'prop-types';
4 | import { PluginIcon, InstallButton } from './';
5 |
6 | export default class PluginHeader extends Component {
7 | static propTypes = {
8 | item: PropTypes.object.isRequired,
9 | };
10 |
11 | render() {
12 | const { item } = this.props;
13 | return (
14 |
15 |
16 |
17 |
18 |
{item.description}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Root.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow } from 'enzyme';
3 | import configStore from '../src/common/configStore';
4 | import Root from '../src/Root';
5 |
6 | describe('Root', () => {
7 | it('Root has no error', () => {
8 | const DumpContainer = () => {this.props.children}
;
9 | const NotFoundComp = () => Not found
;
10 | const routes = [{
11 | childRoutes: [
12 | { path: '/', component: DumpContainer, childRoutes: [{ path: 'abc' }] },
13 | { path: '/root', autoIndexRoute: true },
14 | { path: 'relative-path', name: 'Link Name' },
15 | {
16 | path: 'sub-links',
17 | childRoutes: [
18 | { path: 'sub-link' },
19 | ],
20 | },
21 | { path: '*', component: NotFoundComp },
22 | ],
23 | }];
24 | const store = configStore();
25 |
26 | shallow(
27 |
28 | );
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/tests/features/new-project/redux/clearCreateAppStatus.test.js:
--------------------------------------------------------------------------------
1 | import {
2 | NEW_PROJECT_CLEAR_CREATE_APP_STATUS,
3 | } from '../../../../src/features/new-project/redux/constants';
4 |
5 | import {
6 | clearCreateAppStatus,
7 | reducer,
8 | } from '../../../../src/features/new-project/redux/clearCreateAppStatus';
9 |
10 | describe('new-project/redux/clearCreateAppStatus', () => {
11 | it('returns correct action by clearCreateAppStatus', () => {
12 | expect(clearCreateAppStatus()).toHaveProperty('type', NEW_PROJECT_CLEAR_CREATE_APP_STATUS);
13 | });
14 |
15 | it('handles action type NEW_PROJECT_CLEAR_CREATE_APP_STATUS correctly', () => {
16 | const prevState = {};
17 | const state = reducer(
18 | prevState,
19 | { type: NEW_PROJECT_CLEAR_CREATE_APP_STATUS }
20 | );
21 | // Should be immutable
22 | expect(state).not.toBe(prevState);
23 |
24 | // TODO: use real case expected value instead of {}.
25 | const expectedState = {};
26 | expect(state).toEqual(expectedState);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/src/features/new-project/ProjectProperties.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { bindActionCreators } from 'redux';
4 | import { connect } from 'react-redux';
5 | import * as actions from './redux/actions';
6 |
7 | export class ProjectProperties extends Component {
8 | static propTypes = {
9 | newProject: PropTypes.object.isRequired,
10 | actions: PropTypes.object.isRequired,
11 | };
12 |
13 | render() {
14 | return (
15 |
16 | Page Content: new-project/ProjectProperties2
17 |
18 | );
19 | }
20 | }
21 |
22 | /* istanbul ignore next */
23 | function mapStateToProps(state) {
24 | return {
25 | newProject: state.newProject,
26 | };
27 | }
28 |
29 | /* istanbul ignore next */
30 | function mapDispatchToProps(dispatch) {
31 | return {
32 | actions: bindActionCreators({ ...actions }, dispatch)
33 | };
34 | }
35 |
36 | export default connect(
37 | mapStateToProps,
38 | mapDispatchToProps
39 | )(ProjectProperties);
40 |
--------------------------------------------------------------------------------
/src/styles/mixins.less:
--------------------------------------------------------------------------------
1 | @primary-color: #1890ff;
2 | @header-height: 22px;
3 | @red2: rgb(242, 119, 119);
4 | @green2: rgb(93, 167, 0);
5 | .scroll-style() {
6 | &::-webkit-scrollbar {
7 | width: 0.5rem;
8 | height: 0.5rem;
9 | }
10 |
11 | &::-webkit-scrollbar-thumb {
12 | transition: all 0.3s ease;
13 | border-color: transparent;
14 | background-color: hsla(0, 0%, 100%, 0.1);
15 | z-index: 40;
16 | }
17 |
18 | &::-webkit-scrollbar-corner {
19 | background-color: rgba(0, 0, 0, 0);
20 | }
21 | }
22 |
23 | .abs-fullsize {
24 | position: absolute;
25 | left: 0;
26 | right: 0;
27 | top: 0;
28 | bottom: 0;
29 | }
30 |
31 | .vertical-center {
32 | position: relative;
33 | top: 50%;
34 | transform: translateY(-50%);
35 | }
36 |
37 | .full-page {
38 | .abs-fullsize;
39 | top: @header-height;
40 | }
41 |
42 | .spin {
43 | animation-name: spin;
44 | animation-duration: 2000ms;
45 | animation-iteration-count: infinite;
46 | animation-timing-function: linear;
47 | }
48 |
49 | @keyframes spin {
50 | from {
51 | transform: rotate(0deg);
52 | }
53 | to {
54 | transform: rotate(360deg);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { AppContainer } from 'react-hot-loader';
3 | import { render } from 'react-dom';
4 | // import configStore from './common/configStore';
5 | import store from './common/store';
6 |
7 | import routeConfig from './common/routeConfig';
8 | import Root from './Root';
9 |
10 | function renderApp(app) {
11 | render({app}, document.getElementById('root'));
12 | }
13 |
14 | renderApp();
15 |
16 | window.bridge.ipcRenderer.on('redux-action', (evt, action) => store.getStore().dispatch(action));
17 |
18 | // Hot Module Replacement API
19 | /* istanbul ignore if */
20 | if (module.hot) {
21 | module.hot.accept('./common/routeConfig', () => {
22 | const nextRouteConfig = require('./common/routeConfig').default; // eslint-disable-line
23 | renderApp();
24 | });
25 | module.hot.accept('./Root', () => {
26 | const nextRoot = require('./Root').default; // eslint-disable-line
27 | renderApp();
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/src/features/home/NewProjectDialog.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .home-new-project-dialog {
4 | .ant-modal-body {
5 | height: 500px;
6 | text-align:justify;
7 | position: relative;
8 | }
9 |
10 | h2 {
11 | margin-bottom: 20px;
12 | }
13 | .icon-block-container {
14 | overflow: auto;
15 | }
16 | .icon-block {
17 | display: inline-block;
18 | margin-top: 10px;
19 | text-align: center;
20 | width: 107px;
21 | padding-top: 15px;
22 | height: 100px;
23 | cursor: pointer;
24 | &:hover {
25 | background-color: #eee;
26 | }
27 | .anticon {
28 | font-size: 48px;
29 | }
30 | label {
31 | display: block;
32 | line-height: 40px;
33 | cursor: pointer;
34 | }
35 |
36 | &.selected {
37 | background-color: @primary-color;
38 | color: #fff;
39 | }
40 | }
41 | .description {
42 | border-top: 1px solid #ddd;
43 | position: absolute;
44 | bottom: 0px;
45 | left: 0px;
46 | right: 0px;
47 | overflow: auto;
48 | padding: 10px 20px;
49 | height: 100px;
50 | background-color: #f5f5f5;
51 | line-height: 150%;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/features/common/FolderPicker.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Input, Icon } from 'antd';
4 |
5 | export default class FolderPicker extends Component {
6 | static propTypes = {
7 | value: PropTypes.string,
8 | onChange: PropTypes.func,
9 | };
10 |
11 | static defaultProps = {
12 | value: '',
13 | onChange() {},
14 | };
15 |
16 | handleBrowse = () => {
17 | window.bridge.remote.dialog.showOpenDialog(
18 | {
19 | title: 'Select a folder',
20 | filters: [],
21 | properties: ['openDirectory'],
22 | },
23 | folders => {
24 | if (!folders) return; // canceled
25 | this.props.onChange(folders[0]);
26 | },
27 | );
28 | };
29 |
30 | render() {
31 | const addonAfter = (
32 |
33 | );
34 |
35 | return (
36 | this.props.onChange(evt.target.value)}
41 | />
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/redux/initialState.js:
--------------------------------------------------------------------------------
1 | // Initial state is the place you define all initial values for the Redux store of the feature.
2 | // In the 'standard' way, initialState is defined in reducers: http://redux.js.org/docs/basics/Reducers.html
3 | // But when application grows, there will be multiple reducers files, it's not intuitive what data is managed by the whole store.
4 | // So Rekit extracts the initial state definition into a separate module so that you can have
5 | // a quick view about what data is used for the feature, at any time.
6 |
7 | // NOTE: initialState constant is necessary so that Rekit could auto add initial state when creating async actions.
8 | const initialState = {
9 | fetchInstalledPluginsPending: false,
10 | fetchInstalledPluginsError: null,
11 | enablePluginPending: false,
12 | enablePluginError: null,
13 | disablePluginPending: false,
14 | disablePluginError: null,
15 | installPluginPending: false,
16 | installPluginError: null,
17 | uninstallPluginPending: false,
18 | uninstallPluginError: null,
19 | plugins: [],
20 | onlinePlugins: [],
21 | installing: {},
22 | uninstalling: {},
23 | fetchOnlinePluginsPending: false,
24 | fetchOnlinePluginsError: null,
25 | };
26 |
27 | export default initialState;
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | .DS_Store
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 | /build
35 | dist
36 | build
37 |
38 | # Dependency directories
39 | node_modules/
40 | jspm_packages/
41 |
42 | # TypeScript v1 declaration files
43 | typings/
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # dotenv environment variables file
61 | .env
62 |
63 | # next.js build output
64 | .next
65 |
66 | # electron env
67 | electron-builder.env
--------------------------------------------------------------------------------
/scripts/startRekitStudio.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Start Rekit Studio
4 | const path = require('path');
5 | const http = require('http');
6 | const express = require('express');
7 | const rekitStudioMiddleWare = require('rekit-studio/middleware');
8 | const fallback = require('express-history-api-fallback');
9 |
10 | function startRekitStudio(port) {
11 | console.log('Starting Rekit Studio...');
12 | return new Promise((resolve, reject) => {
13 | const app = express();
14 | const server = http.createServer(app);
15 | const root = path.join(__dirname, '../node_modules/rekit-studio/dist');
16 | app.use(rekitStudioMiddleWare()(server, app));
17 | app.use(express.static(root));
18 | app.use(fallback('index.html', { root }));
19 |
20 | // Other files should not happen, respond 404
21 | app.get('*', (req, res) => {
22 | console.log('Warning: unknown req: ', req.path);
23 | res.sendStatus(404);
24 | });
25 |
26 | server.listen(port, err => {
27 | if (err) {
28 | console.error(err);
29 | reject(err);
30 | return;
31 | }
32 | console.log(`Rekit Studio is running at http://localhost:${port}/`);
33 | resolve();
34 | });
35 | });
36 | }
37 |
38 | module.exports = startRekitStudio;
39 |
--------------------------------------------------------------------------------
/src/features/new-project/AppTypeSelect.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .new-project-app-type-select {
4 | h2 {
5 | margin-bottom: 20px;
6 | }
7 | .icon-block-container {
8 | overflow: auto;
9 | }
10 | .icon-block {
11 | display: inline-block;
12 | margin-bottom: 10px;
13 | text-align: center;
14 | width: 100px;
15 | margin-right: 7px;
16 | padding-top: 15px;
17 | height: 100px;
18 | cursor: pointer;
19 | &:hover {
20 | background-color: #eee;
21 | }
22 | img {
23 | display: inline-block;
24 | width: 48px;
25 | height: 48px;
26 | }
27 | label {
28 | display: block;
29 | line-height: 40px;
30 | white-space: nowrap;
31 | overflow: hidden;
32 | text-overflow: ellipsis;
33 | cursor: pointer;
34 | }
35 |
36 | &.selected {
37 | background-color: #e7e7e7;
38 | // color: #fff;
39 | // label {
40 | // background-color: @primary-color;
41 | // }
42 | }
43 | }
44 | .description {
45 | border-top: 1px solid #eee;
46 | position: absolute;
47 | bottom: 0px;
48 | left: 0px;
49 | right: 0px;
50 | overflow: auto;
51 | padding: 10px 20px;
52 | height: 100px;
53 | background-color: #f5f5f5;
54 | line-height: 150%;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/features/home/redux/reducer.js:
--------------------------------------------------------------------------------
1 | import initialState from './initialState';
2 | import { reducer as openProjectReducer } from './openProject';
3 | import { reducer as closeProjectReducer } from './closeProject';
4 | import { reducer as getInitialStateReducer } from './getInitialState';
5 | import { reducer as getMainStateReducer } from './getMainState';
6 | import { reducer as showNewProjectDialogReducer } from './showNewProjectDialog';
7 | import { reducer as hideNewProjectDialogReducer } from './hideNewProjectDialog';
8 | import { reducer as showWelcomePageReducer } from './showWelcomePage';
9 |
10 | const reducers = [
11 | openProjectReducer,
12 | closeProjectReducer,
13 | getInitialStateReducer,
14 | getMainStateReducer,
15 | showNewProjectDialogReducer,
16 | hideNewProjectDialogReducer,
17 | showWelcomePageReducer,
18 | ];
19 |
20 | export default function reducer(state = initialState, action) {
21 | let newState = state;
22 | switch (action.type) {
23 | // Handle cross-topic actions here
24 | case 'CREATE_APP_STATUS':
25 | break;
26 | case 'CREATE_APP_SUCCESS':
27 | break;
28 | case 'CREATE_APP_FAILURE':
29 | break;
30 | default:
31 | newState = state;
32 | break;
33 | }
34 | /* istanbul ignore next */
35 | return reducers.reduce((s, r) => r(s, action), newState);
36 | }
37 |
--------------------------------------------------------------------------------
/src/common/configStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { routerMiddleware } from 'react-router-redux';
4 | import history from './history';
5 | import rootReducer from './rootReducer';
6 |
7 | const router = routerMiddleware(history);
8 |
9 | // NOTE: Do not change middleares delaration pattern since rekit plugins may register middlewares to it.
10 | const middlewares = [
11 | thunk,
12 | router,
13 | ];
14 |
15 | let devToolsExtension = f => f;
16 |
17 | /* istanbul ignore if */
18 | if (process.env.NODE_ENV === 'development') {
19 | const { createLogger } = require('redux-logger');
20 |
21 | const logger = createLogger({ collapsed: true });
22 | middlewares.push(logger);
23 |
24 | if (window.devToolsExtension) {
25 | devToolsExtension = window.devToolsExtension();
26 | }
27 | }
28 |
29 | export default function configureStore(initialState) {
30 | const store = createStore(rootReducer, initialState, compose(
31 | applyMiddleware(...middlewares),
32 | devToolsExtension
33 | ));
34 |
35 | /* istanbul ignore if */
36 | if (module.hot) {
37 | // Enable Webpack hot module replacement for reducers
38 | module.hot.accept('./rootReducer', () => {
39 | const nextRootReducer = require('./rootReducer').default; // eslint-disable-line
40 | store.replaceReducer(nextRootReducer);
41 | });
42 | }
43 | return store;
44 | }
45 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/InstallButton.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .plugin-manager-install-button {
4 | &.status-installing {
5 | display: inline-block;
6 | background-color: @green2;
7 | border-radius: 2px;
8 | font-size: 10px;
9 | padding: 0 3px;
10 | color: #fff;
11 | .anticon {
12 | font-size: 9px;
13 | margin-right: 5px;
14 | }
15 | cursor: default;
16 | }
17 | &.status-uninstalling {
18 | display: inline-block;
19 | background-color: #444;
20 | border-radius: 2px;
21 | font-size: 10px;
22 | padding: 0 3px;
23 | color: #bbb;
24 | .anticon {
25 | font-size: 9px;
26 | margin-right: 5px;
27 | }
28 | cursor: default;
29 | }
30 | .ant-btn {
31 | border: none;
32 | background-color: @green2;
33 | font-size: 10px;
34 | color: #fff;
35 | padding: 0 3px;
36 | line-height: 14px;
37 | height: 14px;
38 | border-radius: 2px;
39 | transition: none;
40 | &:hover {
41 | background-color: darken(@green2, 5%);
42 | color: #fff;
43 | }
44 | }
45 |
46 | .btn-installed {
47 | color: #bbb;
48 | background-color: #444;
49 |
50 | &:hover {
51 | background-color: #454545;
52 | color: #eee;
53 | }
54 | .anticon {
55 | font-size: 10px;
56 | }
57 | }
58 |
59 | .btn-to-update {
60 | background-color: #2175bc;
61 | color: #fff;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/node/checkUpdate.js:
--------------------------------------------------------------------------------
1 | const { dialog, app } = require('electron');
2 | const { autoUpdater } = require('electron-updater');
3 | const logger = require('./logger');
4 | autoUpdater.logger = logger;
5 |
6 | module.exports = {
7 | checkUpdate() {
8 | autoUpdater.checkForUpdatesAndNotify();
9 | },
10 | handleCheckMenuClick(menuItem) {
11 | menuItem.enabled = false;
12 | // menuItem.label = 'Checking for Updates...';
13 | autoUpdater.checkForUpdatesAndNotify().then(
14 | (args) => {
15 | const updateInfo = (args && args.updateInfo) || null;
16 | logger.info('update info:', updateInfo);
17 | menuItem.enabled = true;
18 | if (updateInfo && updateInfo.version !== app.getVersion()) {
19 | dialog.showMessageBox({
20 | type: 'info',
21 | title: 'Found Updates',
22 | message: "Found updates, downloading behind and will notify you when it's ready.",
23 | });
24 | } else {
25 | dialog.showMessageBox({
26 | type: 'info',
27 | title: 'Up to date',
28 | message: 'There are currently no updates available.',
29 | });
30 | }
31 | },
32 | err => {
33 | logger.warn('Failed to check update.');
34 | dialog.showMessageBox({
35 | title: 'Error',
36 | message: 'Failed to check update.',
37 | });
38 | menuItem.enabled = true;
39 | },
40 | );
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/src/features/home/RekitStudioPage.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .home-rekit-studio-page {
4 | .full-page;
5 | z-index: 9;
6 | .not-found {
7 | .abs-fullsize;
8 | color: #aaa;
9 | padding: 20px;
10 | }
11 | &.hidden {
12 | display: none;
13 | }
14 | .wv-container {
15 | width: 100%;
16 | height: 100%;
17 | .common-web-view {
18 | .abs-fullsize;
19 | }
20 | }
21 |
22 | .loading-status {
23 | .abs-fullsize;
24 | z-index: 10;
25 | text-align: center;
26 | background-color: #333;
27 |
28 | .center-block {
29 | .vertical-center();
30 | }
31 | img {
32 | width: 60px;
33 | .spin;
34 | }
35 | p {
36 | color: #ccc;
37 | margin-top: 30px;
38 | }
39 | }
40 |
41 | .error-message {
42 | .abs-fullsize;
43 | z-index: 10;
44 | background-color: #333;
45 | text-align: left;
46 |
47 | .center-block {
48 | // .vertical-center();
49 | padding: 30px;
50 | }
51 | h2 {
52 | color: red;
53 | }
54 | ul, li {
55 | margin: 0;
56 | padding: 0;
57 | list-style: none;
58 | }
59 | li {
60 | color: red;
61 | }
62 | .buttons {
63 | margin-top: 20px;
64 | .ant-btn {
65 | margin-right: 20px;
66 | }
67 |
68 | .btn-close {
69 | background-color: rgba(255, 255, 255, 0.1);
70 | border-color: rgba(255, 255, 255, 0.1);
71 | color: #ccc;
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/features/home/redux/constants.js:
--------------------------------------------------------------------------------
1 | export const HOME_OPEN_PROJECT_BEGIN = 'HOME_OPEN_PROJECT_BEGIN';
2 | export const HOME_OPEN_PROJECT_SUCCESS = 'HOME_OPEN_PROJECT_SUCCESS';
3 | export const HOME_OPEN_PROJECT_FAILURE = 'HOME_OPEN_PROJECT_FAILURE';
4 | export const HOME_OPEN_PROJECT_DISMISS_ERROR = 'HOME_OPEN_PROJECT_DISMISS_ERROR';
5 | export const HOME_CLOSE_PROJECT_BEGIN = 'HOME_CLOSE_PROJECT_BEGIN';
6 | export const HOME_CLOSE_PROJECT_SUCCESS = 'HOME_CLOSE_PROJECT_SUCCESS';
7 | export const HOME_CLOSE_PROJECT_FAILURE = 'HOME_CLOSE_PROJECT_FAILURE';
8 | export const HOME_CLOSE_PROJECT_DISMISS_ERROR = 'HOME_CLOSE_PROJECT_DISMISS_ERROR';
9 | export const HOME_GET_INITIAL_STATE_BEGIN = 'HOME_GET_INITIAL_STATE_BEGIN';
10 | export const HOME_GET_INITIAL_STATE_SUCCESS = 'HOME_GET_INITIAL_STATE_SUCCESS';
11 | export const HOME_GET_INITIAL_STATE_FAILURE = 'HOME_GET_INITIAL_STATE_FAILURE';
12 | export const HOME_GET_INITIAL_STATE_DISMISS_ERROR = 'HOME_GET_INITIAL_STATE_DISMISS_ERROR';
13 | export const HOME_GET_MAIN_STATE_BEGIN = 'HOME_GET_MAIN_STATE_BEGIN';
14 | export const HOME_GET_MAIN_STATE_SUCCESS = 'HOME_GET_MAIN_STATE_SUCCESS';
15 | export const HOME_GET_MAIN_STATE_FAILURE = 'HOME_GET_MAIN_STATE_FAILURE';
16 | export const HOME_GET_MAIN_STATE_DISMISS_ERROR = 'HOME_GET_MAIN_STATE_DISMISS_ERROR';
17 | export const HOME_SHOW_NEW_PROJECT_DIALOG = 'HOME_SHOW_NEW_PROJECT_DIALOG';
18 | export const HOME_HIDE_NEW_PROJECT_DIALOG = 'HOME_HIDE_NEW_PROJECT_DIALOG';
19 | export const HOME_SHOW_WELCOME_PAGE = 'HOME_SHOW_WELCOME_PAGE';
20 |
--------------------------------------------------------------------------------
/node/init.js:
--------------------------------------------------------------------------------
1 | // Initializing Rekit environment if not set up.
2 | // 1. Copying built in plugins to ~/.rekit/plugins folder.
3 | // NOTE: this may not needeed since rekit-react plugin is packaged in rekit-studio
4 |
5 | const os = require('os');
6 | const fs = require('fs-extra');
7 | const path = require('path');
8 | const log = require('./log');
9 |
10 | const systemPluginDir = path.join(os.homedir(), '.rekit/plugins');
11 | fs.ensureDirSync(systemPluginDir);
12 |
13 | const builtInPlugins = [];
14 |
15 | // Use read and write to copy files to avoid permission issue, don't know why
16 | function copy(src, dest) {
17 | if (fs.statSync(src).isFile()) {
18 | fs.writeFileSync(dest, fs.readFileSync(src));
19 | } else if (fs.statSync(src).isDirectory()) {
20 | fs.ensureDirSync(dest);
21 | fs.readdirSync(src).forEach(fileOrFolderName => {
22 | copy(path.join(src, fileOrFolderName), path.join(dest, fileOrFolderName));
23 | });
24 | }
25 | }
26 |
27 | builtInPlugins.forEach(name => {
28 | if (1 || !fs.existsSync(path.join(systemPluginDir, name))) {
29 | // Always replace built-in plugins when started for beta release.
30 | log.info('Initializing built in plugin: ', name);
31 | const src = path.join(__dirname, '../build/plugins', name);
32 | if (fs.existsSync(src)) {
33 | const dest = path.join(systemPluginDir, name);
34 | copy(src, dest);
35 | } else {
36 | log.error('Built in plugin not found: ', name, src);
37 | }
38 | } else {
39 | log.info('Initial plugin exists: ', name);
40 | }
41 | });
42 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/MainPage.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .plugin-manager-main-page {
4 | color: #ccc;
5 | height: 100%;
6 | h2 {
7 | font-size: 16px;
8 | color: #ccc;
9 | line-height: 40px;
10 | padding-left: 15px;
11 | .plugin-count {
12 | color: #777;
13 | font-size: 12px;
14 | }
15 | }
16 | .main-area {
17 | padding-top: 1px;
18 | margin: 0 auto;
19 | .vertical-center();
20 | height: 600px;
21 | width: 900px;
22 | background-color: #222;
23 | .ant-row, .ant-col{
24 | height: 100%;
25 | }
26 | .plugin-manager-sider {
27 | border-right: 1px solid #111;
28 | }
29 | }
30 | .plugin-manager-header {
31 | height: 40px;
32 | margin-top: -40px;
33 | text-align: right;
34 | .ant-btn {
35 | cursor: pointer;
36 | margin-right: -15px;
37 | background: #222;
38 | border:none;
39 | margin-top: 22px;
40 | color: #999;
41 | transition: none;
42 | &:hover {
43 | color:#ddd;
44 | }
45 | }
46 | }
47 |
48 | .plugin-intro {
49 | padding: 15px 20px 20px 20px;
50 | color: #bbb;
51 | h2 {
52 | border-bottom: 1px solid #333;
53 | padding-left: 0;
54 | }
55 | p {
56 | color: #aaa;
57 | }
58 | }
59 | .ant-row {
60 | // position: absolute;
61 | // left: 30px;
62 | // top: 30px;
63 | // right: 30px;
64 | // bottom: 30px;
65 | }
66 |
67 | .ant-col-12 {
68 | // height: 100%;
69 | // position: relative;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginHeader.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .plugin-manager-plugin-header {
4 | position: relative;
5 | padding: 5px 5px 5px 65px;
6 | border-bottom: 1px solid #333;
7 | padding-bottom: 10px;
8 | margin-bottom: 10px;
9 | .plugin-logo {
10 | position: absolute;
11 | left: 10px;
12 | top: 10px;
13 | width: 40px;
14 | max-height: 40px;
15 | }
16 | .name {
17 | overflow: hidden;
18 | text-overflow: ellipsis;
19 | display: inline-block;
20 | max-width: 200px;
21 | white-space: nowrap;
22 | font-size: 16px;
23 | vertical-align: bottom;
24 | }
25 | .version {
26 | display: inline-block;
27 | margin-left: 5px;
28 | font-size: 10px;
29 | color: #999;
30 | }
31 | p {
32 | color: #999;
33 | font-size: 12px;
34 | margin-bottom: 0;
35 | white-space: nowrap;
36 | overflow: hidden;
37 | text-overflow: ellipsis;
38 | }
39 | .plugin-manager-install-button {
40 | font-size: 12px;
41 | .ant-btn {
42 | font-size: 12px;
43 | padding: 2px 5px;
44 | height: auto;
45 | }
46 | }
47 | .ant-col {
48 | font-size: 12px;
49 | .author {
50 | overflow: hidden;
51 | max-width: 100%;
52 | text-overflow: ellipsis;
53 | }
54 | // .ant-btn {
55 | // border: none;
56 | // background-color: @green2;
57 | // font-size: 12px;
58 | // color: #fff;
59 | // padding: 0 3px;
60 | // line-height: 18px;
61 | // height: 18px;
62 | // border-radius: 2px;
63 | // }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/common/routeConfig.js:
--------------------------------------------------------------------------------
1 | import { App } from '../features/home';
2 | import { PageNotFound } from '../features/common';
3 | import homeRoute from '../features/home/route';
4 | import commonRoute from '../features/common/route';
5 | import _ from 'lodash';
6 | import newProjectRoute from '../features/new-project/route';
7 | import pluginManagerRoute from '../features/plugin-manager/route';
8 |
9 | // NOTE: DO NOT CHANGE the 'childRoutes' name and the declaration pattern.
10 | // This is used for Rekit cmds to register routes config for new features, and remove config when remove features, etc.
11 | const childRoutes = [
12 | homeRoute,
13 | commonRoute,
14 | newProjectRoute,
15 | pluginManagerRoute,
16 | ];
17 |
18 | const routes = [{
19 | path: '/',
20 | component: App,
21 | childRoutes: [
22 | ...childRoutes,
23 | { path: '*', name: 'Page not found', component: PageNotFound },
24 | ].filter(r => r.component || (r.childRoutes && r.childRoutes.length > 0)),
25 | }];
26 |
27 | // Handle isIndex property of route config:
28 | // Dupicate it and put it as the first route rule.
29 | function handleIndexRoute(route) {
30 | if (!route.childRoutes || !route.childRoutes.length) {
31 | return;
32 | }
33 |
34 | const indexRoute = _.find(route.childRoutes, (child => child.isIndex));
35 | if (indexRoute) {
36 | const first = { ...indexRoute };
37 | first.path = '';
38 | first.exact = true;
39 | first.autoIndexRoute = true; // mark it so that the simple nav won't show it.
40 | route.childRoutes.unshift(first);
41 | }
42 | route.childRoutes.forEach(handleIndexRoute);
43 | }
44 |
45 | routes.forEach(handleIndexRoute);
46 | export default routes;
47 |
--------------------------------------------------------------------------------
/src/styles/global.less:
--------------------------------------------------------------------------------
1 | @import './mixins';
2 |
3 | // Here you put all global css rules.
4 |
5 | /* roboto-100 - latin */
6 | @font-face {
7 | font-family: 'Roboto';
8 | font-style: normal;
9 | font-weight: 100;
10 | src: url('../fonts/roboto-v19-latin-100.eot'); /* IE9 Compat Modes */
11 | src: local('Roboto Thin'), local('Roboto-Thin'),
12 | url('../fonts/roboto-v19-latin-100.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
13 | url('../fonts/roboto-v19-latin-100.woff2') format('woff2'), /* Super Modern Browsers */
14 | url('../fonts/roboto-v19-latin-100.woff') format('woff'), /* Modern Browsers */
15 | url('../fonts/roboto-v19-latin-100.ttf') format('truetype'), /* Safari, Android, iOS */
16 | url('../fonts/roboto-v19-latin-100.svg#Roboto') format('svg'); /* Legacy iOS */
17 | }
18 | /* roboto-300 - latin */
19 | @font-face {
20 | font-family: 'Roboto';
21 | font-style: normal;
22 | font-weight: 300;
23 | src: url('../fonts/roboto-v19-latin-300.eot'); /* IE9 Compat Modes */
24 | src: local('Roboto Light'), local('Roboto-Light'),
25 | url('../fonts/roboto-v19-latin-300.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
26 | url('../fonts/roboto-v19-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
27 | url('../fonts/roboto-v19-latin-300.woff') format('woff'), /* Modern Browsers */
28 | url('../fonts/roboto-v19-latin-300.ttf') format('truetype'), /* Safari, Android, iOS */
29 | url('../fonts/roboto-v19-latin-300.svg#Roboto') format('svg'); /* Legacy iOS */
30 | }
31 |
32 | body {
33 | margin: 0;
34 | padding: 0;
35 | font-family: sans-serif;
36 | background-color: #1e1e1e!important;
37 | }
38 |
--------------------------------------------------------------------------------
/src/features/home/RecentProjects.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .home-recent-projects {
4 | background-color: #222;
5 | height: 400px;
6 | padding-left: 20px;
7 | overflow: hidden;
8 | .scroll-style();
9 |
10 | h2 {
11 | line-height: 44px;
12 | border-bottom: 1px solid #444;
13 | margin-bottom: 0;
14 | margin-right: 20px;
15 | }
16 | ul,
17 | li {
18 | margin: 0;
19 | padding: 0;
20 | list-style: none;
21 | }
22 | ul {
23 | height: 360px;
24 | overflow: auto;
25 | padding-right: 20px;
26 | .scroll-style();
27 | }
28 |
29 | .no-recent {
30 | color: #777;
31 | line-height: 36px;
32 | }
33 |
34 | .row-button {
35 | padding-left: 50px;
36 | margin: 0;
37 | color: #ccc;
38 | height: 60px;
39 | border-bottom: 1px solid #444;
40 | white-space: nowrap;
41 | .anticon {
42 | left: 7px;
43 | top: 15px;
44 | font-size: 32px;
45 | color: #BCAAA4;
46 | opacity: 0.7;
47 | }
48 | img.icon {
49 | width: 32px;
50 | height: 32px;
51 | position: absolute;
52 | left: 7px;
53 | top: 15px;
54 | opacity: 0.7;
55 | }
56 | h4 {
57 | padding-top: 13px;
58 | margin: 0;
59 | line-height: 16px;
60 | color: #ccc;
61 | text-overflow: ellipsis;
62 | overflow: hidden;
63 | font-weight: 300;
64 | }
65 | p {
66 | color: #777;
67 | text-overflow: ellipsis;
68 | overflow: hidden;
69 | }
70 | &:hover {
71 | h4,
72 | p {
73 | color: @primary-color;
74 | }
75 | img.icon {
76 | opacity: 0.9;
77 | }
78 | }
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/features/new-project/NewProjectForm.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Form } from 'antd';
4 | import { FormBuilder, FolderPicker } from '../common';
5 |
6 | export class NewProjectForm extends Component {
7 | static propTypes = {
8 | form: PropTypes.object.isRequired,
9 | onSubmit: PropTypes.func.isRequired,
10 | values: PropTypes.object.isRequired,
11 | appType: PropTypes.object.isRequired,
12 | };
13 |
14 | getMeta() {
15 | const { appType } = this.props;
16 | const values = this.props.values || {};
17 | const meta = {
18 | elements: [
19 |
20 | {
21 | key: 'name',
22 | label: 'Project Name',
23 | required: true,
24 | },
25 | {
26 | key: 'location',
27 | label: 'Location',
28 | required: true,
29 | widget: FolderPicker,
30 | },
31 | ],
32 | };
33 | if (appType.args) {
34 | meta.elements.push(...appType.args);
35 | }
36 | meta.elements.forEach(ele => {
37 | if (ele.key in values) ele.initialValue = values[ele.key];
38 | });
39 |
40 | return meta;
41 | }
42 |
43 | doSubmit = () => {
44 | this.props.form.validateFields((errors, values) => {
45 | if (!errors) {
46 | this.props.onSubmit(values);
47 | }
48 | });
49 | };
50 |
51 | render() {
52 | return (
53 |
54 |
57 |
58 | );
59 | }
60 | }
61 |
62 | export default Form.create()(NewProjectForm);
63 |
--------------------------------------------------------------------------------
/src/features/home/WelcomePage.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .home-welcome-page {
4 | .abs-fullsize;
5 | top: @header-height;
6 | background-color: #1e1e1e;
7 | color: #ccc;
8 | font-family: Roboto, Arial, Helvetica, sans-serif;
9 | font-weight: 300;
10 |
11 | h1,
12 | h2,
13 | h3,
14 | h4 {
15 | color: #ccc;
16 | font-weight: 100;
17 | }
18 |
19 | .main-area {
20 | width: 800px;
21 | height: 400px;
22 | background-color: #191919;
23 | border-radius: 6px;
24 | margin: 0 auto;
25 | .vertical-center;
26 | .rekit-logo {
27 | width: 60px;
28 | }
29 | .footer {
30 | // background-color: red;
31 | // margin-bottom:
32 | line-height: 50px;
33 | text-align: left;
34 | color: #555;
35 | font-size: 12px;
36 | padding-left: 10px;
37 | label,
38 | a {
39 | color: #555;
40 | cursor: pointer;
41 | margin-right: 15px;
42 | &:last-child {
43 | margin-right: 0;
44 | }
45 | &:hover {
46 | color: #888;
47 | }
48 | }
49 | }
50 | }
51 |
52 | .welcome-area {
53 | text-align: center;
54 | padding-top: 50px;
55 | }
56 | p {
57 | margin-bottom: 50px;
58 | }
59 |
60 | .row-button {
61 | position: relative;
62 | padding-left: 20px;
63 | margin: 15px 0 0 110px;
64 | text-align: left;
65 | line-height: 20px;
66 | color: @primary-color;
67 | cursor: pointer;
68 | // background-color: rgba(255, 255, 255, 0.05);
69 | height: 20px;
70 | .anticon {
71 | position: absolute;
72 | font-size: 16px;
73 | color: @primary-color;
74 | left: 0px;
75 | top: 2px;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginList.less:
--------------------------------------------------------------------------------
1 | @import '../../styles/mixins';
2 |
3 | .plugin-manager-plugin-list {
4 | background-color: #262626;
5 | position: absolute;
6 | top: 40px;
7 | left: 0px;
8 | right: 0px;
9 | bottom: 0;
10 |
11 | &.plugins-loading {
12 | padding: 10px;
13 | color: #777;
14 | }
15 | ul {
16 | height: 100%;
17 | overflow: auto;
18 | .scroll-style();
19 | }
20 | ul,
21 | li {
22 | list-style: none;
23 | padding: 0;
24 | margin: 0;
25 | }
26 |
27 | li {
28 | position: relative;
29 | padding: 5px 5px 5px 45px;
30 | border-bottom: 1px solid #333;
31 | &.selected {
32 | background-color: #111;
33 | }
34 | .plugin-logo {
35 | position: absolute;
36 | left: 10px;
37 | top: 10px;
38 | width: 26px;
39 | max-height: 40px;
40 | }
41 | .name {
42 | overflow: hidden;
43 | text-overflow: ellipsis;
44 | display: inline-block;
45 | max-width: 200px;
46 | white-space: nowrap;
47 | font-size: 12px;
48 | vertical-align: bottom;
49 | }
50 | .version {
51 | display: inline-block;
52 | margin-left: 5px;
53 | font-size: 10px;
54 | color: #999;
55 | }
56 | p {
57 | color: #999;
58 | font-size: 12px;
59 | margin-bottom: 0;
60 | white-space: nowrap;
61 | overflow: hidden;
62 | text-overflow: ellipsis;
63 | }
64 |
65 | .ant-col {
66 | font-size: 12px;
67 | .author {
68 | overflow: hidden;
69 | white-space: nowrap;
70 | color:#777;
71 | font-size: 10px;
72 | max-width: 100%;
73 | text-overflow: ellipsis;
74 | }
75 |
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/images/discord.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/PluginDetail.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import _ from 'lodash';
4 | import { bindActionCreators } from 'redux';
5 | import { connect } from 'react-redux';
6 | import * as actions from './redux/actions';
7 | import { PluginHeader } from './';
8 |
9 | export class PluginDetail extends Component {
10 | static propTypes = {
11 | pluginManager: PropTypes.object.isRequired,
12 | actions: PropTypes.object.isRequired,
13 | name: PropTypes.string,
14 | };
15 | renderNotFound() {
16 | return Plugin not found: {this.props.name}.
;
17 | }
18 | renderNotSelected() {
19 | return No plugin selected.
;
20 | }
21 | render() {
22 | const { name, plugins, onlinePlugins } = this.props;
23 | if (!name) return this.renderNotSelected();
24 | const allPlugins = [...plugins, ...onlinePlugins];
25 | const found = _.find(allPlugins, { name });
26 | if (!found) return this.renderNotFound();
27 | return (
28 |
32 | );
33 | }
34 | }
35 |
36 | /* istanbul ignore next */
37 | function mapStateToProps(state) {
38 | return {
39 | onlinePlugins: state.pluginManager.onlinePlugins,
40 | plugins: state.pluginManager.plugins,
41 | pluginManager: state.pluginManager,
42 | };
43 | }
44 |
45 | /* istanbul ignore next */
46 | function mapDispatchToProps(dispatch) {
47 | return {
48 | actions: bindActionCreators({ ...actions }, dispatch),
49 | };
50 | }
51 |
52 | export default connect(
53 | mapStateToProps,
54 | mapDispatchToProps,
55 | )(PluginDetail);
56 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/MainPage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { bindActionCreators } from 'redux';
4 | import { connect } from 'react-redux';
5 | import * as actions from './redux/actions';
6 | import { Row, Col, Button } from 'antd';
7 | import { PluginList, PluginDetail } from './';
8 | import history from '../../common/history';
9 |
10 | export class MainPage extends Component {
11 | static propTypes = {
12 | pluginManager: PropTypes.object.isRequired,
13 | actions: PropTypes.object.isRequired,
14 | match: PropTypes.object.isRequired,
15 | };
16 |
17 | render() {
18 | return (
19 |
20 |
21 |
22 |
24 |
25 |
26 | Plugins
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 | }
38 |
39 | /* istanbul ignore next */
40 | function mapStateToProps(state) {
41 | return {
42 | pluginManager: state.pluginManager,
43 | };
44 | }
45 |
46 | /* istanbul ignore next */
47 | function mapDispatchToProps(dispatch) {
48 | return {
49 | actions: bindActionCreators({ ...actions }, dispatch),
50 | };
51 | }
52 |
53 | export default connect(
54 | mapStateToProps,
55 | mapDispatchToProps,
56 | )(MainPage);
57 |
--------------------------------------------------------------------------------
/src/features/plugin-manager/redux/reducer.js:
--------------------------------------------------------------------------------
1 | // This is the root reducer of the feature. It is used for:
2 | // 1. Load reducers from each action in the feature and process them one by one.
3 | // Note that this part of code is mainly maintained by Rekit, you usually don't need to edit them.
4 | // 2. Write cross-topic reducers. If a reducer is not bound to some specific action.
5 | // Then it could be written here.
6 | // Learn more from the introduction of this approach:
7 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da.
8 |
9 | import initialState from './initialState';
10 | import { reducer as fetchInstalledPluginsReducer } from './fetchInstalledPlugins';
11 | import { reducer as enablePluginReducer } from './enablePlugin';
12 | import { reducer as disablePluginReducer } from './disablePlugin';
13 | import { reducer as installPluginReducer } from './installPlugin';
14 | import { reducer as uninstallPluginReducer } from './uninstallPlugin';
15 | import { reducer as fetchOnlinePluginsReducer } from './fetchOnlinePlugins';
16 |
17 | import { HOME_GET_MAIN_STATE_SUCCESS } from '../../home/redux/constants';
18 |
19 | const reducers = [
20 | fetchInstalledPluginsReducer,
21 | enablePluginReducer,
22 | disablePluginReducer,
23 | installPluginReducer,
24 | uninstallPluginReducer,
25 | fetchOnlinePluginsReducer,
26 | ];
27 |
28 | export default function reducer(state = initialState, action) {
29 | let newState;
30 | switch (action.type) {
31 | // Handle cross-topic actions here
32 | case HOME_GET_MAIN_STATE_SUCCESS:
33 | newState = {
34 | ...state,
35 | installing: action.data.installing,
36 | uninstalling: action.data.uninstalling,
37 | };
38 | break;
39 | default:
40 | newState = state;
41 | break;
42 | }
43 | return reducers.reduce((s, r) => r(s, action), newState);
44 | }
45 |
--------------------------------------------------------------------------------
/src/features/home/RecentProjects.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { bindActionCreators } from 'redux';
4 | import { connect } from 'react-redux';
5 | import * as actions from './redux/actions';
6 | import { Icon } from 'antd';
7 | import utils from './utils';
8 |
9 | export class RecentProjects extends Component {
10 | static propTypes = {
11 | recentProjects: PropTypes.array.isRequired,
12 | actions: PropTypes.object.isRequired,
13 | };
14 |
15 | handleOpenProject = (dir) => {
16 | utils.openProject(dir);
17 | };
18 |
19 | render() {
20 | const { recentProjects } = this.props;
21 | return (
22 |
23 |
Recent Projects
24 |
25 | {recentProjects.map(prj => (
26 | - this.handleOpenProject(prj.path)}
31 | >
32 | {prj.logo ?
: }
33 | {prj.path.split('/').pop()}
34 | {prj.path}
35 |
36 | ))}
37 | {recentProjects.length === 0 && - No recent projects.
}
38 |
39 |
40 | );
41 | }
42 | }
43 |
44 | /* istanbul ignore next */
45 | function mapStateToProps(state) {
46 | return {
47 | recentProjects: state.home.recentProjects,
48 | };
49 | }
50 |
51 | /* istanbul ignore next */
52 | function mapDispatchToProps(dispatch) {
53 | return {
54 | actions: bindActionCreators({ ...actions }, dispatch)
55 | };
56 | }
57 |
58 | export default connect(
59 | mapStateToProps,
60 | mapDispatchToProps
61 | )(RecentProjects);
62 |
--------------------------------------------------------------------------------
/src/features/home/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { bindActionCreators } from 'redux';
4 | import { matchPath } from 'react-router-dom';
5 | import { connect } from 'react-redux';
6 | import { RekitStudioPage, TitleBar, DialogPlace } from './';
7 | import { getMainState } from './redux/actions';
8 |
9 | /*
10 | This is the root component of your app. Here you define the overall layout
11 | and the container of the react router.
12 | You should adjust it according to the requirement of your app.
13 | */
14 | export class App extends Component {
15 | static propTypes = {
16 | children: PropTypes.node,
17 | actions: PropTypes.object.isRequired,
18 | initializing: PropTypes.bool.isRequired,
19 | router: PropTypes.object.isRequired,
20 | };
21 |
22 | static defaultProps = {
23 | children: '',
24 | };
25 |
26 | componentDidMount() {
27 | this.props.actions.getMainState();
28 | window.bridge.ipcRenderer.on('state-changed', () => {
29 | this.props.actions.getMainState();
30 | });
31 | }
32 |
33 | render() {
34 | const match = matchPath(this.props.router.location.pathname, {
35 | path: '/rekit-studio/:port',
36 | exact: true,
37 | });
38 | return (
39 |
40 |
41 |
42 |
43 | {this.props.initializing ? 'Loading...' : this.props.children}
44 |
45 |
46 |
47 | );
48 | }
49 | }
50 |
51 | /* istanbul ignore next */
52 | function mapStateToProps(state) {
53 | return {
54 | initializing: state.home.initializing,
55 | router: state.router,
56 | };
57 | }
58 |
59 | /* istanbul ignore next */
60 | function mapDispatchToProps(dispatch) {
61 | return {
62 | actions: bindActionCreators({ getMainState }, dispatch),
63 | };
64 | }
65 |
66 | export default connect(
67 | mapStateToProps,
68 | mapDispatchToProps,
69 | )(App);
70 |
--------------------------------------------------------------------------------
/src/features/new-project/redux/reducer.js:
--------------------------------------------------------------------------------
1 | // This is the root reducer of the feature. It is used for:
2 | // 1. Load reducers from each action in the feature and process them one by one.
3 | // Note that this part of code is mainly maintained by Rekit, you usually don't need to edit them.
4 | // 2. Write cross-topic reducers. If a reducer is not bound to some specific action.
5 | // Then it could be written here.
6 | // Learn more from the introduction of this approach:
7 | // https://medium.com/@nate_wang/a-new-approach-for-managing-redux-actions-91c26ce8b5da.
8 |
9 | import initialState from './initialState';
10 | import { reducer as fetchAppTypesReducer } from './fetchAppTypes';
11 | import { reducer as createAppReducer } from './createApp';
12 | import { reducer as clearCreateAppStatusReducer } from './clearCreateAppStatus';
13 | import { HOME_GET_MAIN_STATE_SUCCESS } from '../../home/redux/constants';
14 | const reducers = [
15 | fetchAppTypesReducer,
16 | createAppReducer,
17 | clearCreateAppStatusReducer,
18 | ];
19 |
20 | export default function reducer(state = initialState, action) {
21 | let newState = state;
22 | switch (action.type) {
23 | // Handle cross-topic actions here
24 | case 'CREATE_APP_STATUS':
25 | newState = {
26 | ...state,
27 | createAppStatus: [...state.createAppStatus, action.data],
28 | };
29 | break;
30 | case 'CREATE_APP_SUCCESS':
31 | newState = {
32 | ...state,
33 | createAppStatus: [],
34 | createAppPending: false,
35 | };
36 | break;
37 | case 'CREATE_APP_FAILURE':
38 | newState = {
39 | ...state,
40 | createAppStatus: [],
41 | createAppPending: false,
42 | createAppError: action.data,
43 | };
44 | break;
45 | case HOME_GET_MAIN_STATE_SUCCESS:
46 | newState = {
47 | ...state,
48 | appTypes: action.data.appTypes,
49 | };
50 | break;
51 | default:
52 | newState = state;
53 | break;
54 | }
55 | return reducers.reduce((s, r) => r(s, action), newState);
56 | }
57 |
--------------------------------------------------------------------------------
/src/Root.js:
--------------------------------------------------------------------------------
1 | /* This is the Root component mainly initializes Redux and React Router. */
2 |
3 | import React from 'react';
4 | import PropTypes from 'prop-types';
5 | import { Provider } from 'react-redux';
6 | import { Switch, Route } from 'react-router-dom';
7 | import { ConnectedRouter } from 'react-router-redux';
8 | import history from './common/history';
9 |
10 | function renderRouteConfigV3(routes, contextPath) {
11 | // Resolve route config object in React Router v3.
12 | const children = []; // children component list
13 |
14 | const renderRoute = (item, routeContextPath) => {
15 | let newContextPath;
16 | if (/^\//.test(item.path)) {
17 | newContextPath = item.path;
18 | } else {
19 | newContextPath = `${routeContextPath}/${item.path}`;
20 | }
21 | newContextPath = newContextPath.replace(/\/+/g, '/');
22 | if (item.component && item.childRoutes) {
23 | const childRoutes = renderRouteConfigV3(item.childRoutes, newContextPath);
24 | children.push(
25 | {childRoutes}}
28 | path={newContextPath}
29 | />
30 | );
31 | } else if (item.component) {
32 | children.push();
33 | } else if (item.childRoutes) {
34 | item.childRoutes.forEach(r => renderRoute(r, newContextPath));
35 | }
36 | };
37 |
38 | routes.forEach(item => renderRoute(item, contextPath));
39 |
40 | // Use Switch so that only the first matched route is rendered.
41 | return {children};
42 | }
43 |
44 | export default class Root extends React.Component {
45 | static propTypes = {
46 | store: PropTypes.object.isRequired,
47 | routeConfig: PropTypes.array.isRequired,
48 | };
49 | render() {
50 | const children = renderRouteConfigV3(this.props.routeConfig, '/');
51 | return (
52 |
53 | {children}
54 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/features/home/NewProjectDialog.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { bindActionCreators } from 'redux';
4 | import { connect } from 'react-redux';
5 | import { Modal, Icon } from 'antd';
6 | import { hideNewProjectDialog } from './redux/actions';
7 |
8 | export class NewProjectDialog extends Component {
9 | static propTypes = {
10 | home: PropTypes.object.isRequired,
11 | actions: PropTypes.object.isRequired,
12 | };
13 |
14 | state = {
15 | selected: null,
16 | };
17 |
18 | handleSelect = key => {
19 | this.setState({ selected: key });
20 | };
21 |
22 | render() {
23 | return (
24 |
33 | Which type of the project to create?
34 |
38 | {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].map(i => (
39 |
this.handleSelect(i)}
43 | >
44 |
45 |
46 |
47 | ))}
48 |
49 | {this.state.selected && (
50 |
51 | Rekit React template provides feature based SPA boilerplate with React Router, Redux
52 | outof box.
53 |
54 | )}
55 |
56 | );
57 | }
58 | }
59 |
60 | /* istanbul ignore next */
61 | function mapStateToProps(state) {
62 | return {
63 | home: state.home,
64 | };
65 | }
66 |
67 | /* istanbul ignore next */
68 | function mapDispatchToProps(dispatch) {
69 | return {
70 | actions: bindActionCreators({ hideNewProjectDialog }, dispatch),
71 | };
72 | }
73 |
74 | export default connect(
75 | mapStateToProps,
76 | mapDispatchToProps,
77 | )(NewProjectDialog);
78 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`;
20 | } else {
21 | return path;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson => envPublicUrl || require(appPackageJson).homepage;
26 |
27 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
28 | // "public path" at which the app is served.
29 | // Webpack needs to know it to put the right