├── .babelrc
├── .dockerignore
├── .editorconfig
├── .env.tpl
├── .eslintignore
├── .eslintrc
├── .flowconfig
├── .gitignore
├── .i18nGenerator.js
├── .jestfilemock.js
├── .plugins.json
├── .travis.yml
├── .yarnrc
├── Dockerfile
├── LICENSE
├── README.md
├── app
├── CodingSDK.js
├── IDE
│ ├── IdeEnvironment.js
│ └── index.js
├── backendAPI
│ ├── fileAPI.js
│ ├── gitAPI.js
│ ├── index.js
│ ├── packageAPI.js
│ ├── websocketClients.js
│ └── workspaceAPI.js
├── commands
│ ├── CommandPalette
│ │ ├── component.jsx
│ │ ├── getPaletteItems.js
│ │ ├── index.js
│ │ └── items.js
│ ├── commandBindings
│ │ ├── editor.js
│ │ ├── file.js
│ │ ├── git.js
│ │ ├── index.js
│ │ ├── misc.js
│ │ └── tab.js
│ ├── dispatchCommand.js
│ ├── index.js
│ ├── keymaps.js
│ └── lib
│ │ ├── helpers.js
│ │ ├── keycodes.js
│ │ ├── keymapper.js
│ │ └── keymapper2.js
├── commons
│ ├── File
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── state.js
│ │ ├── store.js
│ │ └── subscribeToFileChange.js
│ ├── Menu
│ │ └── state.js
│ ├── Pane
│ │ └── state.js
│ ├── Tab
│ │ ├── TabBar.jsx
│ │ ├── TabContent.jsx
│ │ ├── TabLabel.jsx
│ │ ├── index.js
│ │ └── state.js
│ ├── Tree
│ │ ├── TreeNode.jsx
│ │ ├── index.js
│ │ ├── state.js
│ │ └── store.js
│ ├── WorkSpace
│ │ └── state.js
│ └── exports.js
├── components
│ ├── Accordion
│ │ ├── Accordion.jsx
│ │ └── index.js
│ ├── ContextMenu
│ │ ├── ContextMenu.jsx
│ │ ├── ContextMenuContainer.jsx
│ │ ├── index.js
│ │ ├── state.js
│ │ └── store.js
│ ├── DragAndDrop.jsx
│ ├── Editor
│ │ ├── EditorWrapper.jsx
│ │ ├── actions.js
│ │ ├── codemirrorDefaultOptions.js
│ │ ├── components
│ │ │ ├── CodeEditor
│ │ │ │ ├── BaseCodeEditor.jsx
│ │ │ │ ├── CodeEditor.jsx
│ │ │ │ ├── TablessCodeEditor.jsx
│ │ │ │ ├── addMixinMechanism.jsx
│ │ │ │ ├── addons
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── mode
│ │ │ │ │ │ ├── findMode.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── loadMode.js
│ │ │ │ │ │ └── modeInfos.js
│ │ │ │ ├── index.js
│ │ │ │ ├── lib
│ │ │ │ │ └── formatting.js
│ │ │ │ └── mixins
│ │ │ │ │ ├── basicMixin.jsx
│ │ │ │ │ ├── eslintMixin
│ │ │ │ │ ├── codemirror-eslint.js
│ │ │ │ │ ├── eslintMixin.js
│ │ │ │ │ ├── eslintService.js
│ │ │ │ │ ├── eslintServiceCommandTemplate.js
│ │ │ │ │ └── index.js
│ │ │ │ │ └── gitBlameMixin.jsx
│ │ │ ├── CodeMirrorEditor
│ │ │ │ └── index.jsx
│ │ │ ├── EditorWidgets
│ │ │ │ ├── EditorWidgets.jsx
│ │ │ │ ├── EncodingWidget.jsx
│ │ │ │ ├── LineWidget.jsx
│ │ │ │ ├── LinterWidget.jsx
│ │ │ │ ├── ModeWidget.jsx
│ │ │ │ ├── encodings.js
│ │ │ │ └── index.js
│ │ │ ├── HtmlEditor
│ │ │ │ ├── actions.js
│ │ │ │ ├── htmlMixin.js
│ │ │ │ ├── index.jsx
│ │ │ │ └── state.js
│ │ │ ├── ImageEditor.jsx
│ │ │ ├── MarkdownEditor
│ │ │ │ ├── actions.js
│ │ │ │ ├── index.jsx
│ │ │ │ ├── mdMixin.js
│ │ │ │ ├── reducer.js
│ │ │ │ ├── state.js
│ │ │ │ └── utils.js
│ │ │ ├── UnknownEditor.jsx
│ │ │ └── WelcomeEditor.jsx
│ │ ├── index.js
│ │ ├── state.js
│ │ └── store.js
│ ├── FileTree
│ │ ├── FileTree.jsx
│ │ ├── actions.js
│ │ ├── contextMenuItems.js
│ │ ├── fileTreeToFileBinding.js
│ │ ├── index.js
│ │ ├── state.js
│ │ └── store.js
│ ├── Git
│ │ ├── GitBranchWidget.jsx
│ │ ├── GitCommitView.jsx
│ │ ├── GitCommitViewFlat.jsx
│ │ ├── GitFileTree.jsx
│ │ ├── GitGraph
│ │ │ ├── GitGraph.jsx
│ │ │ ├── GitGraphTable.jsx
│ │ │ ├── actions.js
│ │ │ ├── helpers
│ │ │ │ ├── CommitsState.js
│ │ │ │ ├── RandColors.js
│ │ │ │ ├── chroma.js
│ │ │ │ ├── index.js
│ │ │ │ └── roundVertices.js
│ │ │ ├── index.js
│ │ │ └── state.js
│ │ ├── GitHistoryView.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── modals
│ │ │ ├── checkout.jsx
│ │ │ ├── checkoutStash.jsx
│ │ │ ├── commitDiff.jsx
│ │ │ ├── diffFile.jsx
│ │ │ ├── merge.jsx
│ │ │ ├── mergeFile.jsx
│ │ │ ├── newBranch.jsx
│ │ │ ├── rebaseInput.jsx
│ │ │ ├── rebasePrepare.jsx
│ │ │ ├── rebaseStart.jsx
│ │ │ ├── reset.jsx
│ │ │ ├── resolveConflicts.jsx
│ │ │ ├── stash.jsx
│ │ │ ├── tag.jsx
│ │ │ └── unstash.jsx
│ │ └── reducer.js
│ ├── Mask
│ │ ├── Mask.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── state.js
│ ├── Menu
│ │ ├── Menu.jsx
│ │ ├── MenuContextTypes.js
│ │ ├── MenuItem.jsx
│ │ ├── MenuSheet.jsx
│ │ └── index.js
│ ├── MenuBar
│ │ ├── MenuBar.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── menuBarItems.jsx
│ │ ├── state.js
│ │ └── store.js
│ ├── Modal
│ │ ├── FilePalette
│ │ │ ├── component.jsx
│ │ │ └── index.js
│ │ ├── FileSelector
│ │ │ └── index.js
│ │ ├── Modal.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── modals
│ │ │ ├── Alert.jsx
│ │ │ ├── Confirm.jsx
│ │ │ ├── Form.jsx
│ │ │ ├── Prompt.jsx
│ │ │ ├── index.js
│ │ │ └── modalCache.js
│ │ └── state.js
│ ├── Notification
│ │ ├── actions.js
│ │ ├── index.jsx
│ │ └── state.js
│ ├── Offline
│ │ └── Offline.jsx
│ ├── Pane
│ │ ├── Pane.jsx
│ │ ├── PaneAxis.jsx
│ │ ├── PanesContainer.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── state.js
│ ├── Panel
│ │ ├── Panel.jsx
│ │ ├── PanelAxis.jsx
│ │ ├── PanelContent.jsx
│ │ ├── PanelsContainer.jsx
│ │ ├── SideBar
│ │ │ ├── SideBar.jsx
│ │ │ ├── SidePanel.jsx
│ │ │ └── actions.js
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── state.js
│ ├── Plugins
│ │ ├── actions.js
│ │ ├── component.jsx
│ │ ├── constants.js
│ │ ├── extensionList.jsx
│ │ ├── index.js
│ │ └── store.js
│ ├── Prompt
│ │ └── Prompt.jsx
│ ├── ResizeBar.jsx
│ ├── ResizeBar2.jsx
│ ├── Setting
│ │ ├── EditorSetting.jsx
│ │ ├── FormInputGroup.jsx
│ │ ├── KeymapSetting.jsx
│ │ ├── SettingForm.jsx
│ │ ├── index.jsx
│ │ └── state.js
│ ├── StatusBar
│ │ ├── StatusBar.jsx
│ │ ├── UploadWidgets.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── state.js
│ ├── Tab
│ │ ├── TabContainer.jsx
│ │ ├── WelcomePage.jsx
│ │ ├── actions.js
│ │ ├── fileList.jsx
│ │ ├── index.js
│ │ ├── state.js
│ │ └── store.js
│ ├── Terminal
│ │ ├── Terminal.jsx
│ │ ├── TerminalContainer.jsx
│ │ ├── Xterm.jsx
│ │ ├── actions.js
│ │ ├── index.js
│ │ ├── reducer.js
│ │ ├── state.js
│ │ └── terminal-client.js
│ ├── Tooltip
│ │ ├── TooltipTrigger.jsx
│ │ ├── Tooltips.jsx
│ │ ├── index.js
│ │ └── state.js
│ ├── TopBar
│ │ ├── Breadcrumbs.jsx
│ │ ├── TopBar.jsx
│ │ └── index.js
│ └── exports.js
├── config.js
├── containers
│ ├── GlobalPrompt.jsx
│ ├── IDE.jsx
│ ├── Initialize
│ │ ├── index.jsx
│ │ └── state.js
│ ├── Login.jsx
│ ├── Root
│ │ ├── actions.js
│ │ ├── index.jsx
│ │ ├── login.jsx
│ │ └── reducer.js
│ └── Utilities.jsx
├── exports.js
├── i18n
│ ├── en_US
│ │ ├── file.json
│ │ ├── fileTree.json
│ │ ├── git.json
│ │ ├── global.json
│ │ ├── index.js
│ │ ├── login.json
│ │ ├── menuBarItems.json
│ │ ├── modal.json
│ │ ├── panel.json
│ │ ├── settings.json
│ │ └── tab.json
│ ├── index.json
│ └── zh_CN
│ │ ├── file.json
│ │ ├── fileTree.json
│ │ ├── git.json
│ │ ├── global.json
│ │ ├── index.js
│ │ ├── login.json
│ │ ├── menuBarItems.json
│ │ ├── modal.json
│ │ ├── panel.json
│ │ ├── settings.json
│ │ └── tab.json
├── index.html
├── index.jsx
├── initialize
│ ├── index.js
│ └── state.js
├── js.config.json
├── jsconfig.json
├── localStoreCache.js
├── login.html
├── login.jsx
├── mobxStore.js
├── persist.js
├── plugin
│ ├── index.js
│ └── placeholder.js
├── settings.js
├── states
│ ├── index.js
│ └── initialData.js
├── store.js
├── store.spec.js
├── styles
│ ├── base-theme
│ │ ├── index.styl
│ │ └── styles
│ │ │ ├── accordion.styl
│ │ │ ├── bars.styl
│ │ │ ├── editor.styl
│ │ │ ├── filelist.styl
│ │ │ ├── filetree.styl
│ │ │ ├── git.styl
│ │ │ ├── mask.styl
│ │ │ ├── menu.styl
│ │ │ ├── panes.styl
│ │ │ ├── tabs.styl
│ │ │ ├── term.styl
│ │ │ └── ui-variables.styl
│ ├── core-ui
│ │ ├── Accordion.styl
│ │ ├── Bar.styl
│ │ ├── Breadcrumbs.styl
│ │ ├── CommandPalette.styl
│ │ ├── DragAndDrop.styl
│ │ ├── Editor.styl
│ │ ├── FileList.styl
│ │ ├── FileTree.styl
│ │ ├── Git.styl
│ │ ├── GitMerge.styl
│ │ ├── History.styl
│ │ ├── Initialize.styl
│ │ ├── Markdown.styl
│ │ ├── Mask.styl
│ │ ├── Menu.styl
│ │ ├── MenuBar.styl
│ │ ├── Modal.styl
│ │ ├── Offline.styl
│ │ ├── PanelAndPane.styl
│ │ ├── Settings.styl
│ │ ├── StatusBar.styl
│ │ ├── Tab.styl
│ │ ├── Term.styl
│ │ ├── Tooltip.styl
│ │ ├── Welcome.styl
│ │ ├── Workspace.styl
│ │ └── index.styl
│ ├── dark
│ │ ├── index.styl
│ │ └── styles
│ │ │ ├── access.styl
│ │ │ ├── accordion.styl
│ │ │ ├── bars.styl
│ │ │ ├── base.styl
│ │ │ ├── collaborators.styl
│ │ │ ├── diff.styl
│ │ │ ├── editor.styl
│ │ │ ├── env.styl
│ │ │ ├── filelist.styl
│ │ │ ├── filetree.styl
│ │ │ ├── form.styl
│ │ │ ├── git-merge.styl
│ │ │ ├── git.styl
│ │ │ ├── history.styl
│ │ │ ├── java.styl
│ │ │ ├── markdown.styl
│ │ │ ├── mask.styl
│ │ │ ├── menu.styl
│ │ │ ├── modal.styl
│ │ │ ├── panes.styl
│ │ │ ├── tabs.styl
│ │ │ ├── term.styl
│ │ │ ├── ui-variables.styl
│ │ │ ├── weapp.styl
│ │ │ └── welcome.styl
│ ├── lib.styl
│ ├── main.styl
│ ├── mixins
│ │ ├── color.styl
│ │ ├── index.styl
│ │ ├── misc.styl
│ │ ├── padding-margin.styl
│ │ ├── position.styl
│ │ ├── scrollbar.styl
│ │ └── z-index.styl
│ ├── normalize.styl
│ ├── scaffolding.styl
│ ├── ui-variables.styl
│ └── workstation.styl
├── utils
│ ├── RandColors.js
│ ├── actions
│ │ ├── createAction.js
│ │ ├── dispatch.js
│ │ ├── emitterMiddleware.js
│ │ ├── handleAction.js
│ │ ├── handleActions.js
│ │ ├── index.js
│ │ └── registerAction.js
│ ├── assignProps.js
│ ├── codingPackageJsonp.js
│ ├── colors
│ │ ├── chroma.js
│ │ ├── hueFromString.js
│ │ └── index.js
│ ├── composeReducers.js
│ ├── createI18n.js
│ ├── decorators
│ │ ├── defaultProps.js
│ │ ├── index.js
│ │ ├── mapEntity.js
│ │ └── protectedObservable.js
│ ├── dnd.js
│ ├── dynamicStyle.js
│ ├── editorConfig.js
│ ├── emitter
│ │ └── index.js
│ ├── exports.js
│ ├── extendObservableStrict.js
│ ├── getBackoff.js
│ ├── getCookie.js
│ ├── getTabType.js
│ ├── handleActions.js
│ ├── hasVimium.js
│ ├── immutableUpdate.js
│ ├── index.js
│ ├── is.js
│ ├── loadStyle.js
│ ├── multiline.js
│ ├── path.js
│ ├── plugins.js
│ ├── promise.prototype.finalCatch.js
│ ├── qs.js
│ ├── request.js
│ ├── setSelectionRange.js
│ ├── stepFactory.js
│ ├── toJS.js
│ └── withTheme.js
├── workspaces_standalone
│ ├── WorkspaceList.jsx
│ ├── actions.js
│ ├── backendAPI.js
│ ├── bootstrapping.js
│ ├── index.html
│ ├── index.jsx
│ ├── reducer.js
│ └── store.js
├── workstation.jsx
└── workstation
│ ├── api.js
│ ├── index.js
│ ├── initialize.js
│ ├── state.js
│ ├── styles
│ └── workstation.styl
│ ├── workstation.jsx
│ └── workstationFull.jsx
├── cdn.js
├── dist
├── terminal.js
└── workstation.js
├── nginx.conf
├── package.json
├── packageListServer.js
├── static
├── favicon.ico
├── loading-logo.svg
└── logo.svg
├── task.yaml.tpl
├── webpack.config.js
├── webpack_configs
├── common.config.js
├── devServer.config.js
├── loaders
│ └── regexp-replace-loader.js
├── stylesheet.config.js
├── stylesheet.lib.config.js
├── uglify.config.js
├── webpack.dev.config.js
├── webpack.lib.config.js
├── webpack.lib.dev.config.js
├── webpack.prod.config.js
├── webpack.staging.config.js
├── workstation.config.js
└── workstation.dev.config.js
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | **
2 | !build/
3 | !nginx.conf
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.env.tpl:
--------------------------------------------------------------------------------
1 | BACKEND_URL=
2 | WS_URL=
3 | STATIC_SERVING_URL=
4 | RUN_MODE=platform
5 | PACKAGE_SERVER=
6 | PACKAGE_DEV=
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | static/*
3 | *.spec.js
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "env": {
5 | "browser": true
6 | },
7 | "rules": {
8 | "comma-dangle": 0,
9 | "global-require": 0,
10 | "no-param-reassign": 0,
11 | "new-cap": 0,
12 | "no-eval": 0,
13 | "no-plusplus": 0,
14 | "no-return-assign": 0,
15 | "no-underscore-dangle": 0,
16 | "react/require-default-props": 0,
17 | "no-multi-assign": 0,
18 | "semi": ["error", "never"],
19 | "one-var": 0,
20 | "one-var-declaration-per-line": 0,
21 | "space-before-function-paren": ["error", {
22 | "anonymous": "always",
23 | "named": "always",
24 | "asyncArrow": "ignore"
25 | }],
26 | "jsx-quotes": ["error", "prefer-single"],
27 | "react/jsx-first-prop-new-line": 0,
28 | "react/forbid-prop-types": 0,
29 | "jsx-a11y/no-static-element-interactions": 0
30 | },
31 | "settings": {
32 | "import/resolver": "webpack"
33 | },
34 | "globals": {
35 | "i18n": false,
36 | "__PACKAGE_PORTS__": false,
37 | "extension": false,
38 | "__DEV__": false,
39 | "__PACKAGE_SERVER__": false
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/node_modules/fbjs/.*
3 |
4 | [options]
5 | module.system.node.resolve_dirname=node_modules
6 | module.system.node.resolve_dirname=app
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | .idea/
4 | .vscode/
5 | .env
6 | app/config.js
7 | *.bak
8 | coverage/
9 | task.yaml
--------------------------------------------------------------------------------
/.jestfilemock.js:
--------------------------------------------------------------------------------
1 | module.exports = {};
2 |
--------------------------------------------------------------------------------
/.plugins.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | registry "https://registry.npm.taobao.org/"
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx:1.11.9-alpine
2 |
3 | ADD build/ /usr/share/nginx/html/
4 | ADD nginx.conf /etc/nginx/conf.d/default.conf
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2016 CODING(https://coding.net/).
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 |
--------------------------------------------------------------------------------
/app/IDE/IdeEnvironment.js:
--------------------------------------------------------------------------------
1 |
2 | const IdeEnvironment = () => ({
3 | editors: {}
4 | })
5 | export default IdeEnvironment
6 |
--------------------------------------------------------------------------------
/app/IDE/index.js:
--------------------------------------------------------------------------------
1 | // This is the access point to the API that Coding WebIDE exposes
2 | // Idea taken from atom, this is the equivalence of `window.atom`
3 | //
4 | // see: https://github.com/atom/atom/blob/master/src/atom-environment.coffee
5 |
6 | import IdeEnvironment from './IdeEnvironment'
7 | export default window.ide = IdeEnvironment()
8 |
--------------------------------------------------------------------------------
/app/backendAPI/index.js:
--------------------------------------------------------------------------------
1 | import * as fileAPI from './fileAPI'
2 | import * as gitAPI from './gitAPI'
3 | import * as packageAPI from './packageAPI'
4 | import * as workspaceAPI from './workspaceAPI'
5 | import * as websocketClients from './websocketClients'
6 |
7 | export default {
8 | ...fileAPI,
9 | ...gitAPI,
10 | ...packageAPI,
11 | ...workspaceAPI
12 | }
13 |
14 | export { websocketClients }
15 |
--------------------------------------------------------------------------------
/app/commands/CommandPalette/index.js:
--------------------------------------------------------------------------------
1 | import CommandPalette from './component'
2 | import commandPaletteItems from './items'
3 | import getPaletteItems from './getPaletteItems'
4 |
5 | export { CommandPalette, commandPaletteItems, getPaletteItems }
6 |
--------------------------------------------------------------------------------
/app/commands/CommandPalette/items.js:
--------------------------------------------------------------------------------
1 | const commandPaletteItems = [
2 | { name: 'File: New File', command: 'file:new_file' },
3 | { name: 'Git: Commit', command: 'git:commit' }
4 | ]
5 |
6 | export default commandPaletteItems
7 |
--------------------------------------------------------------------------------
/app/commands/commandBindings/editor.js:
--------------------------------------------------------------------------------
1 | import store, { dispatch as $d } from '../../store'
2 | import api from '../../backendAPI'
3 | import * as Pane from '../../components/Pane/actions'
4 |
5 | export default {
6 | 'editor:split_pane_1': (c) => {
7 | Pane.split(1)
8 | },
9 | 'editor:split_pane_vertical_2': (c) => {
10 | Pane.split(2, 'row')
11 | },
12 | 'editor:split_pane_horizontal_2': (c) => {
13 | Pane.split(2, 'column')
14 | },
15 | 'editor:split_pane_vertical_3': (c) => {
16 | Pane.split(3, 'row')
17 | },
18 | 'editor:split_pane_horizontal_3': (c) => {
19 | Pane.split(3, 'column')
20 | },
21 | 'editor:split_pane_vertical_4': (c) => {
22 | Pane.split(4, 'row')
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/app/commands/commandBindings/index.js:
--------------------------------------------------------------------------------
1 | import git from './git'
2 | import file from './file'
3 | import misc from './misc'
4 | import editor from './editor'
5 | import tab from './tab'
6 |
7 | export default {
8 | ...git,
9 | ...file,
10 | ...misc,
11 | ...editor,
12 | ...tab
13 | }
14 |
--------------------------------------------------------------------------------
/app/commands/commandBindings/tab.js:
--------------------------------------------------------------------------------
1 | import { dispatch as $d } from '../../store'
2 | import store from 'mobxStore'
3 | import * as Tab from 'components/Tab/actions'
4 | import * as PaneActions from 'components/Pane/actions'
5 |
6 | export default {
7 | 'tab:close': (c) => {
8 | Tab.removeTab(c.context.id)
9 | },
10 |
11 | 'tab:close_other': (c) => {
12 | Tab.removeOtherTab(c.context.id)
13 | },
14 |
15 | 'tab:close_all': (c) => {
16 | Tab.removeAllTab(c.context.id)
17 | },
18 |
19 | 'tab:split_v': (c) => {
20 | const panes = store.PaneState.panes
21 | const pane = panes.values().find(pane =>
22 | pane.contentId === c.context.tabGroupId
23 | )
24 | PaneActions.splitTo(pane.id, 'bottom').then(newPaneId =>
25 | Tab.moveTabToPane(c.context.id, newPaneId)
26 | )
27 | },
28 |
29 | 'tab:split_h': (c) => {
30 | const panes = store.PaneState.panes
31 | const pane = panes.values().find(pane =>
32 | pane.contentId === c.context.tabGroupId
33 | )
34 | PaneActions.splitTo(pane.id, 'right').then(newPaneId =>
35 | Tab.moveTabToPane(c.context.id, newPaneId)
36 | )
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/app/commands/dispatchCommand.js:
--------------------------------------------------------------------------------
1 | import { emitter } from 'utils'
2 | import { isArray } from 'utils/is'
3 |
4 | let _context = null
5 |
6 | export function setContext (context) { _context = context }
7 |
8 |
9 | export function addCommand (obj) {
10 | if (isArray(obj)) {
11 | obj.forEach((element) => {
12 | addCommand(element)
13 | })
14 | } else {
15 | const key = Object.keys(obj)[0]
16 | emitter.on(key, obj[key])
17 | }
18 | }
19 |
20 | function dispatchCommand (commandType, data) {
21 | let command
22 | if (typeof commandType === 'object') command = commandType
23 | else if (typeof commandType === 'string') command = { type: commandType, data }
24 | else return
25 | // bind context to command before run
26 | command.context = _context
27 | emitter.emit(command.type, command)
28 | }
29 |
30 | window.dispatchCommand = dispatchCommand
31 |
32 | export default dispatchCommand
33 |
--------------------------------------------------------------------------------
/app/commands/index.js:
--------------------------------------------------------------------------------
1 | import { emitter } from 'utils'
2 | import Keymapper from './lib/keymapper'
3 | import keymaps from './keymaps'
4 | import commandBindings from './commandBindings'
5 | import dispatchCommand, { setContext, addCommand } from './dispatchCommand'
6 | import { CommandPalette } from './CommandPalette'
7 |
8 | const key = new Keymapper({ dispatchCommand })
9 | key.loadKeymaps(keymaps)
10 |
11 | Object.keys(commandBindings).map((commandType) => {
12 | emitter.on(commandType, commandBindings[commandType])
13 | })
14 |
15 | export { dispatchCommand, setContext, CommandPalette, addCommand }
16 |
--------------------------------------------------------------------------------
/app/commands/lib/helpers.js:
--------------------------------------------------------------------------------
1 | const keycodes = require('./keycodes')
2 | const MODIFIERS_LIST = ['meta', 'ctrl', 'shift', 'alt']
3 |
4 | export function keyEventToKeyCombination (e, combinator) {
5 | // ensure comb always in the order spec by MODIFIERS_LIST
6 | const modString = MODIFIERS_LIST.filter(mod => e[`${mod}Key`]).join(combinator)
7 | if (modString) {
8 | return [modString, keycodes.keyCodeToKey[e.keyCode]].join(combinator)
9 | }
10 | return keycodes.keyCodeToKey[e.keyCode]
11 | }
12 |
13 | export function normalizeKeys (keys, combinator='+', delimiter=' ') {
14 | // validate keys spec, if valid, also unify order of modifiers as in MODIFIERS_LIST
15 | return keys.toLowerCase().split(delimiter).map((keyCombo) => {
16 | const keyEventObj = {}
17 | keyCombo.split(combinator).forEach((key) => {
18 | // 'meta' is also aliased as 'cmd' or 'super'
19 | if (key === 'cmd' || key === 'command' || key === 'super') key = 'meta'
20 | if (MODIFIERS_LIST.indexOf(key) > -1) {
21 | keyEventObj[`${key}Key`] = true
22 | } else {
23 | keyEventObj.keyCode = keycodes.keyToKeyCode[key]
24 | }
25 | })
26 | if (typeof keyEventObj.keyCode !== 'number') {
27 | throw Error(`Keymapper: Unrecognized key combination \`${keyCombo}\``)
28 | }
29 | return keyEventToKeyCombination(keyEventObj, combinator)
30 | })
31 | .join(delimiter)
32 | }
33 |
--------------------------------------------------------------------------------
/app/commands/lib/keymapper.js:
--------------------------------------------------------------------------------
1 | import Mousetrap from 'mousetrap'
2 | import { normalizeKeys } from './helpers'
3 |
4 | Mousetrap.prototype.stopCallback = function () { return false }
5 |
6 | class Keymapper {
7 | constructor ({ dispatchCommand }) {
8 | this.dispatchCommand = dispatchCommand
9 | }
10 |
11 | loadKeymaps (keymaps) {
12 | Object.entries(keymaps).map(([keycombo, commandType]) => {
13 | Mousetrap.bind(normalizeKeys(keycombo), (e) => {
14 | this.dispatchCommand(commandType)
15 | e.preventDefault(); e.stopPropagation()
16 | })
17 | })
18 | }
19 | }
20 |
21 | export default Keymapper
22 |
--------------------------------------------------------------------------------
/app/commons/File/index.js:
--------------------------------------------------------------------------------
1 | import FileState from './state'
2 | import * as actions from './actions'
3 | import store from './store'
4 |
5 | export {
6 | store,
7 | FileState,
8 | actions,
9 | }
10 |
--------------------------------------------------------------------------------
/app/commons/File/store.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import state, { FileNode } from './state'
3 |
4 | class FileStore {
5 | constructor () {
6 | Object.assign(this, actions)
7 | }
8 |
9 | getState () { return state }
10 |
11 | get (path) {
12 | const file = state.entities.get(path)
13 | return file
14 | }
15 |
16 | isValid (instance) {
17 | return (instance instanceof FileNode && state.entities.has(instance.id))
18 | }
19 | }
20 |
21 | const store = new FileStore()
22 | export default store
23 |
--------------------------------------------------------------------------------
/app/commons/Pane/state.js:
--------------------------------------------------------------------------------
1 | import { extendObservable, observable, computed } from 'mobx'
2 |
3 | function PaneScope () {
4 | const state = observable({
5 | entities: observable.map({})
6 | })
7 |
8 | class BasePane {
9 | @computed
10 | get isRoot () {
11 | return !this.parentId
12 | }
13 |
14 | @computed
15 | get parent () {
16 | const parent = state.entities.get(this.parentId)
17 | if (parent === this) throw Error(`Pane/Panel ${this.id} is parent of itself.`)
18 | return parent
19 | }
20 |
21 | @computed
22 | get views () {
23 | return state.entities.values()
24 | .filter(pane => pane.parentId === this.id)
25 | .sort((a, b) => a.index - b.index)
26 | }
27 |
28 | @computed
29 | get siblings () {
30 | if (!this.parent) return [this]
31 | return this.parent.views
32 | }
33 |
34 | @computed
35 | get leafChildren () {
36 | if (!this.views.length) return [this]
37 | return this.views.reduce((acc, child) =>
38 | acc.concat(child.leafChildren)
39 | , [])
40 | }
41 |
42 | @computed
43 | get prev () {
44 | return this.siblings[this.index - 1]
45 | }
46 |
47 | @computed
48 | get next () {
49 | return this.siblings[this.index + 1]
50 | }
51 | }
52 |
53 | return { BasePane, state }
54 | }
55 |
56 | export default PaneScope
57 |
--------------------------------------------------------------------------------
/app/commons/Tab/TabContent.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import cx from 'classnames'
4 | import { observer } from 'mobx-react'
5 |
6 | export const TabContent = ({ children }) => (
7 |
10 | )
11 |
12 | export const TabContentItem = observer(({ tab, children }) => {
13 | if (tab.isActive && tab.onActive) {
14 | tab.onActive()
15 | }
16 | return (
17 |
18 | {children}
19 |
20 | )
21 | })
22 |
23 | TabContentItem.propTypes = {
24 | tab: PropTypes.object.isRequired,
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/app/commons/Tab/index.js:
--------------------------------------------------------------------------------
1 | import TabStateScope from './state'
2 | import TabBar from './TabBar'
3 | import { TabContent, TabContentItem } from './TabContent'
4 |
5 | export { TabBar, TabContent, TabContentItem, TabStateScope }
6 |
--------------------------------------------------------------------------------
/app/commons/Tree/index.js:
--------------------------------------------------------------------------------
1 | import TreeNode from './TreeNode'
2 | import TreeNodeScope from './state'
3 | export { TreeNode, TreeNodeScope }
4 |
--------------------------------------------------------------------------------
/app/commons/Tree/store.js:
--------------------------------------------------------------------------------
1 | import { registerAction } from 'utils/actions'
2 | import api from 'backendAPI'
3 |
4 | const selectNode = registerAction('FILETREE_SELECT_NODE',
5 | (nodeOrOffset, multiSelect) => ({ nodeOrOffset, multiSelect }),
6 | ({ nodeOrOffset, multiSelect = false }) => {
7 | let offset, node
8 | if (typeof nodeOrOffset === 'number') {
9 | offset = nodeOrOffset
10 | } else {
11 | node = nodeOrOffset
12 | }
13 |
14 | if (offset === 1) {
15 | node = state.focusedNodes[0].next(true)
16 | } else if (offset === -1) {
17 | node = state.focusedNodes[0].prev(true)
18 | }
19 |
20 | if (!multiSelect) {
21 | state.root.unfocus()
22 | state.root.forEachDescendant(childNode => childNode.unfocus())
23 | }
24 |
25 | node.focus()
26 | }
27 | )
28 |
29 | const openNode = registerAction('open_node',
30 | (node, shouldBeFolded, deep) => ({ node, shouldBeFolded, deep }),
31 | ({ node, shouldBeFolded = null, deep = false }) => {
32 |
33 | }
34 | )
35 |
36 |
--------------------------------------------------------------------------------
/app/commons/WorkSpace/state.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding/WebIDE-Frontend/fab3d991cd90be0c04ec7c57332bca32c6e9cddf/app/commons/WorkSpace/state.js
--------------------------------------------------------------------------------
/app/commons/exports.js:
--------------------------------------------------------------------------------
1 | import * as File from './File'
2 |
3 | export {
4 | File,
5 | }
6 |
--------------------------------------------------------------------------------
/app/components/Accordion/index.js:
--------------------------------------------------------------------------------
1 | import { Accordion, AccordionGroup } from './Accordion'
2 |
3 | export { Accordion, AccordionGroup }
4 |
--------------------------------------------------------------------------------
/app/components/ContextMenu/ContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import cx from 'classnames'
4 | import Menu from '../Menu'
5 | import {setContext} from '../../commands/dispatchCommand'
6 |
7 | const ContextMenu = (props) => {
8 | const {items, context, isActive, pos, deactivate, className} = props
9 | if (!isActive) return null
10 | // add a `pos` related key to Menu can force it to re-render at change of pos
11 |
12 | setContext(context) // <- each time invoked, update command's context
13 |
14 | return (
15 |
16 |
21 |
22 | )
23 | }
24 |
25 | export default ContextMenu
26 |
--------------------------------------------------------------------------------
/app/components/ContextMenu/ContextMenuContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { observer } from 'mobx-react'
3 | import { action } from 'mobx'
4 | import ContextMenu from './ContextMenu'
5 | import state from './state'
6 | import store from './store'
7 |
8 | const ContextMenuContainer = observer(() => {
9 | return (
10 |
18 | )
19 | })
20 |
21 | export default ContextMenuContainer
22 |
--------------------------------------------------------------------------------
/app/components/ContextMenu/index.js:
--------------------------------------------------------------------------------
1 | import ContextMenu from './ContextMenu'
2 | import ContextMenuContainer from './ContextMenuContainer'
3 | import store from './store'
4 |
5 | export default ContextMenu
6 | export { ContextMenuContainer, store }
7 |
--------------------------------------------------------------------------------
/app/components/ContextMenu/state.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const state = observable({
4 | isActive: false,
5 | pos: { x: 0, y: 0 },
6 | contextNode: observable.ref(null),
7 | items: observable.ref([]),
8 | className: '',
9 | })
10 |
11 | export default state
12 |
--------------------------------------------------------------------------------
/app/components/Editor/EditorWrapper.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { observer } from 'mobx-react'
4 | import { when } from 'mobx'
5 | import CodeEditor from './components/CodeEditor'
6 | import MarkdownEditor from './components/MarkdownEditor'
7 | import ImageEditor from './components/ImageEditor'
8 | import UnknownEditor from './components/UnknownEditor'
9 | import WelcomeEditor from './components/WelcomeEditor'
10 | import HtmlEditor from './components/HtmlEditor'
11 | import config from '../../config'
12 |
13 | const EditorWrapper = observer(({ tab, active }) => {
14 | const { editor } = tab
15 | const editorType = editor.editorType || 'default'
16 | const file = editor.file || {}
17 | // key is crutial here, it decides whether
18 | // the component should re-construct or
19 | // keep using the existing instance.
20 | const key = `editor_${file.path}`
21 | switch (editorType) {
22 | case 'htmlEditor':
23 | return React.createElement(HtmlEditor, { editor, key, tab, active })
24 | case 'default':
25 | return React.createElement(CodeEditor, { editor, key, tab, active })
26 | case 'editorWithPreview':
27 | return React.createElement(MarkdownEditor, { editor, key, tab, active })
28 | case 'imageEditor':
29 | return React.createElement(ImageEditor, { path: file.path, key, tab, active })
30 | default:
31 | return React.createElement(UnknownEditor, { path: file.path, size: file.size, key, tab, active })
32 | }
33 | })
34 |
35 | EditorWrapper.propTypes = {
36 | tab: PropTypes.object
37 | }
38 |
39 | EditorWrapper.contextTypes = {
40 | i18n: PropTypes.func
41 | }
42 |
43 | export default EditorWrapper
44 |
--------------------------------------------------------------------------------
/app/components/Editor/actions.js:
--------------------------------------------------------------------------------
1 | import { registerAction } from 'utils/actions'
2 | import mobxStore from '../../mobxStore'
3 |
4 | const getCurrentCM = () => {
5 | const { EditorTabState } = mobxStore
6 | const activeTab = EditorTabState.activeTab
7 | const cm = activeTab ? activeTab.editor.cm : null
8 | return cm
9 | }
10 |
11 | export const formatCode = registerAction('edit:toggle_format', () => {
12 | const cm = getCurrentCM()
13 | if (!cm) return
14 | let range = { from: cm.getCursor(true), to: cm.getCursor(false) }
15 | if (range.from.ch === range.to.ch && range.from.line === range.to.line) {
16 | cm.execCommand('selectAll')
17 | range = { from: cm.getCursor(true), to: cm.getCursor(false) }
18 | }
19 | cm.autoFormatRange(range.from, range.to)
20 | })
21 |
22 | export const toggleComment = registerAction('edit:toggle_comment', () => {
23 | const cm = getCurrentCM()
24 | if (!cm) return
25 | const range = { from: cm.getCursor(true), to: cm.getCursor(false) }
26 | // cm.toggleComment(range.from, range.to, { indent: true })
27 | if (range.from.line === range.to.line) {
28 | if (!cm.uncomment(range.from, range.to)) {
29 | cm.lineComment(range.from, range.to, { indent: true })
30 | }
31 | } else if (!cm.uncomment(range.from, range.to)) {
32 | cm.blockComment(range.from, range.to, { fullLines: false })
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/CodeEditor.jsx:
--------------------------------------------------------------------------------
1 | import BaseCodeEditor from './BaseCodeEditor'
2 | import addMixinMechanism from './addMixinMechanism'
3 | import basicMixin from './mixins/basicMixin'
4 | import gitBlameMixin from './mixins/gitBlameMixin'
5 | import eslintMixin from './mixins/eslintMixin'
6 |
7 | class CodeEditor extends BaseCodeEditor {
8 | componentWillReceiveProps (newProps) {
9 | if (newProps.editor && this.editor === newProps.editor) return
10 | this.editor = newProps.editor
11 | this.cm = this.editor.cm
12 | this.cmDOM = this.cm.getWrapperElement()
13 | this.dom.removeChild(this.dom.children[0])
14 | this.dom.appendChild(this.cmDOM)
15 | this.cm.setSize('100%', '100%')
16 | this.cm.refresh()
17 | }
18 | }
19 | addMixinMechanism(CodeEditor, BaseCodeEditor)
20 |
21 | CodeEditor.use(basicMixin)
22 | CodeEditor.use(gitBlameMixin)
23 | CodeEditor.use(eslintMixin)
24 |
25 | export default CodeEditor
26 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/TablessCodeEditor.jsx:
--------------------------------------------------------------------------------
1 | import TabStore from 'components/Tab/store'
2 | import BaseCodeEditor from './BaseCodeEditor'
3 |
4 | class TablessCodeEditor extends BaseCodeEditor {
5 | getEventListeners () {
6 | return {
7 | change: (cm) => {
8 | TabStore.createTab({
9 | flags: { modified: true },
10 | tabGroup: { id: this.props.tabGroupId },
11 | editor: {
12 | content: cm.getValue(),
13 | cm,
14 | },
15 | })
16 | }
17 | }
18 | }
19 |
20 | componentDidMount () {
21 | super.componentDidMount()
22 | const eventListeners = this.getEventListeners()
23 | const disposers = Object.keys(eventListeners).reduce((acc, eventType) => {
24 | this.cm.on(eventType, eventListeners[eventType])
25 | return acc.concat(() => this.cm.off(eventType, eventListeners[eventType]))
26 | }, [])
27 |
28 | this.cmRemoveEventListeners = function cmRemoveEventListeners () {
29 | disposers.forEach(disposer => disposer())
30 | }
31 | }
32 |
33 | componentWillUnmount () {
34 | this.cmRemoveEventListeners()
35 | this.editor.destroy()
36 | }
37 | }
38 |
39 | export default TablessCodeEditor
40 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/addons/index.js:
--------------------------------------------------------------------------------
1 | /* import offical codemirror addons */
2 |
3 | // diaglog
4 | import 'codemirror/addon/dialog/dialog.js'
5 | import 'codemirror/addon/dialog/dialog.css'
6 | // search
7 | import 'codemirror/addon/search/search.js'
8 |
9 | // comment
10 | import 'codemirror/addon/comment/comment.js'
11 |
12 | // bracket
13 | import 'codemirror/addon/edit/matchbrackets.js'
14 | import 'codemirror/addon/edit/closebrackets.js'
15 |
16 | // code fold
17 | import 'codemirror/addon/fold/foldcode.js'
18 | import 'codemirror/addon/fold/foldgutter.js'
19 |
20 | // hint
21 | // import 'codemirror/addon/hint/show-hint.js'
22 |
23 | // lint
24 | import '../lib/formatting'
25 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/addons/mode/index.js:
--------------------------------------------------------------------------------
1 | import modeInfos from './modeInfos'
2 | import loadMode from './loadMode'
3 |
4 | export { modeInfos, loadMode }
5 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/index.js:
--------------------------------------------------------------------------------
1 | import CodeEditor from './CodeEditor'
2 | import TablessCodeEditor from './TablessCodeEditor'
3 | import './addons'
4 |
5 | export default CodeEditor
6 | export { TablessCodeEditor }
7 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/mixins/eslintMixin/eslintMixin.js:
--------------------------------------------------------------------------------
1 | import 'codemirror/addon/lint/lint.js'
2 | import 'codemirror/addon/lint/lint.css'
3 | import { extendObservable } from 'mobx'
4 | import linterFactory from './codemirror-eslint'
5 | import { notify, NOTIFY_TYPE } from 'components/Notification/actions'
6 |
7 | function handleLinterError (error, cm) {
8 | cm.setOption('lint', {
9 | name: 'ESLint',
10 | enabled: false,
11 | error,
12 | retry () {
13 | cm.setOption('lint', lintOption)
14 | }
15 | })
16 | }
17 |
18 | const lintOption = {
19 | name: 'ESLint',
20 | enabled: true,
21 | async: true,
22 | getAnnotations: linterFactory(handleLinterError),
23 | toggle (cm) {
24 | this.enabled = !this.enabled
25 | cm && cm.performLint()
26 | },
27 | }
28 |
29 | export default {
30 | key: 'eslint',
31 | shouldMount () {
32 | const editor = this.editor
33 | if (editor.modeInfo && (editor.modeInfo.mode === 'javascript' || editor.modeInfo.mode === 'jsx')) return true
34 | },
35 | componentDidMount () {
36 | const cm = this.cm
37 | const gutters = cm.options.gutters
38 | if (gutters.findIndex(item => item === 'CodeMirror-lint-markers') < 0) {
39 | cm.setOption('gutters', ['CodeMirror-lint-markers', ...gutters])
40 | }
41 | // cm.setOption('gutters', ['CodeMirror-lint-markers'])
42 | cm.setOption('lint', lintOption)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/mixins/eslintMixin/eslintService.js:
--------------------------------------------------------------------------------
1 | import api from 'backendAPI'
2 | import baseNodeScript from '!raw-loader!uglify-loader?{"compress":{"booleans": false}}!./eslintServiceCommandTemplate.js'
3 |
4 | const getCommand = (runOnFile, filePathOrText) => {
5 | let nodeScript = runOnFile ? baseNodeScript.replace('BOOL,PARAMS', `true, "${filePathOrText}"`)
6 | : baseNodeScript.replace('BOOL,PARAMS', `false, "${JSON.stringify(filePathOrText)}"`)
7 | return `cd /home/coding/workspace && node -e ${JSON.stringify(nodeScript)}`
8 | }
9 |
10 | const eslintService = {
11 | executeOnFile (filePath) {
12 | return api.execShellCommand(getCommand(true, filePath)).then(data => data.stdout)
13 | },
14 |
15 | executeOnText (text) {
16 | return api.execShellCommand(getCommand(false, text)).then(data => data.stdout)
17 | },
18 | }
19 |
20 | export default eslintService
21 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/mixins/eslintMixin/eslintServiceCommandTemplate.js:
--------------------------------------------------------------------------------
1 | console.log(JSON.stringify((function (runOnFile, filePathOrText) {
2 | function not (boolean) { return Boolean(boolean) === false }
3 | try {
4 | var NO_MODULE_ERROR = { error: true, type: 'NO_MODULE', name: 'eslint' };
5 | var NO_FILE_ERROR = { error: true, type: 'NO_FILE' };
6 | var UNKNOWN_ERROR = { error: true, type: 'UNKNOWN' };
7 |
8 | var eslint = require('eslint');
9 | var engine = new eslint.CLIEngine({ useEslintrc: true, cacheFile: '.eslintcache', ignore: true, allowInlineConfig: true });
10 | var report = runOnFile ? engine.executeOnFiles(['./' + filePathOrText]) : engine.executeOnText(filePathOrText);
11 | var singleFileResult = report.results[0];
12 | if (not(singleFileResult)) return NO_FILE_ERROR;
13 | delete singleFileResult.source;
14 | singleFileResult.messages.forEach(function (message) {
15 | delete message.source;
16 | delete message.fix;
17 | delete message.nodeType;
18 | });
19 | return singleFileResult;
20 |
21 | } catch (err) {
22 | var errorMessage = String(err);
23 | var noModuleKeyword = 'Cannot find module ';
24 | if (errorMessage.indexOf(noModuleKeyword) > -1) {
25 | NO_MODULE_ERROR.message = errorMessage;
26 | return NO_MODULE_ERROR;
27 | } else {
28 | UNKNOWN_ERROR.message = errorMessage;
29 | return UNKNOWN_ERROR
30 | }
31 | }
32 | })(BOOL,PARAMS)));
33 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/mixins/eslintMixin/index.js:
--------------------------------------------------------------------------------
1 | import eslintMixin from './eslintMixin'
2 | export default eslintMixin
3 |
--------------------------------------------------------------------------------
/app/components/Editor/components/CodeEditor/mixins/gitBlameMixin.jsx:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 | import { autorun } from 'mobx'
3 | import mtln from 'utils/multiline'
4 |
5 | export default {
6 | key: 'gitBlameGutter',
7 | componentDidMount () {
8 | this.dispose = autorun('renderGitBlameGutter', () => {
9 | // set gutter first
10 | const gitBlameGutterId = 'git-blame-gutter'
11 | const editor = this.editor
12 | const cm = this.cm
13 | const gutters = editor.options.gutters
14 |
15 | if (!editor.gitBlame.show) {
16 | if (!gutters.includes(gitBlameGutterId)) return
17 | cm.clearGutter(gitBlameGutterId)
18 | editor.options.gutters = gutters.filter(id => id !== gitBlameGutterId)
19 | cm.refresh()
20 | return
21 | }
22 |
23 | const gitBlameData = editor.gitBlame.data || []
24 |
25 | if (gutters.indexOf(gitBlameGutterId) === -1) {
26 | editor.options.gutters = [...gutters, gitBlameGutterId]
27 | }
28 |
29 | gitBlameData.forEach(({ author, shortName: commitHash }, ln) => {
30 | if (!commitHash) return
31 | const fullMessage = mtln`
32 | commit: ${commitHash}
33 | time: ${moment(author.when).format('YYYY-MM-DD hh:mm:ss')}
34 | author: ${author.name}<${author.emailAddress}>`
35 | const blameText = document.createElement('div')
36 | blameText.innerHTML = `${commitHash} ${moment(author.when).format('YYYY-MM-DD')} ${author.name}
`
37 | cm.setGutterMarker(ln, 'git-blame-gutter', blameText)
38 | })
39 |
40 | cm.refresh()
41 | })
42 | },
43 | componentWillUnmount () {
44 | this.dispose()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/components/Editor/components/EditorWidgets/EditorWidgets.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { observer, inject } from 'mobx-react'
3 | import ModeWidget from './ModeWidget'
4 | import LineWidget from './LineWidget'
5 | import LinterWidget from './LinterWidget'
6 | import EncodingWidget from './EncodingWidget'
7 |
8 | @inject(({ EditorTabState }) => {
9 | const activeTab = EditorTabState.activeTab
10 | if (!activeTab || !activeTab.editor) return { editor: null }
11 | return { editor: activeTab.editor }
12 | })
13 | @observer
14 | class EditorWidgets extends Component {
15 | render () {
16 | const editor = this.props.editor
17 | if (!editor) return null
18 | return (
19 |
20 |
21 | {editor.file && }
22 |
23 | {editor.options.lint && }
24 |
25 | )
26 | }
27 | }
28 |
29 | export default EditorWidgets
30 |
--------------------------------------------------------------------------------
/app/components/Editor/components/EditorWidgets/LinterWidget.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { observer } from 'mobx-react'
3 | import cx from 'classnames'
4 | import TooltipTrigger from 'components/Tooltip'
5 |
6 | @observer
7 | export default class LinterWidget extends Component {
8 | render () {
9 | const editor = this.props.editor
10 | const lintOptions = editor.options.lint
11 | const lintError = lintOptions.error
12 | return (
13 | Boolean(lintError)}
15 | placement='top'
16 | content={lintError && {lintError.message}}
17 | >
18 | lintOptions.toggle && lintOptions.toggle(editor.cm)}
20 | >
21 | ESLint
23 |
24 |
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/components/Editor/components/EditorWidgets/ModeWidget.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { observer } from 'mobx-react'
3 | import cx from 'classnames'
4 | import modeInfos from 'components/Editor/components/CodeEditor/addons/mode/modeInfos'
5 | import Menu from 'components/Menu'
6 |
7 | @observer
8 | export default class ModeWidget extends Component {
9 | constructor (props) {
10 | super(props)
11 | this.state = {
12 | isActive: false
13 | }
14 | }
15 |
16 | toggleActive (isActive, isTogglingEnabled) {
17 | if (isTogglingEnabled) { isActive = !this.state.isActive }
18 | this.setState({ isActive })
19 | }
20 |
21 | setMode (name) {
22 | this.props.editor.setMode(name)
23 | }
24 |
25 | makeModeMenuItems () {
26 | return modeInfos.map((mode) => ({
27 | key: mode.name,
28 | name: mode.name,
29 | command: () => {
30 | this.setMode(mode.name)
31 | },
32 | }))
33 | }
34 |
35 | render () {
36 | const editor = this.props.editor
37 | return (
38 | { this.toggleActive(true, true) }}
40 | >
41 |
{editor.mode}
42 | {this.state.isActive ?
43 |
44 |
52 |
53 | : null}
54 |
55 | )
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/components/Editor/components/EditorWidgets/index.js:
--------------------------------------------------------------------------------
1 | import EditorWidgets from './EditorWidgets'
2 |
3 | export default EditorWidgets
4 |
--------------------------------------------------------------------------------
/app/components/Editor/components/HtmlEditor/actions.js:
--------------------------------------------------------------------------------
1 | import registerAction from 'utils/actions/registerAction'
2 | // import state from './state'
3 |
4 | export const HTML_EDITOR_RESIZE = 'HTML_EDITOR_RESIZE'
5 | export const editorResize = registerAction(HTML_EDITOR_RESIZE,
6 | (__, dX, dY, state) => ({ dX, dY, state }),
7 | ({ dX, dY, state }) => {
8 | const leftDom = document.getElementById('editor_preview_markdown_editor')
9 | const rightDom = document.getElementById('editor_html_preview')
10 | state.leftGrow = state.leftGrow * (leftDom.offsetWidth - dX) / leftDom.offsetWidth
11 | state.rightGrow = state.rightGrow * (rightDom.offsetWidth + dX) / rightDom.offsetWidth
12 | }
13 | )
14 |
15 | export const HTML_EDITOR_TOGGLE_PREVIEW = 'HTML_EDITOR_TOGGLE_PREVIEW'
16 | export const togglePreview = registerAction(HTML_EDITOR_TOGGLE_PREVIEW,
17 | ({ state }) => state.showPreview = !state.showPreview
18 | )
19 |
20 | export const HTML_EDITOR_TOGGLE_SIZE = 'HTML_EDITOR_TOGGLE_SIZE'
21 | export const togglePreviewSize = registerAction(HTML_EDITOR_TOGGLE_SIZE,
22 | ({ state }) => state.showBigSize = !state.showBigSize
23 | )
24 |
--------------------------------------------------------------------------------
/app/components/Editor/components/HtmlEditor/htmlMixin.js:
--------------------------------------------------------------------------------
1 | import state from './state'
2 | import debounce from 'lodash/debounce'
3 |
4 | export default {
5 | key: 'java_mixin',
6 | getEventListeners () {
7 | return {
8 | change: debounce((cm) => {
9 | }, 1200),
10 | }
11 | },
12 | componentWillMount () {},
13 | componentDidMount () {},
14 | }
15 |
--------------------------------------------------------------------------------
/app/components/Editor/components/HtmlEditor/state.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const state = observable({
4 | leftGrow: 50,
5 | rightGrow: 50,
6 | showBigSize: false,
7 | showPreview: false,
8 | previewUniqueId: '1',
9 | isResizing: false,
10 | })
11 |
12 | export default state
13 |
--------------------------------------------------------------------------------
/app/components/Editor/components/MarkdownEditor/actions.js:
--------------------------------------------------------------------------------
1 | import registerAction from 'utils/actions/registerAction'
2 | // import state from './state'
3 |
4 | export const MARKDOWN_EDITOR_RESIZE = 'EDITOR_RESIZE'
5 | export const editorResize = registerAction(MARKDOWN_EDITOR_RESIZE,
6 | (__, dX, dY, state) => ({ dX, dY, state }),
7 | ({ dX, dY, state }) => {
8 | const leftDom = document.getElementById('editor_preview_markdown_editor')
9 | const rightDom = document.getElementById('editor_preview_preview')
10 | state.leftGrow = state.leftGrow * (leftDom.offsetWidth - dX) / leftDom.offsetWidth
11 | state.rightGrow = state.rightGrow * (rightDom.offsetWidth + dX) / rightDom.offsetWidth
12 | }
13 | )
14 |
15 | export const MARKDOWN_EDITOR_TOGGLE_PREVIEW = 'MARKDOWN_EDITOR_TOGGLE_PREVIEW'
16 | export const togglePreview = registerAction(MARKDOWN_EDITOR_TOGGLE_PREVIEW,
17 | ({ state }) => state.showPreview = !state.showPreview
18 | )
19 |
20 | export const MARKDOWN_EDITOR_TOGGLE_SIZE = 'MARKDOWN_EDITOR_TOGGLE_SIZE'
21 | export const togglePreviewSize = registerAction(MARKDOWN_EDITOR_TOGGLE_SIZE,
22 | ({ state }) => state.showBigSize = !state.showBigSize
23 | )
24 |
--------------------------------------------------------------------------------
/app/components/Editor/components/MarkdownEditor/reducer.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 |
3 | import {
4 | MARKDOWN_EDITOR_RESIZE,
5 | MARKDOWN_EDITOR_TOGGLE_PREVIEW,
6 | MARKDOWN_EDITOR_TOGGLE_SIZE
7 | } from './actions'
8 |
9 | const defaultState = {
10 | leftGrow: 50,
11 | rightGrow: 50,
12 | showBigSize: false,
13 | showPreview: true,
14 | }
15 |
16 | export default handleActions({
17 | [MARKDOWN_EDITOR_RESIZE]: (state, action) => {
18 | const { sectionId, dX, dY } = action.payload
19 | const leftDom = document.getElementById('editor_preview_markdown_editor')
20 | const rightDom = document.getElementById('editor_preview_preview')
21 | return ({
22 | ...state,
23 | leftGrow: state.leftGrow * (leftDom.offsetWidth - dX) / leftDom.offsetWidth,
24 | rightGrow: state.rightGrow * (rightDom.offsetWidth + dX) / rightDom.offsetWidth,
25 | })
26 | },
27 | [MARKDOWN_EDITOR_TOGGLE_PREVIEW]: (state, action) => ({
28 | ...state,
29 | showPreview: !state.showPreview,
30 | }),
31 | [MARKDOWN_EDITOR_TOGGLE_SIZE]: (state, action) => ({
32 | ...state,
33 | showBigSize: !state.showBigSize,
34 | }),
35 | }, defaultState)
36 |
--------------------------------------------------------------------------------
/app/components/Editor/components/MarkdownEditor/state.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const state = observable({
4 | leftGrow: 50,
5 | rightGrow: 50,
6 | showBigSize: false,
7 | showPreview: true,
8 | })
9 |
10 | export default state
11 |
--------------------------------------------------------------------------------
/app/components/Editor/components/WelcomeEditor.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function () {
4 | return Welcome
5 | }
6 |
--------------------------------------------------------------------------------
/app/components/Editor/index.js:
--------------------------------------------------------------------------------
1 | import EditorWrapper from './EditorWrapper'
2 | import CodeEditor from './components/CodeEditor'
3 | import EditorWidgets from './components/EditorWidgets'
4 | import modeInfos from './components/CodeEditor/addons/mode/modeInfos'
5 | import * as actions from './actions'
6 |
7 | export default EditorWrapper
8 | export { CodeEditor, EditorWidgets, modeInfos, actions }
9 |
--------------------------------------------------------------------------------
/app/components/Editor/store.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import state, { Editor } from './state'
3 | import FileStore from 'commons/File/store'
4 |
5 | class EditorStore {
6 | constructor () {
7 | Object.assign(this, actions)
8 | }
9 |
10 | getState () { return state }
11 |
12 | get (key) { return state.entities.get(key) }
13 |
14 | isValid (instance) {
15 | return (instance instanceof Editor && state.entities.has(instance.id))
16 | }
17 |
18 | create (config) {
19 | return new Editor(config)
20 | }
21 | }
22 |
23 | const store = new EditorStore()
24 | export default store
25 |
--------------------------------------------------------------------------------
/app/components/FileTree/contextMenuItems.js:
--------------------------------------------------------------------------------
1 | import { gitBlameNode } from './actions'
2 | import i18n from 'utils/createI18n'
3 |
4 | const divider = { isDivider: true }
5 |
6 | const items = [
7 | {
8 | name: i18n`fileTree.contextMenu.newFile`,
9 | icon: 'fa fa-file-text-o',
10 | command: 'file:new_file',
11 | id: 'filetree_menu_new_file',
12 | },
13 | {
14 | name: i18n`fileTree.contextMenu.newFolder`,
15 | icon: 'fa fa-folder-o',
16 | command: 'file:new_folder',
17 |
18 | },
19 | divider,
20 | {
21 | name: i18n`fileTree.contextMenu.delete`,
22 | icon: 'fa fa-trash-o',
23 | command: 'file:delete',
24 | id: 'filetree_menu_delete',
25 | }, {
26 | name: i18n`fileTree.contextMenu.rename`,
27 | icon: 'fa',
28 | command: 'file:rename',
29 | },
30 | divider,
31 | {
32 | name: i18n`fileTree.contextMenu.download`,
33 | icon: 'fa fa-download',
34 | command: 'file:download'
35 | },
36 | {
37 | name: i18n`fileTree.contextMenu.upload`,
38 | icon: 'fa fa-upload',
39 | command: () => {
40 | // reset the files selected from last round
41 | document.getElementById('filetree-hidden-input-form').reset()
42 | const input = document.getElementById('filetree-hidden-input')
43 | input.dispatchEvent(new MouseEvent('click'))
44 | },
45 | getIsHidden: ctx => !ctx.isDir,
46 |
47 | },
48 | divider,
49 | {
50 | name: i18n`fileTree.contextMenu.gitBlame`,
51 | icon: 'fa',
52 | command: (c) => {
53 | gitBlameNode(c)
54 | },
55 | id: 'filetree_menu_gitBlame',
56 | }
57 | ]
58 |
59 | export default items
60 |
--------------------------------------------------------------------------------
/app/components/FileTree/index.js:
--------------------------------------------------------------------------------
1 | import FileTree from './FileTree'
2 | import * as actions from './actions'
3 | import state from './state'
4 |
5 | export default FileTree
6 | export { actions, state }
7 |
--------------------------------------------------------------------------------
/app/components/FileTree/store.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import state from './state'
3 |
4 | class FileTreeStore {
5 | constructor () {
6 | Object.assign(this, actions)
7 | }
8 |
9 | getState () { return state }
10 |
11 | get (key) { return state.entities.get(key) }
12 |
13 | }
14 |
15 | const store = new FileTreeStore()
16 | export default store
17 |
--------------------------------------------------------------------------------
/app/components/Git/GitGraph/helpers/RandColors.js:
--------------------------------------------------------------------------------
1 | const COLORS = [
2 | // the git tower palettes [mod]
3 | '#58c6f3', // blue
4 | '#6de6e9', // aqua
5 | '#ace09e', // grean
6 | '#59b38a', // olive
7 | '#f7d56b', // yellow
8 | '#f5a95f', // orange
9 | '#fe7874', // red
10 | '#967acc', // purple
11 | '#ea75f3', // violet
12 | '#d3d7ed', // light purplish gray
13 | ]
14 |
15 | export default class RandColors {
16 | constructor () {
17 | this.colors = COLORS.slice()
18 | this._index = 0
19 | this._period = this.colors.length
20 | }
21 |
22 | get () {
23 | return this.colors[this._index++ % this._period]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/components/Git/GitGraph/helpers/index.js:
--------------------------------------------------------------------------------
1 | import chroma from './chroma'
2 | import RandColors from './RandColors'
3 |
4 | export { chroma, RandColors }
5 |
--------------------------------------------------------------------------------
/app/components/Git/GitGraph/helpers/roundVertices.js:
--------------------------------------------------------------------------------
1 | const getLength = (p1, p2) => Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2))
2 |
3 | export default function roundVertices (data) {
4 | const DI = 4
5 | const multiplier = 0.7
6 | if (data.length >= 3) {
7 | // mod first and last data point
8 | data = data.slice()
9 | const first = data.shift()
10 | data.unshift([first[0], first[1] - DI])
11 | first[1] += DI * multiplier
12 | data.unshift(first)
13 | const last = data.pop()
14 | data.push([last[0], last[1] + DI])
15 | last[1] -= DI * multiplier
16 | data.push(last)
17 | // end mod
18 | return data.reduce((acc, currentPoint, index, data) => {
19 | // first and last point, noop
20 | if (index === 0 || data.length - 1 === index) {
21 | acc.push(currentPoint)
22 | } else {
23 | const prevPoint = data[index - 1]
24 | const nextPoint = data[index + 1]
25 | const [x_p, y_p] = prevPoint
26 | const [x_n, y_n] = nextPoint
27 | const [x_c, y_c] = currentPoint
28 |
29 | const r_cp = getLength(currentPoint, prevPoint)
30 | const r_cn = getLength(currentPoint, nextPoint)
31 |
32 | const unitVec_cp = [(x_p - x_c) / r_cp, (y_p - y_c) / r_cp]
33 | const unitVec_cn = [(x_n - x_c) / r_cn, (y_n - y_c) / r_cn]
34 |
35 | const curveStartPoint = [unitVec_cp[0] * DI + x_c, unitVec_cp[1] * DI + y_c]
36 | const curveEndPoint = [unitVec_cn[0] * DI + x_c, unitVec_cn[1] * DI + y_c]
37 |
38 | acc.push([curveStartPoint, currentPoint, curveEndPoint])
39 | }
40 |
41 | return acc
42 | }, [])
43 | } else {
44 | return data
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/components/Git/GitGraph/index.js:
--------------------------------------------------------------------------------
1 | import GitGraphTable from './GitGraphTable'
2 | export default GitGraphTable
3 |
--------------------------------------------------------------------------------
/app/components/Git/GitGraph/state.js:
--------------------------------------------------------------------------------
1 | import { observable, computed, action, autorun, autorunAsync, runInAction } from 'mobx'
2 | import { RandColors } from './helpers'
3 | import CommitsState from './helpers/CommitsState'
4 |
5 | const randColors = new RandColors()
6 | const state = observable({
7 | refs: observable.map({}),
8 | rawCommits: observable.map({}),
9 | commitsState: observable.shallowObject({}),
10 | })
11 |
12 | autorun('parse commits', () => {
13 | state.commitsState = new CommitsState({
14 | rawCommits: state.rawCommits.values(),
15 | })
16 | })
17 |
18 | autorun('connect refs', () => {
19 | state.commitsState.refs = state.refs
20 | })
21 |
22 | export default state
23 |
--------------------------------------------------------------------------------
/app/components/Git/index.js:
--------------------------------------------------------------------------------
1 | import GitBranchWidget from './GitBranchWidget'
2 | import GitCommitView from './GitCommitView'
3 |
4 | export { GitBranchWidget, GitCommitView }
5 |
--------------------------------------------------------------------------------
/app/components/Mask/Mask.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { observer } from 'mobx-react'
4 | import state from './state'
5 |
6 | @observer
7 | class Mask extends Component {
8 | render () {
9 | if (state.operating) {
10 | return (
11 |
12 |
13 |
14 | {state.operatingMessage}
15 |
16 |
17 | )
18 | }
19 | return
20 | }
21 | }
22 |
23 | export default Mask
24 |
--------------------------------------------------------------------------------
/app/components/Mask/actions.js:
--------------------------------------------------------------------------------
1 | import { registerAction } from 'utils/actions'
2 | import state from './state'
3 |
4 | export const showMask = registerAction('mask:show_mask', ({ message = 'Working...' }) => {
5 | state.operating = true
6 | state.operatingMessage = message
7 | })
8 |
9 | export const hideMask = registerAction('mask:hide_mask', () => {
10 | state.operating = false
11 | state.operatingMessage = ''
12 | })
13 |
--------------------------------------------------------------------------------
/app/components/Mask/index.js:
--------------------------------------------------------------------------------
1 | import Mask from './Mask'
2 | import * as maskActions from './actions'
3 |
4 | export { maskActions }
5 | export default Mask
6 |
7 |
--------------------------------------------------------------------------------
/app/components/Mask/state.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const state = observable({
4 | operating: false,
5 | operatingMessage: '',
6 | })
7 |
8 | export default state
9 |
--------------------------------------------------------------------------------
/app/components/Menu/MenuContextTypes.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | export default {
4 | subscribe: PropTypes.func,
5 | setFocus: PropTypes.func,
6 | getFocus: PropTypes.func,
7 | menuContext: PropTypes.any.isRequired,
8 | deactivateTopLevelMenu: PropTypes.func,
9 | activatePrevTopLevelMenuItem: PropTypes.func,
10 | activateNextTopLevelMenuItem: PropTypes.func,
11 | }
12 |
--------------------------------------------------------------------------------
/app/components/Menu/index.js:
--------------------------------------------------------------------------------
1 | import Menu from './Menu'
2 |
3 | export default Menu
4 |
--------------------------------------------------------------------------------
/app/components/MenuBar/actions.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding/WebIDE-Frontend/fab3d991cd90be0c04ec7c57332bca32c6e9cddf/app/components/MenuBar/actions.js
--------------------------------------------------------------------------------
/app/components/MenuBar/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { observer } from 'mobx-react'
3 | import MenuBar from './MenuBar'
4 | import menuBarItems from './menuBarItems'
5 | import state from './state'
6 |
7 | const MenuBarContainer = observer((() => ))
8 |
9 | export default MenuBarContainer
10 | export { menuBarItems }
11 |
--------------------------------------------------------------------------------
/app/components/MenuBar/state.js:
--------------------------------------------------------------------------------
1 | import menuBarItems from './menuBarItems'
2 | import MenuScope from 'commons/Menu/state'
3 | import { autorun } from 'mobx'
4 | import { observable } from 'mobx'
5 |
6 | const { state, MenuItem } = MenuScope(menuBarItems)
7 |
8 | autorun(() => {
9 | state.items = observable.shallowArray(menuBarItems.map((opts) => {
10 | return new MenuItem(opts)
11 | }))
12 | })
13 |
14 | export default state
15 |
--------------------------------------------------------------------------------
/app/components/MenuBar/store.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import { extendObservable, observable } from 'mobx'
3 | import state from './state'
4 |
5 |
6 | class MenuBarStore {
7 | constructor () {
8 | Object.assign(this, actions)
9 | this.labels = observable.map({})
10 | }
11 |
12 | getState () { return state }
13 |
14 | get (path) {
15 | return this.getMenuItemByPath(path)
16 | }
17 | }
18 |
19 |
20 | const store = new MenuBarStore()
21 | export default (store)
22 |
--------------------------------------------------------------------------------
/app/components/Modal/FilePalette/index.js:
--------------------------------------------------------------------------------
1 | import FilePalette from './component'
2 |
3 | export { FilePalette }
4 |
--------------------------------------------------------------------------------
/app/components/Modal/index.js:
--------------------------------------------------------------------------------
1 | import Modal from './Modal'
2 | import * as actions from './actions'
3 |
4 | export default Modal
5 | export { actions }
6 |
--------------------------------------------------------------------------------
/app/components/Modal/modals/Alert.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import i18n from 'utils/createI18n'
3 | import { dismissModal } from '../actions'
4 |
5 | const Alert = (props) => {
6 | const { meta, content } = props
7 | return (
8 |
9 | { content.header ?
10 |
{content.header}
11 | : null }
12 |
13 | { content.message ?
14 |
{content.message}
15 | : null }
16 |
17 | { content.statusMessage ?
18 |
{content.statusMessage}
19 | : null }
20 |
21 |
22 |
28 |
29 |
30 | )
31 | }
32 |
33 | export default Alert
34 |
--------------------------------------------------------------------------------
/app/components/Modal/modals/Confirm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import i18n from 'utils/createI18n'
3 |
4 | const Confirm = (props) => {
5 | const { meta, content } = props
6 | return (
7 |
8 | { content.header ?
9 |
{content.header}
10 | : null }
11 |
12 | { content.message ?
13 |
{content.message}
14 | : null }
15 |
16 | { content.statusMessage ?
17 |
{content.statusMessage}
18 | : null }
19 |
20 |
21 |
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default Confirm
29 |
--------------------------------------------------------------------------------
/app/components/Modal/modals/Form.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import i18n from 'utils/createI18n'
3 |
4 | const Form = (props) => {
5 | const { meta, content } = props
6 | return (
7 |
8 | { content.header ?
9 |
{content.header}
10 | : null }
11 |
12 | { content.message ?
13 |
{content.message}
14 | : null }
15 |
16 | { content.statusMessage ?
17 |
{content.statusMessage}
18 | : null }
19 |
20 |
21 |
30 |
33 |
34 |
35 | )
36 | }
37 |
38 | export default Form
39 |
--------------------------------------------------------------------------------
/app/components/Modal/modals/Prompt.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { setSelectionRange } from 'utils'
3 | import { dismissModal } from '../actions'
4 |
5 | class Prompt extends Component {
6 | constructor (props) {
7 | super(props)
8 | this.state = { value: props.content.defaultValue || '' }
9 | }
10 |
11 | componentDidMount () {
12 | if (this.props.content.selectionRange) {
13 | setSelectionRange(this.input, ...this.props.content.selectionRange)
14 | }
15 | }
16 |
17 | render () {
18 | const {meta, content} = this.props
19 | return (
20 |
21 | { content.message ?
22 |
{content.message}
23 | : null }
24 |
this.input =r}
27 | onChange={e => this.setState({ value: e.target.value })}
28 | onKeyDown={this.onKeyDown}
29 | value={this.state.value}
30 | placeholder={content.placeholder}
31 | />
32 | { content.statusMessage ?
33 |
34 |
35 | {content.statusMessage}
36 |
37 | : null }
38 |
39 | )
40 | }
41 |
42 | confirm (value) {
43 | this.props.meta.resolve(value)
44 | }
45 |
46 | cancel = () => {
47 | this.props.meta.reject()
48 | }
49 |
50 | onKeyDown = e => {
51 | if (e.keyCode === 13) {
52 | this.confirm(e.target.value)
53 | }
54 | }
55 | }
56 |
57 | export default Prompt
58 |
--------------------------------------------------------------------------------
/app/components/Modal/modals/index.js:
--------------------------------------------------------------------------------
1 | export Prompt from './Prompt'
2 | export Confirm from './Confirm'
3 | export Alert from './Alert'
4 | export Form from './Form'
5 | export { GitCommitView } from '../../Git'
6 | export { CommandPalette } from '../../../commands'
7 | export { FilePalette } from '../FilePalette'
8 | export GitStashView from '../../Git/modals/stash'
9 | export GitUnstashView from '../../Git/modals/unstash'
10 | export GitResetView from '../../Git/modals/reset'
11 | export GitTagView from '../../Git/modals/tag'
12 | export GitMergeView from '../../Git/modals/merge'
13 | export GitNewBranchView from '../../Git/modals/newBranch'
14 | export SettingsView from '../../Setting'
15 | export GitRebaseStart from '../../Git/modals/rebaseStart'
16 | export GitResolveConflictsView from '../../Git/modals/resolveConflicts'
17 | export GitMergeFileView from '../../Git/modals/mergeFile'
18 | export GitDiffFileView from '../../Git/modals/diffFile'
19 | export GitRebasePrepare from '../../Git/modals/rebasePrepare'
20 | export GitRebaseInput from '../../Git/modals/rebaseInput'
21 | export GitCommitDiffView from '../../Git/modals/commitDiff'
22 | export GitCheckoutView from '../../Git/modals/checkout'
23 | export GitCheckoutStashView from '../../Git/modals/checkoutStash'
24 | export FileSelectorView from '../FileSelector'
25 |
--------------------------------------------------------------------------------
/app/components/Modal/modals/modalCache.js:
--------------------------------------------------------------------------------
1 | // import React, { Component } from 'react'
2 | // import cx from 'classnames'
3 | import { observable } from 'mobx'
4 | import { observer, inject } from 'mobx-react'
5 |
6 | import {
7 | Prompt,
8 | Confirm,
9 | Alert,
10 | SettingsView,
11 | CommandPalette,
12 | FilePalette,
13 | GitCommitView,
14 | GitStashView,
15 | GitUnstashView,
16 | GitResetView,
17 | GitTagView,
18 | GitMergeView,
19 | GitNewBranchView,
20 | GitRebaseStart,
21 | GitResolveConflictsView,
22 | GitMergeFileView,
23 | GitDiffFileView,
24 | GitRebasePrepare,
25 | GitRebaseInput,
26 | GitCommitDiffView,
27 | GitCheckoutView,
28 | GitCheckoutStashView,
29 | FileSelectorView,
30 | Form,
31 | } from './index'
32 |
33 |
34 | const modalCache = observable.map({
35 | Form,
36 | GitCommit: GitCommitView,
37 | GitResolveConflicts: GitResolveConflictsView,
38 | GitCommitDiff: GitCommitDiffView,
39 | GitStash: GitStashView,
40 | GitUnstash: GitUnstashView,
41 | GitTag: GitTagView,
42 | GitMerge: GitMergeView,
43 | GitNewBranch: GitNewBranchView,
44 | GitResetHead: GitResetView,
45 | GitRebaseStart,
46 | GitRebasePrepare,
47 | GitRebaseInput,
48 | GitMergeFile: GitMergeFileView,
49 | GitCheckout: GitCheckoutView,
50 | GitCheckoutStash: GitCheckoutStashView,
51 | Prompt,
52 | Confirm,
53 | Alert,
54 | CommandPalette,
55 | FilePalette,
56 | Settings: SettingsView,
57 | FileSelectorView,
58 | GitDiffFile: GitDiffFileView,
59 | })
60 |
61 | window.modalCache = modalCache
62 |
63 | export default modalCache
64 |
--------------------------------------------------------------------------------
/app/components/Modal/state.js:
--------------------------------------------------------------------------------
1 | import { observable, extendObservable } from 'mobx'
2 |
3 | class Modal {
4 | // make modal.content observable, to faciliate modal content updating
5 |
6 | constructor (opt) {
7 | let resolve, reject
8 | const promise = new Promise((rsv, rjt) => {
9 | resolve = rsv
10 | reject = rjt
11 | })
12 | const defaults = {
13 | id: 0,
14 | isActive: false,
15 | showBackdrop: false,
16 | position: 'top',
17 | content: {},
18 | meta: { promise, resolve, reject },
19 | }
20 | extendObservable(this, defaults, opt)
21 | }
22 |
23 | update (content) {
24 | let resolve, reject
25 | const promise = new Promise((rsv, rjt) => {
26 | resolve = rsv
27 | reject = rjt
28 | })
29 | this.content = { ...this.content, ...content }
30 | this.meta = { promise, resolve, reject }
31 | }
32 | }
33 |
34 | const state = observable({
35 | stack: observable.shallowArray(),
36 | })
37 |
38 | export default state
39 | export { Modal }
40 |
--------------------------------------------------------------------------------
/app/components/Notification/actions.js:
--------------------------------------------------------------------------------
1 | import { registerAction } from 'utils/actions'
2 | import state from './state'
3 |
4 | export const NOTIFY_TYPE = {
5 | ERROR: 'error',
6 | INFO: 'info',
7 | }
8 |
9 | export const NOTIFICATION_REMOVE = 'NOTIFICATION_REMOVE'
10 | export const removeNotification = registerAction.normal(NOTIFICATION_REMOVE, (notifKey) => {
11 | const notifToRemove = state.notifications.find(notif => notif.key === notifKey)
12 | state.notifications.remove(notifToRemove)
13 | })
14 |
15 | const NOTIFICATION_ADD = 'NOTIFICATION_ADD'
16 | export const addNotification = registerAction.normal(NOTIFICATION_ADD,
17 | (notification) => {
18 | const notifKey = Date.now()
19 | let defaultNotification = {
20 | message: '',
21 | action: 'Dismiss',
22 | key: notifKey,
23 | dismissAfter: 6000,
24 | onClick: () => removeNotification(notifKey)
25 | }
26 |
27 | if (notification.notifyType === NOTIFY_TYPE.ERROR) {
28 | defaultNotification = {
29 | ...defaultNotification,
30 | barStyle: { backgroundColor: 'red' },
31 | actionStyle: { color: 'white' },
32 | }
33 | }
34 |
35 | return { ...defaultNotification, ...notification }
36 | },
37 | (notification) => {
38 | state.notifications.push(notification)
39 | }
40 | )
41 |
42 | export const notify = addNotification
43 |
--------------------------------------------------------------------------------
/app/components/Notification/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { inject } from 'mobx-react'
3 | import { NotificationStack } from 'react-notification'
4 | import * as actions from './actions'
5 | import state from './state'
6 |
7 | const barStyleFactory = (index, style) => {
8 | return Object.assign({}, style, {
9 | left: 'initial',
10 | right: '-100%',
11 | bottom: 'initial',
12 | top: `${2 + index * 4}rem`,
13 | fontSize: '12px',
14 | padding: '8px',
15 | })
16 | }
17 |
18 | const activeBarStyleFactory = (index, style) => {
19 | return Object.assign({}, style, {
20 | left: 'initial',
21 | right: '1rem',
22 | bottom: 'initial',
23 | top: `${2 + index * 4}rem`,
24 | fontSize: '12px',
25 | padding: '8px',
26 | })
27 | }
28 |
29 | class Notification extends Component {
30 | constructor (props) {
31 | super()
32 | }
33 | render () {
34 | const { notifications } = this.props
35 | return (
36 | actions.removeNotification(notification.key)}
39 | barStyleFactory={barStyleFactory}
40 | activeBarStyleFactory={activeBarStyleFactory}
41 | />
42 | )
43 | }
44 | }
45 |
46 | export default inject(() => {
47 | const notifications = state.notifications.toJS()
48 | return { notifications }
49 | })(Notification)
50 |
51 | export { actions, state }
52 |
--------------------------------------------------------------------------------
/app/components/Notification/state.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const state = observable({
4 | notifications: [],
5 | })
6 |
7 | export default state
8 |
--------------------------------------------------------------------------------
/app/components/Pane/Pane.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { observer } from 'mobx-react'
4 | import cx from 'classnames'
5 | import { confirmResize } from './actions'
6 | import TabContainer from 'components/Tab'
7 | import PaneAxis from './PaneAxis'
8 | import ResizeBar from '../ResizeBar'
9 |
10 |
11 | const Pane = observer(props => {
12 | const { pane, parentFlexDirection } = props
13 | const style = { flexGrow: pane.size, display: pane.display }
14 | const closePane = pane.isRoot ? null : pane.destroy.bind(pane)
15 |
16 | return (
17 | {pane.views.length // priortize `pane.views` over `pane.content`
26 | ?
27 | :
28 |
29 |
30 | }
31 |
34 |
35 | )
36 | })
37 |
38 | Pane.propTypes = {
39 | pane: PropTypes.object,
40 | parentFlexDirection: PropTypes.string,
41 | }
42 |
43 | export default Pane
44 |
--------------------------------------------------------------------------------
/app/components/Pane/PaneAxis.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { observer } from 'mobx-react'
4 | import cx from 'classnames'
5 | import Pane from './Pane'
6 |
7 | @observer
8 | class PaneAxis extends Component {
9 | static propTypes = {
10 | id: PropTypes.string,
11 | pane: PropTypes.object,
12 | };
13 |
14 | static childContextTypes = { onResizing: PropTypes.func };
15 |
16 | constructor (props) {
17 | super(props)
18 | this.resizingListeners = []
19 | }
20 |
21 | getChildContext () { return { onResizing: this.onResizing.bind(this) } }
22 | onResizing (listener) { if (typeof listener === 'function') { this.resizingListeners.push(listener) } }
23 |
24 | render () {
25 | const selfPane = this.props.pane
26 | let Subviews
27 | if (selfPane.views.length) {
28 | Subviews = selfPane.views.map(pane =>
29 |
30 | )
31 | } else {
32 | Subviews =
33 | }
34 |
35 | return (
36 | {Subviews}
40 |
41 | )
42 | }
43 | }
44 |
45 | export default PaneAxis
46 |
--------------------------------------------------------------------------------
/app/components/Pane/PanesContainer.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { inject } from 'mobx-react'
3 | import PaneAxis from './PaneAxis'
4 |
5 | var PrimaryPaneAxis = inject(state => {
6 | let rootPane = state.PaneState.rootPane
7 | return { pane: rootPane }
8 | })(PaneAxis)
9 |
10 | var PanesContainer = () => {
11 | return
12 | }
13 |
14 | export default PanesContainer
15 |
--------------------------------------------------------------------------------
/app/components/Pane/index.js:
--------------------------------------------------------------------------------
1 | import PanesContainer from './PanesContainer'
2 |
3 | export default PanesContainer
4 |
--------------------------------------------------------------------------------
/app/components/Panel/Panel.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import { observer } from 'mobx-react'
4 | import cx from 'classnames'
5 | import PanelAxis from './PanelAxis'
6 | import PanelContent from './PanelContent'
7 | import { confirmResize } from './actions'
8 | import ResizeBar from '../ResizeBar'
9 |
10 | const Panel = observer((props) => {
11 | const { panel, parentFlexDirection } = props
12 | const style = {}
13 | if (panel.resizable) {
14 | style.flexGrow = panel.size
15 | } else {
16 | style.flexGrow = 0
17 | style.flexBasis = 'auto'
18 | }
19 | if (panel.hidden) style.display = 'none'
20 | if (panel.disabled) style.display = 'none'
21 | if (panel.overflow) style.overflow = panel.overflow
22 |
23 | return (
24 | { panel.views.length
32 | ?
33 | :
34 | }
35 | {panel.disableResizeBar
36 | ? null
37 | :
40 | }
41 |
42 | )
43 | })
44 |
45 | Panel.propTypes = {
46 | panel: PropTypes.object.isRequired,
47 | parentFlexDirection: PropTypes.string.isRequired,
48 | }
49 |
50 | export default Panel
51 |
--------------------------------------------------------------------------------
/app/components/Panel/PanelAxis.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import cx from 'classnames'
4 | import Panel from './Panel'
5 |
6 | class PanelAxis extends Component {
7 | static propTypes = {
8 | id: PropTypes.string,
9 | panel: PropTypes.object,
10 | className: PropTypes.string,
11 | style: PropTypes.object,
12 | };
13 |
14 | render () {
15 | const { panel, className, style } = this.props
16 | let subviews
17 | if (panel.views.length) {
18 | subviews = panel.views.map(childPanel =>
19 |
20 | )
21 | } else {
22 | subviews =
23 | }
24 |
25 | return (
26 | {subviews}
30 | )
31 | }
32 | }
33 |
34 | export default PanelAxis
35 |
--------------------------------------------------------------------------------
/app/components/Panel/PanelsContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { inject } from 'mobx-react'
3 | import PanelState from './state'
4 | import PanelAxis from './PanelAxis'
5 |
6 |
7 | const PrimaryPanelAxis = inject(() =>
8 | ({ panel: PanelState.rootPanel })
9 | )(PanelAxis)
10 |
11 | const PanelsContainer = () => {
12 | return ()
13 | }
14 |
15 | export default PanelsContainer
16 |
--------------------------------------------------------------------------------
/app/components/Panel/SideBar/SideBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import cx from 'classnames'
4 | import PluginArea from 'components/Plugins/component'
5 | import { SIDEBAR } from 'components/Plugins/constants'
6 | import { observer } from 'mobx-react'
7 | import { toggleSidePanelView } from './actions'
8 |
9 |
10 | /* shape of label
11 | label = {
12 | text: String,
13 | icon: String,
14 | viewId: String,
15 | }
16 | */
17 |
18 |
19 | const SideBarLabel = ({ label, isActive, onClick }) => (
20 |
26 |
27 |
28 |
29 | {label.text}
30 |
31 |
32 |
33 | )
34 |
35 | SideBarLabel.propTypes = {
36 | isActive: PropTypes.bool,
37 | onClick: PropTypes.func
38 | }
39 |
40 | const SideBar = observer(({ side }) => (
41 | !plugin.status.hidden}
45 | getChildView={plugin => (
46 | toggleSidePanelView(plugin.viewId)}
50 | isActive={plugin.status.get('active')}
51 | />
52 | )}
53 | />))
54 |
55 |
56 | SideBar.propTypes = {
57 | // labels: labelsShape,
58 | side: PropTypes.string,
59 | activeViewId: PropTypes.string,
60 | activateView: PropTypes.func
61 | }
62 |
63 |
64 | export default SideBar
65 |
--------------------------------------------------------------------------------
/app/components/Panel/actions.js:
--------------------------------------------------------------------------------
1 | import { registerAction } from 'utils/actions'
2 | import _ from 'lodash'
3 | import state from './state'
4 |
5 |
6 | export const PANEL_CONFIRM_RESIZE = 'PANEL_CONFIRM_RESIZE'
7 | export const confirmResize = registerAction(PANEL_CONFIRM_RESIZE,
8 | (leftViewId, leftSize, rightViewId, rightSize) => ({
9 | leftView: { id: leftViewId, size: leftSize },
10 | rightView: { id: rightViewId, size: rightSize },
11 | }),
12 | ({ leftView, rightView }) => {
13 | state.panels.get(leftView.id).size = leftView.size
14 | state.panels.get(rightView.id).size = rightView.size
15 | }
16 | )
17 |
18 | export const PANEL_TOGGLE_LAYOUT = 'PANEL_TOGGLE_LAYOUT'
19 | export const togglePanelLayout = registerAction(PANEL_TOGGLE_LAYOUT,
20 | (selectors, shouldShow) => ({ selectors, shouldShow }),
21 | ({ selectors, shouldShow }) => {
22 | const selectedPanels = selectors.map(panelId => state.panels.get(panelId))
23 | selectedPanels.forEach((panel) => {
24 | if (shouldShow === undefined) {
25 | panel.hidden ? panel.show() : panel.hide()
26 | } else if (shouldShow) {
27 | panel.show()
28 | } else {
29 | panel.hide()
30 | }
31 | })
32 | }
33 | )
34 |
35 | export const PRIMIARY_PANEL_BLUR_CONTROLER = 'PRIMIARY_PANEL_BLUR_CONTROLER'
36 |
37 | export const primiaryPanelBlurControler = registerAction(PRIMIARY_PANEL_BLUR_CONTROLER,
38 | (shouldShow) => {
39 | state.primaryPanelAxis.blur = shouldShow
40 | })
41 |
42 |
--------------------------------------------------------------------------------
/app/components/Panel/index.js:
--------------------------------------------------------------------------------
1 | import PanelsContainer from './PanelsContainer'
2 |
3 | export default PanelsContainer
4 |
--------------------------------------------------------------------------------
/app/components/Plugins/component.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { observer } from 'mobx-react'
3 | import store from './store'
4 |
5 | function getChildren (children) {
6 | if (!children) return []
7 | return Array.isArray(children) ? children : [children]
8 | }
9 |
10 |
11 | const PluginArea = observer(({ position = '', childProps = {}, children, getChildView, filter, ...others }) => {
12 | const pluginsArray = store.plugins.values().filter(plugin => plugin.position === position)
13 |
14 | const pluginComponents = pluginsArray
15 | .filter(filter || (() => true))
16 | .sort((pluginA, pluginB) => (pluginA.label.weight || 0) < (pluginB.label.weight || 0) ? 1 : -1)
17 | .map((plugin) => {
18 | const view = store.views[plugin.viewId]
19 | return getChildView ? getChildView(plugin, view) :
20 | React[React.isValidElement(view) ? 'cloneElement' : 'createElement'](view, {
21 | key: plugin.viewId,
22 | ...childProps,
23 | })
24 | })
25 | // 允许提供children的必有不可插拔项
26 | return (
27 |
28 | {getChildren(children).concat(pluginComponents)}
29 |
30 | )
31 | })
32 |
33 |
34 | export default PluginArea
35 |
--------------------------------------------------------------------------------
/app/components/Plugins/constants.js:
--------------------------------------------------------------------------------
1 | // 菜单栏
2 | export const MENUBAR = {
3 | // 菜单栏右上角的 widget
4 | WIDGET: 'MENUBAR.WIDGET'
5 | }
6 | // 侧边烂区域
7 | export const SIDEBAR = {
8 | RIGHT: 'SIDEBAR.RIGHT',
9 | LEFT: 'SIDEBAR.LEFT',
10 | BOTTOM: 'SIDEBAR.BOTTOM'
11 | }
12 |
13 | export const TOPBAR = {
14 | WIDGET: 'TOPBAR.WIDGET'
15 | }
16 | // containers 最外层区域
17 | export const CONTAINERS = {
18 | UTILITIES: 'CONTAINERS.UTILITIES'
19 | }
20 |
21 | export const TERMINAL = {
22 | ENV: 'TERMINAL.ENV'
23 | }
24 |
--------------------------------------------------------------------------------
/app/components/Plugins/index.js:
--------------------------------------------------------------------------------
1 | import PluginArea from './component'
2 | import * as actions from './actions'
3 | import * as position from './constants'
4 |
5 | export default { PluginArea, actions, position }
6 |
--------------------------------------------------------------------------------
/app/components/Plugins/store.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const store = {
4 | views: {},
5 | plugins: observable.map({}),
6 | list: observable([]),
7 | toJS () {
8 | const requiredList = this.list.toJS().filter(obj => obj.enabled && obj.requirement !== 'Required')
9 | return requiredList
10 | }
11 | }
12 |
13 | // for test
14 | window.pluginStore = store
15 |
16 | export default store
17 |
--------------------------------------------------------------------------------
/app/components/Prompt/Prompt.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | class Prompts extends Component {
5 | render () {
6 | const { prompts, handleClose } = this.props
7 | return (
8 |
9 | {prompts.map((prompt, idx) => (
10 |
15 | {prompt.content}
16 | handleClose(prompt.id, prompt.type)}>×
17 |
18 | ))}
19 |
20 | )
21 | }
22 | }
23 |
24 | Prompts.propTypes = {
25 | prompts: PropTypes.array,
26 | handleClose: PropTypes.func,
27 | }
28 |
29 | export default Prompts
30 |
--------------------------------------------------------------------------------
/app/components/Setting/EditorSetting.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { observer } from 'mobx-react'
3 | import i18n from 'utils/createI18n'
4 | import { dismissModal } from 'components/Modal/actions'
5 | import { openFile } from 'commands/commandBindings/file'
6 | import TabStore from 'components/Tab/store'
7 | import FormInputGroup from './FormInputGroup'
8 |
9 | const editorconfigDefaultContent = `# See: http://editorconfig.org
10 | root = true
11 |
12 | [*]
13 | indent_style =
14 | indent_size =
15 | tab_width =
16 | trim_trailing_whitespace =
17 | insert_final_newline =
18 | `
19 |
20 | const openEditorConfigFile = (e) => {
21 | e.preventDefault()
22 | dismissModal()
23 | openFile({ path: '/.editorconfig' }).catch((err) => {
24 | TabStore.createTab({
25 | icon: 'fa fa-file-text-o',
26 | title: '.editorconfig',
27 | editor: { content: editorconfigDefaultContent }
28 | })
29 | })
30 | }
31 |
32 | export default observer(({ content }) => (
33 |
34 |
{i18n`settings.editor.main`}
35 |
40 |
41 | {content.items.map(settingItem =>
42 |
46 | )}
47 |
48 |
49 | ))
50 |
--------------------------------------------------------------------------------
/app/components/Setting/KeymapSetting.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { observer } from 'mobx-react'
3 | import i18n from 'utils/createI18n'
4 | import FormInputGroup from './FormInputGroup'
5 |
6 | export default observer(({ content }) => (
7 |
8 |
{i18n`settings.keymap.main`}
9 |
10 | {content.items.map(settingItem =>
11 |
15 | )}
16 |
17 |
18 | ))
19 |
--------------------------------------------------------------------------------
/app/components/Setting/SettingForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { observer } from 'mobx-react'
3 | import FormInputGroup from './FormInputGroup'
4 |
5 | export default observer(({ header, content }) => (
6 |
7 |
{ header }
8 |
9 | {content.items.map(settingItem =>
10 |
15 | )}
16 |
17 |
18 | ))
19 |
--------------------------------------------------------------------------------
/app/components/StatusBar/StatusBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { GitBranchWidget } from 'components/Git'
3 | import { dispatchCommand } from 'commands'
4 | import { EditorWidgets } from 'components/Editor'
5 | import { observer } from 'mobx-react'
6 | import UploadWidgets from './UploadWidgets'
7 |
8 | const StatusBar = observer(({ messages=[] }) => {
9 | return (
10 |
11 |
12 |
dispatchCommand('view:toggle_bars')} >
13 |
14 |
15 |
16 | {messages.map(message =>
{message}
)}
17 |
18 |
19 |
20 | window.refs.GitBranchWidget = com}
21 | />
22 |
23 | )
24 | })
25 |
26 | export default StatusBar
27 |
--------------------------------------------------------------------------------
/app/components/StatusBar/actions.js:
--------------------------------------------------------------------------------
1 | import isObject from 'lodash/isObject'
2 | import { registerAction } from 'utils/actions'
3 | import state from './state'
4 |
5 | export const STATUS_ADD_MESSAGE = 'status:add_message'
6 | export const addMessage = registerAction(STATUS_ADD_MESSAGE,
7 | (messageKey, messageText, autoDismiss) => {
8 | if (isObject(messageKey)) return messageKey
9 | return { key: messageKey, text: messageText, autoDismiss }
10 | },
11 | (message) => {
12 | const { text, key, autoDismiss } = message
13 | state.messages.set(key, text)
14 | const DISMISS_AFTER_MS = 3000
15 | if (autoDismiss) {
16 | setTimeout(() => {
17 | state.messages.delete(key)
18 | }, DISMISS_AFTER_MS)
19 | }
20 | }
21 | )
22 |
23 | export const STATUS_REMOVE_MESSAGE = 'status:remove_message'
24 | export const removeMessage = registerAction(STATUS_REMOVE_MESSAGE, (messageKey) => {
25 | if (!messageKey) messageKey = state.messages.keys().pop()
26 | state.messages.delete(messageKey)
27 | })
28 |
--------------------------------------------------------------------------------
/app/components/StatusBar/index.js:
--------------------------------------------------------------------------------
1 | import { inject, observer } from 'mobx-react'
2 | import StatusBar from './StatusBar'
3 | import state from './state'
4 |
5 | export default inject(() => ({ messages: state.messages.values() }))(StatusBar)
6 |
--------------------------------------------------------------------------------
/app/components/StatusBar/state.js:
--------------------------------------------------------------------------------
1 | import { observable, autorun } from 'mobx'
2 |
3 | const state = observable({
4 | messages: observable.map(),
5 | uploadList: observable.map({
6 | // 'aaa.txt': { percentCompleted: 0, size: 100020303 },
7 | // 'bbb.txt': { percentCompleted: 50, size: 200020303 },
8 | }),
9 | setFileUploadInfo: ({ path, info }) => {
10 | const { percentCompleted, size } = info
11 | state.uploadList.set(path, { percentCompleted, size })
12 | if (percentCompleted === 100) {
13 | state.checkUploadInfo()
14 | }
15 | },
16 | removeFileUploadInfo: ({ path }) => {
17 | state.uploadList.delete(path)
18 | },
19 | checkUploadInfo: () => {
20 | let totalPercent = 0
21 | const uploadList = state.uploadList.entries()
22 | uploadList.forEach((item) => {
23 | totalPercent += item[1].percentCompleted
24 | })
25 | if (totalPercent === 100 * uploadList.length) {
26 | setTimeout(() => state.uploadList = observable.map(), 3000)
27 | }
28 | }
29 | })
30 |
31 | export default state
32 |
--------------------------------------------------------------------------------
/app/components/Tab/index.js:
--------------------------------------------------------------------------------
1 | import TabContainer from './TabContainer'
2 | import * as actions from './actions'
3 | import * as state from './state'
4 |
5 | export default TabContainer
6 | export { actions, state }
7 |
--------------------------------------------------------------------------------
/app/components/Tab/store.js:
--------------------------------------------------------------------------------
1 | import * as actions from './actions'
2 | import state, { Tab, TabGroup } from './state'
3 |
4 | class TabStore {
5 | constructor () {
6 | Object.assign(this, actions)
7 | }
8 |
9 | getState () { return state }
10 |
11 | getTab (key) { return state.tabs.get(key) }
12 | getTabGroup (key) { return state.tabGroups.get(key) }
13 |
14 | findTab (predicate) {
15 | const state = this.getState()
16 | return state.tabs.values().filter(predicate)
17 | }
18 |
19 | isValidTab (instance) {
20 | return (instance instanceof Tab && state.tabs.has(instance.id))
21 | }
22 |
23 | isValidTabGroup (instance) {
24 | return (instance instanceof TabGroup && state.tabGroups.has(instance.id))
25 | }
26 |
27 | }
28 |
29 | const store = new TabStore()
30 | export default store
31 |
--------------------------------------------------------------------------------
/app/components/Terminal/index.js:
--------------------------------------------------------------------------------
1 | import TerminalContainer from './TerminalContainer'
2 |
3 | export default TerminalContainer
4 |
--------------------------------------------------------------------------------
/app/components/Terminal/state.js:
--------------------------------------------------------------------------------
1 | import uniqueId from 'lodash/uniqueId'
2 | import is from 'utils/is'
3 | import { TabStateScope } from 'commons/Tab'
4 | import config from 'config'
5 |
6 | const { Tab: BaseTab, TabGroup: BaseTabGroup, state } = TabStateScope()
7 | state.keepOne = config.isLib
8 |
9 | class Tab extends BaseTab {
10 | constructor (props = {}) {
11 | super()
12 | this.id = is.undefined(props.id) ? uniqueId('tab_') : props.id
13 | state.tabs.set(this.id, this)
14 | }
15 | }
16 |
17 | class TabGroup extends BaseTabGroup {
18 | constructor (props = {}) {
19 | super()
20 | this.id = is.undefined(props.id) ? uniqueId('tab_group_') : props.id
21 | state.tabGroups.set(this.id, this)
22 | }
23 | }
24 |
25 | export default state
26 | export { Tab, TabGroup, state }
27 |
--------------------------------------------------------------------------------
/app/components/Tooltip/TooltipTrigger.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import is from 'utils/is'
3 | import PropTypes from 'prop-types'
4 | import { addTooltip, removeTooltip } from './state'
5 |
6 | class TooltipTrigger extends Component {
7 | static defaultProps = {
8 | placement: 'top',
9 | }
10 |
11 | static propTypes = {
12 | placement: PropTypes.string,
13 | shouldShow: PropTypes.func,
14 | children: PropTypes.any,
15 | content: PropTypes.any,
16 | }
17 |
18 | onMouseOver = (e) => {
19 | const tooltipProps = this.getTooltipProps()
20 | if (!tooltipProps) return
21 | const tooltip = addTooltip(tooltipProps)
22 | e.stopPropagation()
23 | const dismissTooltip = (e) => {
24 | removeTooltip(tooltip)
25 | window.removeEventListener('mouseover', dismissTooltip)
26 | }
27 | window.addEventListener('mouseover', dismissTooltip)
28 | }
29 |
30 | getTooltipProps () {
31 | if (is.function(this.props.shouldShow) && this.props.shouldShow() === false) return null
32 | return {
33 | rect: this.dom.getBoundingClientRect(),
34 | placement: this.props.placement,
35 | content: this.props.content,
36 | }
37 | }
38 |
39 | render () {
40 | const childrenCount = React.Children.count(this.props.children)
41 | if (childrenCount === 1) {
42 | return React.cloneElement(this.props.children, {
43 | ref: r => this.dom = r,
44 | onMouseOver: this.onMouseOver,
45 | })
46 | }
47 | return null
48 | }
49 | }
50 |
51 | export default TooltipTrigger
52 |
--------------------------------------------------------------------------------
/app/components/Tooltip/Tooltips.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { observer } from 'mobx-react'
3 | import cx from 'classnames'
4 | import state from './state'
5 | import _ from 'lodash'
6 |
7 | const Tooltip = observer(({ placement, rect, content, show }) => {
8 | let x, y
9 | switch (placement) {
10 | case 'top':
11 | x = rect.left + rect.width / 2
12 | y = rect.top
13 | break
14 | case 'bottom':
15 | x = rect.left + rect.width / 2
16 | y = rect.bottom
17 | break
18 | case 'left':
19 | x = rect.left
20 | y = rect.top + rect.height / 2
21 | break
22 | case 'right':
23 | x = rect.right
24 | y = rect.top + rect.height / 2
25 | break
26 | }
27 | return (
28 |
32 | )
33 | })
34 |
35 |
36 | const Tooltips = observer(() => (
37 |
38 | {state.entities.map(tooltip => )}
39 |
40 | ))
41 |
42 | export default Tooltips
43 |
--------------------------------------------------------------------------------
/app/components/Tooltip/index.js:
--------------------------------------------------------------------------------
1 | import TooltipTrigger from './TooltipTrigger'
2 | import Tooltips from './Tooltips'
3 |
4 | export default TooltipTrigger
5 | export { Tooltips }
6 |
--------------------------------------------------------------------------------
/app/components/Tooltip/state.js:
--------------------------------------------------------------------------------
1 | import { observable } from 'mobx'
2 |
3 | const state = observable({
4 | entities: []
5 | })
6 |
7 | export function addTooltip (tooltip, delay=500) {
8 | tooltip.show = false
9 | state.entities.push(tooltip)
10 | tooltip = state.entities[state.entities.length - 1]
11 | tooltip.timeoutId = window.setTimeout(() => tooltip.show = true, delay)
12 | return tooltip
13 | }
14 |
15 | export function removeTooltip (tooltip, delay=500) {
16 | window.clearTimeout(tooltip.timeoutId)
17 | tooltip.show = false
18 | window.setTimeout(() => state.entities.remove(tooltip), delay)
19 | }
20 |
21 | export default state
22 |
--------------------------------------------------------------------------------
/app/components/TopBar/TopBar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PluginArea from 'components/Plugins/component'
3 | import { TOPBAR } from 'components/Plugins/constants'
4 | import { observer } from 'mobx-react'
5 | import Breadcrumbs from './Breadcrumbs'
6 |
7 | const TopBar = observer(() => (
8 |
12 | ))
13 |
14 | export default TopBar
15 |
--------------------------------------------------------------------------------
/app/components/TopBar/index.js:
--------------------------------------------------------------------------------
1 | import TopBar from './TopBar'
2 |
3 | export default TopBar
4 |
--------------------------------------------------------------------------------
/app/components/exports.js:
--------------------------------------------------------------------------------
1 | import * as Accordion from './Accordion'
2 | import * as TopBar from './TopBar'
3 | import * as ContextMenu from './ContextMenu'
4 | import * as Editor from './Editor'
5 | import * as Menu from './Menu'
6 | import * as MenuBar from './MenuBar'
7 | import * as Modal from './Modal'
8 | import * as Tab from './Tab'
9 | import * as Notification from './Notification'
10 | import * as FileTree from './FileTree'
11 | import * as Tooltip from './Tooltip'
12 | import * as Mask from './Mask'
13 |
14 |
15 | export {
16 | Accordion,
17 | TopBar,
18 | ContextMenu,
19 | Editor,
20 | Menu,
21 | Modal,
22 | Tab,
23 | MenuBar,
24 | Notification,
25 | FileTree,
26 | Tooltip,
27 | Mask
28 | }
29 |
--------------------------------------------------------------------------------
/app/containers/GlobalPrompt.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | import Prompt from 'components/Prompt/Prompt'
4 |
5 | class GlobalPrompt extends Component {
6 | constructor (props) {
7 | super(props)
8 | this.state = {
9 | prompts: []
10 | }
11 | }
12 |
13 | componentDidMount () {
14 | const id = 'global-prompt-ide'
15 | const promptMessage = []
16 |
17 | if (!localStorage.getItem('visited')) {
18 | promptMessage.push({
19 | content: (
20 |
21 | WebIDE 现已全面升级为 Cloud Studio,点击
22 | this.handleClosePrompt(id, 'update')}
27 | >
28 | 立即体验
29 | {' '}
30 |
31 | ),
32 | id: 'global-prompt-ide',
33 | type: 'update'
34 | })
35 | this.setState({ prompts: promptMessage })
36 | }
37 | }
38 |
39 | handleClosePrompt = (id, type) => {
40 | const { prompts } = this.state
41 | this.setState({ prompts: prompts.filter(prompt => prompt.id !== id) })
42 | if (type === 'update') {
43 | localStorage.setItem('visited', true)
44 | }
45 | }
46 |
47 | render () {
48 | const { prompts } = this.state
49 | return
50 | }
51 | }
52 |
53 | export default GlobalPrompt
54 |
--------------------------------------------------------------------------------
/app/containers/IDE.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { connect } from 'react-redux'
3 | import { initializeFileTree } from '../components/FileTree/actions'
4 | import PanelsContainer from '../components/Panel'
5 | import Utilities from './Utilities'
6 | import hasVimium from 'utils/hasVimium'
7 | import { notify, NOTIFY_TYPE } from '../components/Notification/actions'
8 | import i18n from 'utils/createI18n'
9 | import GlobalPrompt from './GlobalPrompt'
10 |
11 | class IDE extends Component {
12 | constructor (props) {
13 | super(props)
14 | this.state = { isReady: false }
15 | }
16 |
17 | componentWillMount () { // initLifecycle_3: IDE specific init
18 | initializeFileTree() // @fixme: this is related to the quirk in filetree state
19 | this.setState({ isReady: true })
20 | }
21 |
22 | componentDidMount () {
23 | if (hasVimium()) {
24 | notify({
25 | notifyType: NOTIFY_TYPE.ERROR,
26 | message: i18n`global.hasVimium`,
27 | dismissAfter: 12000
28 | })
29 | }
30 | }
31 |
32 | render () {
33 | if (!this.state.isReady) return null
34 | return (
35 |
40 | )
41 | }
42 | }
43 |
44 | export default connect()(IDE)
45 |
--------------------------------------------------------------------------------
/app/containers/Initialize/state.js:
--------------------------------------------------------------------------------
1 | import { observable, computed } from 'mobx'
2 |
3 | const state = observable({
4 | errorInfo: '',
5 | errorCode: null,
6 | status: '',
7 | })
8 | export default state
9 |
--------------------------------------------------------------------------------
/app/containers/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import i18n from 'utils/createI18n'
3 |
4 | class Login extends Component {
5 | render () {
6 | return (
7 |
8 |
9 |
10 |
11 |
Coding WebIDE
12 | Coding Anytime Anywhere
13 |
14 |
15 |
16 |
17 | )
18 | }
19 | handleCodingLogin (e) {
20 | e.preventDefault()
21 | e.stopPropagation()
22 | // window.location.href = `/login?return_url=${window.location.href}`
23 | }
24 | }
25 |
26 | export default Login
27 |
--------------------------------------------------------------------------------
/app/containers/Root/actions.js:
--------------------------------------------------------------------------------
1 | // import { createAction } from 'redux-actions'
2 | import config from '../../config'
3 | import ide from '../../IDE'
4 | import { qs } from 'utils'
5 |
6 | // initAppState
7 | export const INIT_STATE = 'INIT_STATE'
8 | export const initState = () => (dispatch) => {
9 | if (config.isPlatform) {
10 | const urlPath = window.location.pathname
11 | const queryEntryPathPattern = /^\/ws\/?$/
12 | const wsPathPattern = /^\/ws\/([^\/]+)$/
13 | const isFromQueryEntryPath = queryEntryPathPattern.test(urlPath)
14 | let spaceKey = null
15 | const match = wsPathPattern.exec(urlPath)
16 | if (match) {
17 | spaceKey = match[1]
18 | config.spaceKey = spaceKey
19 | }
20 | } else {
21 | const { spaceKey } = qs.parse(window.location.hash.slice(1))
22 | if (spaceKey) config.spaceKey = spaceKey
23 | }
24 | window.ide = ide
25 | // dispatch({ type: INIT_STATE })
26 | }
27 |
--------------------------------------------------------------------------------
/app/containers/Root/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Provider, connect } from 'react-redux'
4 | import { Provider as MobxProvider } from 'mobx-react'
5 |
6 | import store from '../../store' // initLifecycle_1: gives the defaultState
7 | import mobxStore from '../../mobxStore'
8 | import IDE from '../IDE'
9 | import { initState } from './actions'
10 |
11 | class Root extends Component {
12 | static proptypes = {
13 | dispatch: PropTypes.func
14 | }
15 | componentWillMount () {
16 | // this.props.dispatch(initState()) // initLifecycle_2
17 | }
18 | render () {
19 | return
20 | }
21 | }
22 |
23 | Root = connect(null)(Root)
24 |
25 | export default () => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/app/containers/Root/login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { Provider, connect } from 'react-redux'
4 | import { Provider as MobxProvider } from 'mobx-react'
5 |
6 | import store from '../../store' // initLifecycle_1: gives the defaultState
7 | import mobxStore from '../../mobxStore'
8 | import Login from '../Login'
9 | import { initState } from './actions'
10 |
11 | class Root extends Component {
12 | static proptypes = {
13 | dispatch: PropTypes.func
14 | }
15 | componentWillMount () {
16 | // this.props.dispatch(initState()) // initLifecycle_2
17 | }
18 | render () {
19 | return
20 | }
21 | }
22 |
23 | Root = connect(null)(Root)
24 |
25 | export default () => {
26 | return (
27 |
28 |
29 |
30 |
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/app/containers/Root/reducer.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 | import { qs } from 'utils'
3 | import config from '../../config'
4 |
5 | import {
6 | INIT_STATE,
7 | } from './actions'
8 |
9 |
10 | export default handleActions({
11 | [INIT_STATE]: (state, action) => {
12 | const { spaceKey } = qs.parse(window.location.hash.slice(1))
13 | if (spaceKey) config.spaceKey = spaceKey
14 | return ({
15 | ...state,
16 | })
17 | }
18 | })
19 |
--------------------------------------------------------------------------------
/app/containers/Utilities.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ModalContainer from 'components/Modal'
3 | import Notification from 'components/Notification'
4 | import DragAndDrop from 'components/DragAndDrop'
5 | import { Tooltips } from 'components/Tooltip'
6 | import PluginArea from 'components/Plugins/component'
7 | import { CONTAINERS } from 'components/Plugins/constants'
8 | import { ContextMenuContainer } from 'components/ContextMenu'
9 | import Mask from 'components/Mask'
10 |
11 |
12 | const Utilities = () => (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 |
24 | export default Utilities
25 |
--------------------------------------------------------------------------------
/app/exports.js:
--------------------------------------------------------------------------------
1 | const app = {
2 | backendAPI: require('backendAPI'),
3 | commands: require('commands'),
4 | commons: require('commons/exports'),
5 | components: require('components/exports'),
6 | utils: require('utils/exports'),
7 | config: require('./config'),
8 | settings: require('./settings'),
9 | settingFormState: require('components/Setting/state')
10 | }
11 |
12 | const lib = {
13 | react: require('react'),
14 | mobx: require('mobx'),
15 | mobxReact: require('mobx-react'),
16 | classnames: require('classnames'),
17 | lodash: require('lodash'),
18 | eventemitter: require('eventemitter3'),
19 | sockjsClient: require('sockjs-client'),
20 | moment: require('moment'),
21 | codemirror: require('codemirror'),
22 | reactDom: require('react-dom'),
23 | axios: require('axios'),
24 | localforage: require('localforage'),
25 | styled: require('styled-components'),
26 | }
27 |
28 | export { app, lib }
29 |
--------------------------------------------------------------------------------
/app/i18n/en_US/file.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileForceMove": "Do you want to overwrite?",
3 | "newFilePath": "Enter the path for the new file.",
4 | "posInfo": "Ln:{ln} Col:{col}",
5 | "goto": "Go to Line[:Column]",
6 | "newFileName": "Enter the new name (or new path) for this file.",
7 | "folderExist": "The folder already exists",
8 | "deleteNotifySuccess": "Delete success!",
9 | "newFileFolderPath": "Enter the path for the new folder.",
10 | "carets": "{length} carets",
11 | "deleteNotifyFailed": "Delete fail: {err}",
12 | "deleteHeader": "Are you sure you want to delete this file?",
13 | "fileExist": "The path already exists",
14 | "moveFolderFailed": "Failed to move folder",
15 | "deleteMessage": "You're trying to delete {file}",
16 | "deleteButton": "Delete",
17 | "overwriteButton": "Overwrite",
18 | "totalSelected": "{length} carets ({count} characters selected)",
19 | "fileToLarge": "Filesize is limit to {filesize} MB",
20 | "uploading": "Uploading",
21 | "uploadSucceed": "Upload succeed",
22 | "uploadFailed": "Upload failed"
23 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/fileTree.json:
--------------------------------------------------------------------------------
1 | {
2 | "contextMenu": {
3 | "newFile": "New File...",
4 | "newFolder": "New Folder...",
5 | "delete": "Delete...",
6 | "rename": "Rename...",
7 | "download": "Download",
8 | "upload": "Upload",
9 | "gitBlame": "Git Blame"
10 | }
11 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "loadingWorkspaceCloning": "Now cloning code...",
3 | "loadingWorkspaceCloneFailed": "Clone failed",
4 | "offline": "Offline",
5 | "recloneWorkspace": "Try clone again",
6 | "requestingCollaboration": "You have requested collaboration",
7 | "online": "Online",
8 | "loadingWorkspaceDenied": "Loading workspace denied",
9 | "requestCollaborationReject": "Your request has been rejected.",
10 | "loadingWorkspaceFailed": "Loading workspace is failed",
11 | "onSocketError": "file socket is error",
12 | "requestCollaboration": "Request collaboration",
13 | "connecting": "Connecting",
14 | "loadingWorkspace": "Loading workspace...",
15 | "reloadWorkspace": "Retry",
16 | "requestCollaborationExpires": "The demo has already expired.",
17 | "hasVimium": "Please disable Vimium plugin."
18 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/index.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | const contents = ['menuBarItems', 'settings', 'file', 'panel', 'tab', 'git', 'fileTree', 'global', 'modal', 'login']
4 |
5 | export default contents.reduce((p, v) => {
6 | p[v] = require(`./${v}.json`)
7 | return p
8 | }, {})
9 |
--------------------------------------------------------------------------------
/app/i18n/en_US/login.json:
--------------------------------------------------------------------------------
1 | {
2 | "loginCoding": "Login with Coding"
3 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/menuBarItems.json:
--------------------------------------------------------------------------------
1 | {
2 | "meta": {
3 | "main": "Settings"
4 | },
5 | "file": {
6 | "main": "File",
7 | "newFile": "New File",
8 | "newFolder": "New Folder",
9 | "save": "Save",
10 | "commit": "commit"
11 | },
12 | "edit": {
13 | "main": "Edit",
14 | "format": "Format",
15 | "comment": "Toggle Comment"
16 | },
17 | "git": {
18 | "rebase": "Rebase",
19 | "pull": "Pull",
20 | "unstashChanges": "Unstash Changes",
21 | "continueRebase": "Continue Rebasing",
22 | "stashChanges": "Stash Changes",
23 | "resetHead": "Reset HEAD",
24 | "branches": "Branches",
25 | "abortRebase": "Abort Rebasing",
26 | "resolveConflicts": "Resolve Conflicts",
27 | "skipCommit": "Skip Commit",
28 | "commit": "Commit",
29 | "main": "Git",
30 | "push": "Push",
31 | "tag": "Tag",
32 | "mergeBranch": "Merge Branch"
33 | },
34 | "tools": {
35 | "main": "Tools",
36 | "terminal": "Terminal",
37 | "newTerminal": "New Terminal"
38 | },
39 | "switch": "Switch to v1"
40 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/modal.json:
--------------------------------------------------------------------------------
1 | {
2 | "okButton": "OK",
3 | "cancelButton": "Cancel"
4 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/panel.json:
--------------------------------------------------------------------------------
1 | {
2 | "bottom": {
3 | "terminal": "Terminal",
4 | "gitGraph": "Git Logs",
5 | "history": "History"
6 | },
7 | "left": {
8 | "project": "Project",
9 | "working": "Working"
10 | }
11 | }
--------------------------------------------------------------------------------
/app/i18n/en_US/tab.json:
--------------------------------------------------------------------------------
1 | {
2 | "makeDropdownMenuItems": {
3 | "close": "Close Pane",
4 | "untitledTab": "untitled",
5 | "noTabs": ""
6 | },
7 | "contextMenu": {
8 | "close": "Close",
9 | "closeOthers": "Close Others",
10 | "closeAll": "Close All",
11 | "verticalSplit": "Vertical Split",
12 | "horizontalSplit": "Horizontal Split"
13 | }
14 | }
--------------------------------------------------------------------------------
/app/i18n/index.json:
--------------------------------------------------------------------------------
1 | ["en_US", "zh_CN"]
--------------------------------------------------------------------------------
/app/i18n/zh_CN/file.json:
--------------------------------------------------------------------------------
1 | {
2 | "fileForceMove": "是否覆盖现有文件",
3 | "newFilePath": "请输入新文件的路径",
4 | "posInfo": "行:{ln} 列:{col}",
5 | "goto": "跳转到 行[:列]",
6 | "newFileName": "请输入新文件路径/文件名",
7 | "folderExist": "目录已经存在",
8 | "deleteNotifySuccess": "成功删除",
9 | "newFileFolderPath": "请输入新文件夹的路径",
10 | "carets": "{length} 光标",
11 | "deleteNotifyFailed": "删除失败:{err}",
12 | "deleteHeader": "你是否确认删除此文件",
13 | "fileExist": "文件已经存在",
14 | "moveFolderFailed": "目录移动失败",
15 | "deleteMessage": "你正在删除 {file}",
16 | "deleteButton": "删除",
17 | "overwriteButton": "覆盖",
18 | "totalSelected": "{length} 光标 (已选中 {count} 个字符)",
19 | "fileToLarge": "文件不能超过 {filesize} MB",
20 | "uploading": "正在上传",
21 | "uploadSucceed": "上传完成",
22 | "uploadFailed": "上传失败"
23 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/fileTree.json:
--------------------------------------------------------------------------------
1 | {
2 | "contextMenu": {
3 | "newFile": "新建文件...",
4 | "newFolder": "新建文件夹...",
5 | "delete": "删除...",
6 | "rename": "重命名...",
7 | "download": "下载",
8 | "upload": "上传",
9 | "gitBlame": "查看历史"
10 | }
11 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "loadingWorkspaceCloning": "正在 Clone 项目代码...",
3 | "loadingWorkspaceCloneFailed": "Clone 代码失败",
4 | "offline": "离线",
5 | "recloneWorkspace": "重新 Clone 代码",
6 | "requestingCollaboration": "协作申请已经提交",
7 | "online": "在线",
8 | "loadingWorkspaceDenied": "访问工作区未授权",
9 | "requestCollaborationReject": "您的申请被拒绝了",
10 | "loadingWorkspaceFailed": "加载工作区失败",
11 | "onSocketError": "已断线",
12 | "requestCollaboration": "申请协作",
13 | "connecting": "连接",
14 | "loadingWorkspace": "正在加载工作区",
15 | "reloadWorkspace": "重新加载",
16 | "requestCollaborationExpires": "试用已经结束",
17 | "hasVimium": "请禁用 Vimium 插件以避免按键冲突"
18 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/index.js:
--------------------------------------------------------------------------------
1 | const contents = ['menuBarItems', 'settings', 'file', 'panel', 'tab', 'git', 'fileTree', 'global', 'modal', 'login']
2 |
3 | export default contents.reduce((p, v) => {
4 | p[v] = require(`./${v}.json`)
5 | return p
6 | }, {})
7 |
--------------------------------------------------------------------------------
/app/i18n/zh_CN/login.json:
--------------------------------------------------------------------------------
1 | {
2 | "loginCoding": "使用 Coding 账号登录"
3 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/menuBarItems.json:
--------------------------------------------------------------------------------
1 | {
2 | "meta": {
3 | "main": "设置"
4 | },
5 | "file": {
6 | "main": "文件",
7 | "newFile": "新建文件",
8 | "newFolder": "新建文件夹",
9 | "save": "保存",
10 | "commit": "commit"
11 | },
12 | "edit": {
13 | "main": "编辑",
14 | "format": "格式化代码",
15 | "comment": "切换注释"
16 | },
17 | "git": {
18 | "rebase": "变基",
19 | "pull": "拉取",
20 | "unstashChanges": "恢复代码",
21 | "continueRebase": "继续变基",
22 | "stashChanges": "储藏代码",
23 | "resetHead": "重置代码",
24 | "branches": "分支",
25 | "abortRebase": "终止变基",
26 | "resolveConflicts": "解决冲突",
27 | "skipCommit": "跳过提交",
28 | "commit": "提交",
29 | "main": "版本",
30 | "push": "推送",
31 | "tag": "标签",
32 | "mergeBranch": "合并分支"
33 | },
34 | "tools": {
35 | "main": "工具",
36 | "terminal": "终端",
37 | "newTerminal": "新建"
38 | },
39 | "switch": "切换到 v1 版本"
40 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/modal.json:
--------------------------------------------------------------------------------
1 | {
2 | "okButton": "确定",
3 | "cancelButton": "取消"
4 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/panel.json:
--------------------------------------------------------------------------------
1 | {
2 | "bottom": {
3 | "terminal": "终端",
4 | "gitGraph": "项目网络",
5 | "history": "版本历史"
6 | },
7 | "left": {
8 | "project": "文件树",
9 | "working": "工作文件"
10 | }
11 | }
--------------------------------------------------------------------------------
/app/i18n/zh_CN/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": "默认",
3 | "reset": "重置",
4 | "commit": "确认",
5 | "general": {
6 | "main": "通用",
7 | "language": "语言",
8 | "languageOption": {
9 | "english": "英语",
10 | "chinese": "中文"
11 | },
12 | "hideFiles": "隐藏文件"
13 | },
14 | "editor": {
15 | "main": "编辑器",
16 | "editorconfigNote": "如果你的项目根目录下有 .editorconfig 文件,那么本页面下的一些选项的控制会被它接管。",
17 | "editorconfigNoteLink": "打开 .editorconfig",
18 | "fontFamily": "字体",
19 | "charset": "编码",
20 | "indentStyle": "缩进风格",
21 | "indentSize": "缩进空格数",
22 | "tabWidth": "Tab 宽度",
23 | "trimTrailingWhitespace": "删除行尾空格",
24 | "insertFinalNewline": "文件结尾插入空白行",
25 | "autoSave": "自动保存",
26 | "autoWrap": "自动换行",
27 | "autoCompletion": "自动补全",
28 | "snippets": "代码片段"
29 | },
30 | "keymap": {
31 | "main": "快捷键",
32 | "keyboardMode": "编辑器键盘模式"
33 | },
34 | "appearance": {
35 | "main": "样式",
36 | "uiTheme": "UI 主题",
37 | "uiThemeOption": {
38 | "baseTheme": "默认",
39 | "dark": "黑色"
40 | },
41 | "syntaxTheme": "代码着色",
42 | "fontSize": "编辑器字号"
43 | },
44 | "extension": {
45 | "main": "插件中心",
46 | "searchPlaceholder": "根据插件名字搜索"
47 | },
48 | "tabs": {
49 | "general": "通用",
50 | "appearance": "样式",
51 | "editor": "编辑器",
52 | "keymap": "快捷键",
53 | "extensions": "插件中心"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/i18n/zh_CN/tab.json:
--------------------------------------------------------------------------------
1 | {
2 | "makeDropdownMenuItems": {
3 | "close": "关闭面板",
4 | "untitledTab": "未命名",
5 | "noTabs": "<没有标签>"
6 | },
7 | "contextMenu": {
8 | "close": "关闭标签",
9 | "closeOthers": "关闭其他标签",
10 | "closeAll": "关闭全部标签",
11 | "verticalSplit": "垂直切分",
12 | "horizontalSplit": "水平切分"
13 | }
14 | }
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Coding WebIDE 开启云端开发模式! - Coding.net
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/index.jsx:
--------------------------------------------------------------------------------
1 | import { AppContainer } from 'react-hot-loader'
2 | import React from 'react'
3 | import { render } from 'react-dom'
4 | import Root from './containers/Root'
5 | import './styles/main.styl'
6 | import initialize from './initialize'
7 | import InitializeContainer from './containers/Initialize'
8 | import SettingState from 'components/Setting/state'
9 | const uiTheme = SettingState.settings.appearance.ui_theme.value
10 | if (uiTheme === 'base-theme') {
11 | const baseTheme = require('!!style-loader/useable!css-loader!stylus-loader!./styles/base-theme/index.styl')
12 | baseTheme.use()
13 | window.themes = { '@current': baseTheme }
14 | } else {
15 | const darkTheme = require('!!style-loader/useable!css-loader!stylus-loader!./styles/dark/index.styl')
16 | darkTheme.use()
17 | window.themes = { '@current': darkTheme }
18 | }
19 |
20 | const rootElement = document.getElementById('root')
21 | render(, rootElement)
22 |
23 | async function startApp (module) {
24 | const step = await initialize()
25 | if (!step.allSuccess) {
26 | return
27 | }
28 | if (__DEV__) {
29 | const hotLoaderRender = () =>
30 | render(, rootElement)
31 |
32 | hotLoaderRender()
33 | if (module.hot) module.hot.accept('./containers/Root', hotLoaderRender)
34 | } else {
35 | render(, rootElement)
36 | }
37 | }
38 |
39 | startApp(module)
40 |
41 | const log = (...args) => console.log(...args) || (x => x)
42 | if (__VERSION__) log(`[VERSION] ${__VERSION__}`)
43 |
--------------------------------------------------------------------------------
/app/js.config.json:
--------------------------------------------------------------------------------
1 | { "moduleNameMapper": {
2 | "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/.jestfilemock.js",
3 | "^.+\\.(css|less|scss)$": "identity-obj-proxy",
4 | "utils": "/app/utils/index.js",
5 | "config": "/app/config.js",
6 | "commons": "/app/commons",
7 | "components": "/app/components"
8 | },
9 | "roots": [
10 | "/app"
11 | ],
12 | "snapshotSerializers": ["/node_modules/enzyme-to-json/serializer"],
13 | "collectCoverageFrom": ["app/*.js"],
14 | "coveragePathIgnorePatterns": [],
15 | "coverageThreshold": {
16 | "global": {
17 | "branches": 46.17,
18 | "functions": 62.18,
19 | "lines": 58.18,
20 | "statements": 57.46
21 | }
22 | },
23 | "globals": {
24 | "__DEV__": true,
25 | "__BACKEND_URL__": "",
26 | "__PACKAGE_SERVER__": "",
27 | "__WS_URL__": "",
28 | "__RUN_MODE__": "mode"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES6",
4 | "allowSyntheticDefaultImports": true,
5 | "experimentalDecorators": true,
6 | "jsx": "react",
7 | "baseUrl": "./",
8 | "paths": {
9 | "*": ["*", "*/index", "*/index.js", "*/index.jsx"]
10 | }
11 | },
12 | "exclude": [
13 | "node_modules"
14 | ]
15 | }
--------------------------------------------------------------------------------
/app/localStoreCache.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | const localStoreCache = {}
3 |
4 | const stateDomainsToCache = [
5 | // 'MarkdownEditorState',
6 | // 'FileTreeState',
7 | // 'PanelState',
8 | // 'PaneState',
9 | // 'TabState',
10 | // 'EditorState',
11 | // 'ModalState',
12 | // 'TerminalState',
13 | // 'GitState',
14 | // 'NotificationState',
15 | // 'WorkspaceState',
16 | // 'DragAndDrop',
17 | // 'SettingState',
18 | // 'PackageState',
19 | ]
20 |
21 | const stateFilter = state => stateDomainsToCache.reduce((stateToCache, domain) => {
22 | if (!state) return ''
23 | stateToCache[domain] = state[domain]
24 | return stateToCache
25 | }, {})
26 |
27 |
28 | let cachedState
29 | localStoreCache.beforeReducer = (state, action) => {
30 | // if (!state) state = JSON.parse(window.localStorage.getItem('snapshot'))
31 | if (!state) return
32 | cachedState = JSON.stringify(stateFilter(state))
33 | return state
34 | }
35 |
36 | localStoreCache.afterReducer = (state, action) => {
37 | const nextCachedState = JSON.stringify(stateFilter(state))
38 | if (nextCachedState !== cachedState) {
39 | localStorage.setItem('snapshot', nextCachedState)
40 | cachedState = nextCachedState
41 | }
42 | return state
43 | }
44 |
45 | export default localStoreCache
46 |
--------------------------------------------------------------------------------
/app/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Coding WebIDE 开启云端开发模式! - Coding.net
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/login.jsx:
--------------------------------------------------------------------------------
1 | import { AppContainer } from 'react-hot-loader'
2 | import React from 'react'
3 | import { render } from 'react-dom'
4 | import Login from './containers/Root/login'
5 | import './styles/main.styl'
6 |
7 | const rootElement = document.getElementById('root')
8 | async function startApp (module) {
9 | if (__DEV__) {
10 | const hotLoaderRender = () =>
11 | render(, rootElement)
12 |
13 | hotLoaderRender()
14 | if (module.hot) module.hot.accept('./containers/Login', hotLoaderRender)
15 | } else {
16 | render(, rootElement)
17 | }
18 | }
19 |
20 | startApp(module)
21 |
22 | const log = (...args) => console.log(...args) || (x => x)
23 | if (__VERSION__) log(`[VERSION] ${__VERSION__}`)
24 |
--------------------------------------------------------------------------------
/app/plugin/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | key: 'preload',
3 | Manager: require('./placeholder').default,
4 | }
5 |
--------------------------------------------------------------------------------
/app/plugin/placeholder.js:
--------------------------------------------------------------------------------
1 | export default class {
2 | pluginWillMount () {
3 | console.log('inner plugin')
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/app/states/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux'
2 | import reducers from '../reducers'
3 |
4 | const store = createStore(reducers)
5 |
6 | export default store
7 |
--------------------------------------------------------------------------------
/app/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
2 | import { composeReducers } from './utils'
3 | import { dispatch as emitterDispatch, emitterMiddleware } from 'utils/actions'
4 | import thunkMiddleware from 'redux-thunk'
5 |
6 | import GitReducer from './components/Git/reducer'
7 | import RootReducer from './containers/Root/reducer'
8 |
9 | import localStoreCache from './localStoreCache'
10 |
11 | const combinedReducers = combineReducers({
12 | GitState: GitReducer,
13 | })
14 |
15 | const crossReducers = composeReducers(RootReducer)
16 | const finalReducer = composeReducers(
17 | localStoreCache.afterReducer,
18 | crossReducers,
19 | combinedReducers,
20 | localStoreCache.beforeReducer
21 | )
22 |
23 | const enhancer = compose(
24 | applyMiddleware(thunkMiddleware, emitterMiddleware),
25 | window.devToolsExtension ? window.devToolsExtension({
26 | serialize: {
27 | replacer: (key, value) => {
28 | if (key === 'editor') return {}
29 | if (key === 'DOMNode') return {}
30 | return value
31 | }
32 | }
33 | }) : f => f
34 | )
35 | // enhancer = applyMiddleware(thunkMiddleware)
36 | const store = createStore(finalReducer, enhancer)
37 | window.getState = store.getState
38 | window.dispatch = store.dispatch
39 |
40 |
41 | window.addEventListener('storage', (e) => {
42 | if (e.key && e.key.includes('CodingPackage')) {
43 | store.dispatch({ type: 'UPDATE_EXTENSION_CACHE' })
44 | }
45 | })
46 |
47 | export default store
48 | export const getState = store.getState
49 | export const dispatch = store.dispatch
50 |
--------------------------------------------------------------------------------
/app/store.spec.js:
--------------------------------------------------------------------------------
1 | import { getState } from './store'
2 |
3 | describe('getState', () => {
4 | it('should getState', () => expect(getState()).toBe(false))
5 | })
6 |
--------------------------------------------------------------------------------
/app/styles/base-theme/index.styl:
--------------------------------------------------------------------------------
1 | @import './styles/ui-variables';
2 | @import './styles/tabs';
3 | @import './styles/panes';
4 | @import './styles/filetree';
5 | @import './styles/menu';
6 | @import './styles/bars';
7 | @import './styles/git';
8 | @import './styles/accordion';
9 | @import './styles/filelist';
10 | @import './styles/editor';
11 | @import './styles/term';
12 | @import './styles/mask';
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/accordion.styl:
--------------------------------------------------------------------------------
1 | .accordion {
2 | .accordion-topbar {
3 | background-color: $tab-bar-background-color;
4 | border-color: $base-border-color;
5 | color: $text-color;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/bars.styl:
--------------------------------------------------------------------------------
1 | .menu-bar {
2 | background-color: $base-top-menu-bar-background;
3 | border-color: gray(80%);
4 | color: gray(15%);
5 | }
6 |
7 | .status-bar {
8 | border-color: $base-border-color;
9 | background-color: $base-top-menu-bar-background;
10 | }
11 |
12 | .breadcrumbs {
13 | background-color: $menu-background-color;
14 | border-color: $base-border-color;
15 | .crumb {
16 | background-color: $menu-background-color;
17 | &:before {
18 | border-left-color: $base-border-color;
19 | }
20 | &:after {
21 | border-left-color: $menu-background-color;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/editor.styl:
--------------------------------------------------------------------------------
1 | .status-bar-editor-widgets {
2 | display: flex;
3 | align-items: stretch;
4 | .editor-widget {
5 | display: flex;
6 | align-items: center;
7 | padding: 0 8px;
8 | &.enabled {
9 | background-color: #ccc;
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/filelist.styl:
--------------------------------------------------------------------------------
1 | .file-list-container {
2 | .file-list-item {
3 | color: $text-color;
4 | &.focus {
5 | color: $text-color-highlight;
6 | background-color: $tree-view-background-color-highlight;
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/filetree.styl:
--------------------------------------------------------------------------------
1 | .filetree-node.focus {
2 | color: $text-color-highlight;
3 | > .filetree-node-bg {
4 | background-color: $tree-view-background-color-highlight;
5 | }
6 | }
7 |
8 | .filetree-container {
9 | .filetree-node-container.highlight,
10 | .filetree-node.focus {
11 | background-color: $tree-view-background-color-highlight;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/git.styl:
--------------------------------------------------------------------------------
1 | // .status { color: @text-color; }
2 | // .status-added { color: @text-color-success; } // green
3 | // .status-ignored { color: @text-color-subtle; } // faded
4 | // .status-modified { color: @text-color-warning; } // orange
5 | // .status-removed { color: @text-color-error; } // red
6 | // .status-renamed { color: @text-color-info; } // blue
7 |
8 | .git-untracked {
9 | color: $untracked-color;
10 | }
11 | .git-confliction {
12 | color: $confliction-color;
13 | }
14 | .git-modified {
15 | color: $modified-color;
16 | }
17 | .git-modify {
18 | color: $modified-color;
19 | }
20 | .git-changed {
21 | color: $modified-color;
22 | }
23 | .git-rename {
24 | color: $modified-color;
25 | }
26 | .git-copy {
27 | color: $modified-color;
28 | }
29 |
30 | .git-added {
31 | color: $added-color;
32 | }
33 | .git-add {
34 | color: $added-color;
35 | }
36 | .git-missing {
37 | text-decoration: line-through;
38 | }
39 |
40 | .git-removed {
41 | text-decoration: line-through;
42 | }
43 | .git-delete {
44 | text-decoration: line-through;
45 | }
46 |
47 | .file-status-indicator {
48 | &.modified, &.changed, &.modify {color: hsl(47,100%,50%)}
49 | &.untracked, &.add {color: hsl(80,60%,40%)}
50 | &.missing {color: hsl(10,80%,45%)}
51 | &.confliction {color: hsl(10,80%,45%)}
52 | }
53 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/mask.styl:
--------------------------------------------------------------------------------
1 | .mask-container {
2 | background: rgba(0, 0, 0, 0.4);
3 | .progress {
4 | background-color: #fff;
5 | }
6 | }
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/menu.styl:
--------------------------------------------------------------------------------
1 | .menu {
2 | color: $text-color;
3 | background-color: $menu-background-color;
4 | border-color: $menu-border-color;
5 | opacity: 0.95;
6 | > li > hr {
7 | border-color: gray(80%);
8 | }
9 | }
10 |
11 | .menu-bar-container {
12 | background-color: $base-top-menu-bar-background;
13 | }
14 |
15 | .menu-bar-item-container, .menu-item-container {
16 | &.active {
17 | background-color: $menu-background-color-highlight;
18 | color: #fff;
19 | }
20 | &.disabled, &.active.disabled {
21 | background-color: transparent;
22 | color: $text-color-subtle;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/panes.styl:
--------------------------------------------------------------------------------
1 | .panel-container, .pane-container {
2 | background-color: $base-background-color;
3 | border: 1px solid $base-border-color;
4 | }
5 |
--------------------------------------------------------------------------------
/app/styles/base-theme/styles/term.styl:
--------------------------------------------------------------------------------
1 | .ide-terminal {
2 | background: #FFF;
3 | }
4 |
5 | .terminal-panel {
6 | .terminal-toolbar {
7 | color: $text-color;
8 | }
9 | }
--------------------------------------------------------------------------------
/app/styles/core-ui/Accordion.styl:
--------------------------------------------------------------------------------
1 | .resizeBar {
2 | $size = 6px
3 | z-index: z(pane-resize-bar);
4 | &.col-resize {
5 | cursor: col-resize;
6 | height: 100%;
7 | width: $size;
8 | margin-lr: $size * -0.5;
9 | }
10 | &.row-resize {
11 | cursor: row-resize;
12 | width: 100%;
13 | height: $size;
14 | margin-tb: $size * -0.5;
15 | }
16 | }
17 |
18 | .accordion-group {
19 | display: flex;
20 | height: 100%;
21 | width: 100%;
22 | }
23 |
24 | .accordion {
25 | flex-basis: 0;
26 | min-height: $tab-height;
27 | position: relative;
28 | // transition: flex-grow 1s ease;
29 | .accordion-topbar {
30 | display: flex;
31 | align-items: center;
32 | background-color: #f4f4f4;
33 | border-bottom: 1px solid $base-border-color;
34 | height: $tab-height;
35 | noSelect();
36 |
37 | .indicator {
38 | margin-lr: 5px;
39 | width: 9px;
40 | text-align: center;
41 | }
42 | .accordion-header {
43 | cursor: pointer;
44 | flex: 1;
45 | white-space: nowrap;
46 |
47 | .icon {
48 | margin-right: 4px;
49 | }
50 | }
51 | .accordion-actions {
52 | cursor: pointer;
53 | margin-right: 6px;
54 | }
55 | }
56 | .accordion-body {
57 | position: absolute;
58 | top: 30px;
59 | bottom: 0;
60 | left: 0;
61 | right: 0;
62 | // overflow: auto;
63 | scrollBar();
64 | &.hidden {
65 | display: none;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/styles/core-ui/Bar.styl:
--------------------------------------------------------------------------------
1 | $bar-size= 25px
2 |
3 | // the rotated label trick, see: https://gist.github.com/aiboy/7406727
4 | .side-bar {
5 | display: flex;
6 | noSelect();
7 |
8 | &.left, &.right {
9 | width: $bar-size;
10 | flex-direction: column;
11 | .side-bar-label {
12 | padding: 5px 4px;
13 | text-align right;
14 | }
15 | }
16 | &.right {
17 | .side-bar-label {
18 | padding: 5px 2px;
19 | }
20 | }
21 | &.top, &.bottom {
22 | height: $bar-size;
23 | flex-direction: row;
24 | .side-bar-label {
25 | padding-lr: 5px;
26 | text-align center;
27 | }
28 | }
29 |
30 | .side-bar-label {
31 | cursor: pointer;
32 | overflow: hidden;
33 | &:hover {
34 | background-color: gray(95%);
35 | }
36 | &.active {
37 | background-color: gray(90%);
38 | }
39 | }
40 |
41 | &.top, &.bottom {
42 | .side-bar-label-container {
43 | display: flex;
44 | align-items: center;
45 | height: 100%;
46 | }
47 | }
48 |
49 | &.left, &.right {
50 | .side-bar-label-container {
51 | display: inline-block;
52 | white-space: nowrap;
53 | transform: translate(0.8em, -0.5em) rotate(90deg);
54 | transform-origin: 0 0.5em;
55 | &:before {
56 | // this is the key, it keeps the box from collapse
57 | content: "";
58 | float: right;
59 | margin-bottom: 100%;
60 | }
61 | }
62 | }
63 |
64 | .side-bar-label-content {
65 | display: flex;
66 | line-height: 1;
67 | align-items: center;
68 | .icon {margin-right: 4px}
69 | }
70 |
71 | &.left .side-bar-label-content {
72 | transform: rotate(180deg);
73 | }
74 |
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/app/styles/core-ui/Breadcrumbs.styl:
--------------------------------------------------------------------------------
1 | .top-bar {
2 | display: flex;
3 | noSelect();
4 |
5 | .widget {
6 | display: flex;
7 | margin-left: auto;
8 | white-space: nowrap;
9 | line-height: 12px;
10 | padding-right: 4px;
11 | align-items: center;
12 | }
13 | }
14 |
15 | .breadcrumbs {
16 | noSelect();
17 | $breadcrumbs-height = 24px;
18 |
19 | display: flex;
20 | height: $breadcrumbs-height + 1px;
21 |
22 | .crumb-node-name {
23 | line-height: $breadcrumbs-height;
24 | }
25 |
26 | .crumb {
27 | display: flex;
28 | align-items: center;
29 | padding-left: 10px;
30 | padding-right: $breadcrumbs-height*0.25 + 10px;
31 | position: relative;
32 |
33 | i {
34 | font-style: normal;
35 | }
36 |
37 | &:before, &:after {
38 | display: block;
39 | content: '';
40 | border-style: solid;
41 | border-color: transparent;
42 | border-right: none;
43 | }
44 | &:before {
45 | absolute(right 0 top -1px)
46 | border-top-width: $breadcrumbs-height*0.5 + 1px;
47 | border-bottom-width: $breadcrumbs-height*0.5 + 1px;
48 | border-left-width: $breadcrumbs-height*0.25 + 1px;
49 | }
50 | &:after {
51 | absolute(right 1px top 0px)
52 | border-top-width: $breadcrumbs-height*0.5
53 | border-bottom-width: $breadcrumbs-height*0.5
54 | border-left-width: $breadcrumbs-height*0.25;
55 | }
56 | &:last-child {
57 | &:before, &:after {display: none}
58 | }
59 | .fa-folder-o {
60 | margin-top: 2px;
61 | }
62 | .fa-folder {
63 | margin-top: 2px;
64 | color: $tree-view-folder-color;
65 | }
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/styles/core-ui/CommandPalette.styl:
--------------------------------------------------------------------------------
1 | .command-palette-input {
2 | margin-tb: 10px;
3 | }
4 |
5 | .command-palette-items {
6 | i, em {font-style: normal;}
7 | em {
8 | font-weight: bold;
9 | color: $text-color-highlight;
10 | }
11 |
12 | li {
13 | padding: 10px 10px;
14 | margin-lr: -10px;
15 | &.selected {
16 | background-color: $background-color-highlight;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/styles/core-ui/DragAndDrop.styl:
--------------------------------------------------------------------------------
1 | .pane-layout-overlay {
2 | z-index: z(pane-layout-overlay);
3 | transition: all ease 0.2s;
4 | opacity: 0;
5 | background-color: hsl(210, 81%, 75%);
6 | }
7 |
--------------------------------------------------------------------------------
/app/styles/core-ui/Editor.styl:
--------------------------------------------------------------------------------
1 | .CodeMirror-scroll {
2 | padding-bottom: 18px;
3 | }
4 |
5 | .CodeMirror pre {
6 | font-family: Consolas, "Source Code Pro", Consolas, "Courier New", Menlo, Monaco, "DejaVu Sans Mono", monospace;
7 | line-height: 1.3;
8 | }
9 |
10 | .CodeMirror .CodeMirror-linenumber {
11 | line-height: 1.3;
12 | }
13 |
14 | .CodeMirror .breakpoint-highlight {
15 | background: red;
16 | }
17 |
18 | .status-bar-editor-widgets {
19 | display: flex;
20 | align-items: stretch;
21 | .editor-widget {
22 | display: flex;
23 | align-items: center;
24 | padding: 0 8px;
25 | }
26 | }
27 |
28 | .mode-widget {
29 | display: flex;
30 | position: absolute;
31 | bottom: 24px;
32 | min-width: 200px;
33 | right: 1px;
34 | flex-direction: column;
35 | z-index: 201;
36 |
37 | ul {
38 | max-height: 200px;
39 | overflow: auto;
40 | }
41 | }
42 |
43 | .image-editor-loading {
44 | .fa-spinner {
45 | position: absolute;
46 | top: 50%;
47 | left: 50%;
48 | transform: translate(-50%,-50%);
49 | color: $text-color;
50 | }
51 | }
52 |
53 | .htmlPreview {
54 | overflow: hidden;
55 | position: relative;
56 | .preview-iframe-msk {
57 | position: absolute;
58 | overflow: auto;
59 | width: 100%;
60 | height: 100%;
61 | }
62 | iframe {
63 | position: absolute;
64 | overflow: auto;
65 | width: 100%;
66 | height: 100%;
67 | border: 0;
68 | background-color: white;
69 | }
70 | }
--------------------------------------------------------------------------------
/app/styles/core-ui/FileList.styl:
--------------------------------------------------------------------------------
1 | .file-list-container {
2 | height: 100%;
3 | overflow: auto;
4 | scrollBar();
5 | .file-list-item {
6 | padding: 4px 4px;
7 | cursor: pointer;
8 | .icon {
9 | width: 16px;
10 |
11 | }
12 | i {
13 | font-style: normal;
14 | width: 1em;
15 | text-align: center;
16 | margin-right: 5px;
17 | &:before {
18 | font-size: 14px;
19 | }
20 | }
21 | .fa-times {
22 | color: red;
23 | visibility: hidden;
24 | }
25 | &.focus {
26 | background-color: #e8e8e8;
27 | }
28 | &:hover .fa-times {
29 | visibility: visible;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/styles/core-ui/Mask.styl:
--------------------------------------------------------------------------------
1 | .mask-container {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | right: 0;
6 | bottom: 0;
7 | background: rgba(0, 0, 0, 0.4);
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | .progress {
12 | // position: absolute;
13 | // left: 25%;
14 | // top: 50%;
15 | // width: 50%;
16 | // margin-top: -15px;
17 | noSelect()
18 | padding: 10px 10px;
19 | border-radius: 5px;
20 | background-color: #fff;
21 | z-index: 10;
22 | > i {
23 | margin-right: 5px;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/styles/core-ui/Offline.styl:
--------------------------------------------------------------------------------
1 | .offline-container {
2 | $container-width = 60px;
3 | $toggle-handle-width = 10px;
4 | $button-text-width = $container-width - $toggle-handle-width - 2px;
5 |
6 | padding: 0;
7 | width: $container-width;
8 | height: 16px;
9 | line-height: 1.2;
10 | position: relative;
11 | overflow: hidden;
12 | noSelect()
13 |
14 | .toggle-group {
15 | position: absolute;
16 | display: flex;
17 | align-content: center;
18 | transition: all 0.35s ease;
19 | top: 0;
20 | bottom: 0;
21 |
22 | & > span {
23 | width: $button-text-width;
24 | display: inline-block;
25 | text-align: center;
26 | height: 100%
27 | }
28 | }
29 |
30 | &.on .toggle-group {
31 | left: 0px;
32 | }
33 |
34 | &.off .toggle-group {
35 | left: -1 * $button-text-width;
36 | }
37 |
38 | .toggle-handle {
39 | display: inline-block;
40 | width: $toggle-handle-width;
41 | background-color: #ffffff;
42 | height: 100%;
43 | border-radius: 2px;
44 | }
45 | }
46 |
47 | .blink {
48 | animation: blink 800ms infinite;
49 | }
50 |
51 | @keyframes blink {
52 | 0% {
53 | opacity: 1;
54 | }
55 | 50% {
56 | opacity: 0.2;
57 | }
58 | 100% {
59 | opacity: 1;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/styles/core-ui/StatusBar.styl:
--------------------------------------------------------------------------------
1 | $bar-height = 30px;
2 |
3 | .status-bar {
4 | // border-top: 1px solid transparent;
5 | display: flex;
6 | align-items: stretch;
7 | min-height: $bar-height;
8 | noSelect();
9 |
10 | .status-widget-container {
11 | display: flex;
12 | &.right {
13 | margin-left: auto;
14 | }
15 | }
16 |
17 | .status-bar-menu-item {
18 | display: flex;
19 | align-items: center;
20 | padding: 0 8px;
21 | cursor: pointer;
22 | position: relative;
23 | > .menu {
24 | right: 6px;
25 | bottom: $bar-height;
26 | min-width: 200px;
27 | }
28 | }
29 |
30 | .toggle-layout {
31 | padding: 0 8px;
32 | }
33 |
34 | .status-messages {
35 | display: flex;
36 | align-items: center;
37 | .status-message {
38 | margin-right: 0.5em;
39 | }
40 | .status-message + .status-message {
41 | &:before {
42 | content: '|';
43 | margin-right: 0.5em;
44 | }
45 | }
46 | }
47 | .status-bar-upload {
48 | padding: 0 8px;
49 | // line-height: $bar-height;
50 | display: flex;
51 | position: relative;
52 | .fa {
53 | line-height: $bar-height;
54 | margin-right: 4px;
55 | }
56 | .upload-messages {
57 | display: flex;
58 | align-items: center;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/styles/core-ui/Welcome.styl:
--------------------------------------------------------------------------------
1 | .welcome-page {
2 | color: $text-color;
3 | padding: 10px 40px;
4 | height: 100%;
5 | width: 100%;
6 | overflow: scroll;
7 | h1 {
8 | font-weight: normal;
9 | padding: 0;
10 | margin: 0;
11 | }
12 | .subtitle {
13 | font-size: 1.4em;
14 | padding-top: 10px;
15 | color: $text-color-subtle;
16 | padding-bottom: 40px;
17 | }
18 | h2 {
19 | font-weight: normal;
20 | padding: 0;
21 | margin: 0;
22 | font-size: 1.2em;
23 | padding-bottom: 6px;
24 | }
25 | .block {
26 | padding-bottom: 40px;
27 | }
28 | .link-item {
29 |
30 | }
31 | }
--------------------------------------------------------------------------------
/app/styles/core-ui/Workspace.styl:
--------------------------------------------------------------------------------
1 | .workspace-list {
2 | fixed(0);
3 | z-index: z(workspace-list);
4 |
5 | &:after {
6 | absolute(0);
7 | z-index: -1;
8 | content: "";
9 | background-color: white;
10 | }
11 |
12 | overflow: auto;
13 | }
14 |
15 | .workspace {
16 | display: flex;
17 | align-items: center;
18 | height: 80px;
19 | border-bottom: 1px solid gray(85%);
20 |
21 | .workspace-name {
22 | font-size: 20px;
23 | }
24 |
25 | .workspace-action {
26 | margin-left: auto;
27 | }
28 | }
29 |
30 |
31 | .create-workspace-container,
32 | .workspace-list-container {
33 | width: 40vw;
34 | min-width: 600px;
35 | margin: auto;
36 | }
37 |
38 | .create-workspace-container {
39 | margin-bottom: 30px;
40 | .pre {
41 | font-family: monospace;
42 | font-size: 1em;
43 | overflow-wrap: break-word;
44 | word-wrap: break-word;
45 | }
46 | }
47 |
48 | .create-workspace-controls {
49 | display: flex;
50 | align-items: center;
51 | input {flex-grow: 1;}
52 | button {margin-left: 4px;}
53 | }
54 |
55 | @keyframes CREATING-WORKSPACE {
56 | 0% { content:'.'; }
57 | 33% { content:'..'; }
58 | 66% { content:'...'; }
59 | }
60 |
61 | .creating-workspace-indicator {
62 | opacity: 0.7;
63 | &:after {
64 | content: '.';
65 | animation: CREATING-WORKSPACE 3s infinite;
66 | }
67 | }
68 | .creating-workspace-indicator-error {
69 | opacity: 0.7;
70 | color: red;
71 | }
72 | .creating-workspace-process {
73 | opacity: 0.7;
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/app/styles/core-ui/index.styl:
--------------------------------------------------------------------------------
1 | @import "../ui-variables";
2 | @import "./Menu";
3 | @import "./MenuBar";
4 | @import "./Breadcrumbs";
5 | @import "./PanelAndPane";
6 | @import "./Tab";
7 | @import "./FileTree";
8 | @import "./Modal";
9 | @import "./Term";
10 | @import "./Git";
11 | @import "./GitMerge";
12 | @import "./StatusBar";
13 | @import "./Workspace";
14 | @import "./CommandPalette";
15 | @import "./DragAndDrop";
16 | @import "./Settings";
17 | @import "./Bar";
18 | @import "./Markdown";
19 | @import "./History";
20 | @import "./Accordion";
21 | @import "./Initialize";
22 | @import "./Editor";
23 | @import "./Offline";
24 | @import "./FileList";
25 | @import "./Tooltip";
26 | @import "./Welcome";
27 | @import "./Mask";
--------------------------------------------------------------------------------
/app/styles/dark/index.styl:
--------------------------------------------------------------------------------
1 | @import './styles/ui-variables';
2 | @import './styles/tabs';
3 | @import './styles/panes';
4 | @import './styles/filetree';
5 | @import './styles/menu';
6 | @import './styles/bars';
7 | @import './styles/git';
8 | @import './styles/editor';
9 | @import './styles/modal';
10 | @import './styles/form';
11 | @import './styles/history';
12 | @import './styles/accordion';
13 | @import './styles/markdown';
14 | @import './styles/filelist';
15 | @import './styles/base';
16 | @import './styles/term';
17 | @import './styles/mask';
18 | @import './styles/git-merge';
19 | @import './styles/diff';
20 |
21 | // 插件用样式
22 | @import './styles/weapp';
23 | @import './styles/collaborators';
24 | @import './styles/env';
25 | @import './styles/access';
26 | @import './styles/java';
27 |
28 | @import './styles/welcome';
--------------------------------------------------------------------------------
/app/styles/dark/styles/access.styl:
--------------------------------------------------------------------------------
1 | .panel {
2 | .access-url-container {
3 | color: $text-color;
4 | .access-url-panel {
5 | .panel-heading {
6 | background-color: $tab-bar-background-color;
7 | border-color: $tab-bar-border-color;
8 |
9 | input {
10 | background: $tab-bar-background-color;
11 | border: 1px solid $base-border-color;
12 | padding: 2px 10px;
13 | color: $text-color;
14 | }
15 | }
16 | .port-item {
17 | border-color: $tab-bar-border-color;
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/accordion.styl:
--------------------------------------------------------------------------------
1 | .accordion {
2 | .accordion-topbar {
3 | background: $second-background-color;
4 | border-color: $base-border-color;
5 | color: $text-color;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/bars.styl:
--------------------------------------------------------------------------------
1 | .menu-bar {
2 | background-color: $base-top-menu-bar-background;
3 | border-color: $base-border-color;
4 | color: $text-color;
5 | }
6 |
7 | .status-bar {
8 | border-color: $base-border-color;
9 | background-color: $base-top-menu-bar-background;
10 | color: $text-color;
11 |
12 | .git-branch-widget .widget-header {
13 | background-color: $base-background-color;
14 | color: $text-color;
15 | }
16 | }
17 |
18 | .breadcrumbs {
19 | background-color: $menu-background-color;
20 | border-color: $base-border-color;
21 | color: $text-color;
22 | .crumb {
23 | background-color: $menu-background-color;
24 | &:before {
25 | border-left-color: $base-border-color;
26 | }
27 | &:after {
28 | border-left-color: $menu-background-color;
29 | }
30 | }
31 | }
32 |
33 | .side-bar {
34 | color: $text-color;
35 | .side-bar-label:hover {
36 | background-color: $tab-background-color-hover;
37 | }
38 | .side-bar-label.active {
39 | background-color: $tab-background-color-active;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/base.styl:
--------------------------------------------------------------------------------
1 | body {
2 | scrollbar-face-color: $tab-bar-background-color ;
3 | scrollbar-track-color: $base-border-color;
4 | scrollbar-highlight-color: $tab-bar-background-color;
5 | scrollbar-shadow-color: $tab-bar-background-color ;
6 | scrollbar-3dlight-color: $tab-bar-background-color;
7 | scrollbar-arrow-color: $text-color;
8 | scrollbar-darkshadow-color: $tab-bar-background-color;
9 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/collaborators.styl:
--------------------------------------------------------------------------------
1 | .panel {
2 | .loading {
3 | color: $text-color;
4 | }
5 | .collaborators {
6 | color: $text-color;
7 | }
8 | .collaboration-chat {
9 | color: $text-color;
10 | .chat-editor {
11 | border-color: $tab-bar-border-color;
12 | }
13 | .chat-item .chat-item-body {
14 | color: $text-color;
15 | }
16 | .chat-item .chat-item-header .chat-item-time {
17 | color: $text-color-subtle;
18 | }
19 | .emoji-mart {
20 | background: $base-background-color;
21 | border-color: $tab-bar-border-color;
22 | .emoji-mart-category-label span {
23 | background: $base-background-color;
24 | color: $text-color;
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/diff.styl:
--------------------------------------------------------------------------------
1 | .CodeMirror-merge-l-chunk-end {
2 | border-bottom: 1px solid #88e;
3 | }
4 | .CodeMirror-merge-l-chunk-start {
5 | border-top: 1px solid #88e;
6 | }
7 | .CodeMirror-merge-l-chunk {
8 | background: $app-background-color;
9 | }
10 | .CodeMirror-merge-l-connect {
11 | fill: $app-background-color;
12 | stroke: #88e;
13 | stroke-width: 1px;
14 | }
15 | .CodeMirror-merge-gap {
16 | background-color: $app-background-color;
17 | border-color: $base-border-color;
18 | }
19 | .CodeMirror-merge {
20 | border-color: $base-border-color;
21 | }
22 |
23 | .git-merge {
24 | .diffModal {
25 | .loading {
26 | background-color: $app-background-color;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/editor.styl:
--------------------------------------------------------------------------------
1 | .cm-s-monokai, .cm-s-material {
2 | &.CodeMirror {
3 | background: $base-background-color;
4 | }
5 |
6 | .CodeMirror-gutters {
7 | background: $base-background-color;
8 | }
9 | }
10 |
11 | .image-editor-loading {
12 | .fa-spinner {
13 | color: $text-color;
14 | }
15 | }
16 |
17 | .status-bar-editor-widgets {
18 | display: flex;
19 | align-items: stretch;
20 | .editor-widget {
21 | display: flex;
22 | align-items: center;
23 | padding: 0 8px;
24 | &.enabled {
25 | background-color: #444;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/env.styl:
--------------------------------------------------------------------------------
1 | .panel {
2 | .env-list {
3 | .env-list-container .env-list-panel .panel-heading {
4 | color: $text-color;
5 | background-color: $tab-bar-background-color;
6 | border-color: $tab-bar-border-color;
7 | }
8 | .env-list-container .env-list-panel .env-item {
9 | border-color: $tab-bar-border-color;
10 | color: $text-color;
11 | &.current {
12 | border-left-color: #578ddf;
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/filelist.styl:
--------------------------------------------------------------------------------
1 | .file-list-container {
2 | color: $text-color;
3 | background-color: #252526;
4 | .file-list-item {
5 | color: $text-color;
6 | &.focus {
7 | color: $text-color-highlight;
8 | background-color: $tree-view-background-color-highlight;
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/filetree.styl:
--------------------------------------------------------------------------------
1 | .filetree-node.focus {
2 | color: $text-color-highlight;
3 | > .filetree-node-bg {
4 | background-color: $tree-view-background-color-highlight;
5 | }
6 | }
7 |
8 | .filetree-container {
9 | color: $text-color;
10 | background-color: #252526;
11 | .filetree-node-container.highlight,
12 | .filetree-node.focus {
13 | background-color: $tree-view-background-color-highlight;
14 | }
15 | }
16 | .filetree-node-icon, .breadcrumbs .crumb, .menu-item-container {
17 | .fa-folder, .fa-folder-open {
18 | color: $tree-view-folder-color;
19 | }
20 | }
21 |
22 | .git-filetree-container {
23 | .filetree-node {
24 | border-color: $base-border-color;
25 | }
26 | }
27 |
28 | .git-commit-message-container > textarea {
29 | background-color: $base-background-color;
30 | border-color: $base-border-color;
31 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/form.styl:
--------------------------------------------------------------------------------
1 | .ide-container {
2 | .form-control {
3 | color: $text-color;
4 | background-color: $base-background-color;
5 | border-color: $base-border-color;
6 | }
7 |
8 | .form-control[disabled] {
9 | background-color: $base-background-color;
10 | border-color: $base-border-color;
11 | }
12 |
13 | .btn-default {
14 | background-color: $base-background-color;
15 | border-color: $base-border-color;
16 | color: $text-color;
17 |
18 | &:hover, &:active, &:active:hover {
19 | background-color: #000;
20 | border-color: $base-border-color;
21 | color: $text-color;
22 | }
23 | }
24 |
25 | hr {
26 | border-color: $base-border-color;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/git-merge.styl:
--------------------------------------------------------------------------------
1 | .ide-container {
2 | .public_fixedDataTable_main {
3 | border-color: $base-border-color;
4 | }
5 | .public_fixedDataTableRow_main {
6 | background-color: $base-background-color;
7 | border-color: $base-border-color;
8 | color: $text-color;
9 | }
10 | .public_fixedDataTable_header, .public_fixedDataTable_header .public_fixedDataTableCell_main {
11 | background-color: $base-background-color;
12 | border-color: $base-border-color;
13 | color: $text-color;
14 | }
15 | .public_Scrollbar_main.public_Scrollbar_mainActive, .public_Scrollbar_main:hover {
16 | background-color: $base-background-color;
17 | }
18 |
19 | .public_Scrollbar_main:hover .public_Scrollbar_face:after, .public_Scrollbar_mainActive .public_Scrollbar_face:after, .public_Scrollbar_faceActive:after {
20 | background-color: $base-top-menu-bar-background;
21 | }
22 |
23 | .public_Scrollbar_face:after {
24 | background-color: $base-top-menu-bar-background;
25 | }
26 |
27 | .public_fixedDataTableRow_highlighted, .public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main {
28 | background-color: $base-top-menu-bar-background;
29 | border-color: $base-border-color;
30 | color: $text-color;
31 | }
32 |
33 | .public_fixedDataTableCell_main {
34 | background-color: $second-background-color;
35 | border-color: $base-border-color;
36 | color: $text-color;
37 | }
38 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/git.styl:
--------------------------------------------------------------------------------
1 | // .status { color: @text-color; }
2 | // .status-added { color: @text-color-success; } // green
3 | // .status-ignored { color: @text-color-subtle; } // faded
4 | // .status-modified { color: @text-color-warning; } // orange
5 | // .status-removed { color: @text-color-error; } // red
6 | // .status-renamed { color: @text-color-info; } // blue
7 |
8 | .git-untracked {
9 | color: $untracked-color;
10 | }
11 | .git-confliction {
12 | color: $confliction-color;
13 | }
14 | .git-modified {
15 | color: $modified-color;
16 | }
17 | .git-modify {
18 | color: $modified-color;
19 | }
20 | .git-changed {
21 | color: $modified-color;
22 | }
23 | .git-rename {
24 | color: $modified-color;
25 | }
26 | .git-copy {
27 | color: $modified-color;
28 | }
29 |
30 | .git-added {
31 | color: $added-color;
32 | }
33 | .git-add {
34 | color: $added-color;
35 | }
36 | .git-missing {
37 | text-decoration: line-through;
38 | }
39 |
40 | .git-removed {
41 | text-decoration: line-through;
42 | }
43 | .git-delete {
44 | text-decoration: line-through;
45 | }
46 |
47 | .file-status-indicator {
48 | &.modified, &.changed, &.modify {color: hsl(47,100%,50%)}
49 | &.untracked, &.add {color: hsl(80,60%,40%)}
50 | &.missing {color: hsl(10,80%,45%)}
51 | &.confliction {color: hsl(10,80%,45%)}
52 | }
53 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/history.styl:
--------------------------------------------------------------------------------
1 | .ide-history .history-container {
2 | .history-title {
3 | background-color: $base-top-menu-bar-background;
4 | border-color: $base-border-color;
5 | color: $text-color;
6 | }
7 | .public_fixedDataTableRow_main {
8 | background-color: $base-background-color;
9 | border-color: $base-border-color;
10 | color: $text-color;
11 | }
12 | .public_fixedDataTable_header, .public_fixedDataTable_header .public_fixedDataTableCell_main {
13 | background-color: $base-background-color;
14 | border-color: $base-border-color;
15 | color: $text-color;
16 | }
17 | .public_Scrollbar_main.public_Scrollbar_mainActive, .public_Scrollbar_main:hover {
18 | background-color: $base-background-color;
19 | }
20 |
21 | .public_Scrollbar_main:hover .public_Scrollbar_face:after, .public_Scrollbar_mainActive .public_Scrollbar_face:after, .public_Scrollbar_faceActive:after {
22 | background-color: $base-top-menu-bar-background;
23 | }
24 |
25 | .public_Scrollbar_face:after {
26 | background-color: $base-top-menu-bar-background;
27 | }
28 |
29 | .public_fixedDataTableRow_highlighted, .public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main {
30 | background-color: $base-top-menu-bar-background;
31 | border-color: $base-border-color;
32 | color: $text-color;
33 | }
34 |
35 | .public_fixedDataTableCell_main {
36 | background-color: $second-background-color;
37 | border-color: $base-border-color;
38 | color: $text-color;
39 | }
40 |
41 | }
42 |
43 | .git-logs-view {
44 | .flex-row {
45 | .message-text {
46 | color: $text-color;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/java.styl:
--------------------------------------------------------------------------------
1 | .modals-container .project-config-container .form-line .fa-folder-o {
2 | border-color: $tab-bar-border-color;
3 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/mask.styl:
--------------------------------------------------------------------------------
1 | .mask-container {
2 | background: rgba(0, 0, 0, 0.4);
3 | .progress {
4 | background-color: $base-background-color;
5 | color: $text-color;
6 | border: 1px solid $base-border-color;
7 | }
8 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/menu.styl:
--------------------------------------------------------------------------------
1 | .menu {
2 | color: $text-color;
3 | background-color: $menu-background-color;
4 | border-color: $menu-border-color;
5 | opacity: 0.95;
6 | > li > hr {
7 | border-color: $base-border-color;
8 | }
9 | }
10 |
11 | .menu-bar-container {
12 | background-color: $base-top-menu-bar-background;
13 | }
14 |
15 | .menu-bar-item-container, .menu-item-container {
16 | &.active {
17 | background-color: $menu-background-color-highlight;
18 | color: #fff;
19 | }
20 | &.disabled, &.active.disabled {
21 | background-color: transparent;
22 | color: $text-color-subtle;
23 | }
24 | }
25 |
26 | .menu-bar-item-logo {
27 | filter: invert(100%)
28 | }
29 |
--------------------------------------------------------------------------------
/app/styles/dark/styles/modal.styl:
--------------------------------------------------------------------------------
1 | .modal-container {
2 | .modal {
3 | background: $base-top-menu-bar-background;
4 | color: $text-color;
5 | .settings-header, .settings-header .tab-bar-header {
6 | border-color: $menu-border-color;
7 | }
8 |
9 | .settings-view {
10 | .modal-ops {
11 | border-color: $menu-border-color;
12 | }
13 | }
14 | }
15 | }
16 |
17 | .git-unstash-container .stash-list {
18 | border-color: $menu-border-color;
19 | background: $base-background-color;
20 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/panes.styl:
--------------------------------------------------------------------------------
1 | .panel-container, .pane-container {
2 | background-color: $base-background-color;
3 | border: 1px solid $base-border-color;
4 | .panel {
5 | background-color: $base-top-menu-bar-background;
6 | }
7 | }
8 | .resize-bar {
9 | &.col-resize {
10 | border-color: $base-border-color;
11 | }
12 | &.row-resize {
13 | border-color: $base-border-color;
14 | }
15 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/term.styl:
--------------------------------------------------------------------------------
1 | .ide-terminal {
2 | background: #000000;
3 | }
4 |
5 | .terminal-panel {
6 | .terminal-toolbar {
7 | color: #FFF;
8 | }
9 | }
--------------------------------------------------------------------------------
/app/styles/dark/styles/welcome.styl:
--------------------------------------------------------------------------------
1 | .welcome-page {
2 | color: $text-color;
3 | .subtitle {
4 | color: $text-color-subtle;
5 | }
6 | }
--------------------------------------------------------------------------------
/app/styles/lib.styl:
--------------------------------------------------------------------------------
1 | @import 'ui-variables';
2 | @import "mixins";
3 | @import 'scaffolding';
4 |
5 | @import "core-ui"
6 |
7 | .hidden {display: none !important;}
8 |
9 | .workstation {
10 | @import 'bootstrap/normalize';
11 | @import 'normalize';
12 | width: 100%;
13 | height: 100%;
14 | display: flex;
15 | flex-direction: column;
16 | align-items: stretch;
17 | }
18 |
19 | .utilities-container {
20 | isolation: isolate;
21 | z-index: z(utilities-container);
22 | }
23 |
--------------------------------------------------------------------------------
/app/styles/main.styl:
--------------------------------------------------------------------------------
1 | @import 'bootstrap/variables';
2 | @import 'bootstrap/mixins';
3 | // @import 'bootstrap/scaffolding';
4 |
5 | @import 'bootstrap/grid';
6 | @import 'bootstrap/buttons';
7 | @import 'bootstrap/forms';
8 | @import 'bootstrap/alerts';
9 |
10 | @import 'ui-variables';
11 | @import "mixins";
12 | @import 'scaffolding';
13 |
14 | @import "core-ui"
15 |
16 | @import "../../node_modules/fixed-data-table-2/dist/fixed-data-table.min.css"
17 |
18 | @import "../../node_modules/codemirror/lib/codemirror.css"
19 | @import "../../node_modules/codemirror/theme/monokai.css"
20 | @import "../../node_modules/codemirror/theme/material.css"
21 | @import "../../node_modules/codemirror/theme/neo.css"
22 | @import "../../node_modules/codemirror/theme/eclipse.css"
23 | @import "../../node_modules/file-icons-js/css/style.css"
24 | @import 'bootstrap/normalize';
25 | @import 'normalize';
26 |
27 | @import "../../node_modules/octicons/build/font/octicons.min.css"
28 |
29 | .hidden {display: none !important;}
30 |
31 | .ide-container {
32 | width: 100%;
33 | height: 100%;
34 | display: flex;
35 | flex-direction: column;
36 | align-items: stretch;
37 | }
38 |
39 | .utilities-container {
40 | isolation: isolate;
41 | z-index: z(utilities-container);
42 | }
43 |
--------------------------------------------------------------------------------
/app/styles/mixins/color.styl:
--------------------------------------------------------------------------------
1 | hsb($h-hsb, $s-hsb, $b-hsb, $a = 1)
2 | if $b-hsb == 0
3 | return rgba(hsla(0, 0, 0, $a))
4 | else
5 | $l-hsl = ($b-hsb / 2) * (2 - ($s-hsb / 100))
6 | $s-hsl = ($b-hsb * $s-hsb) / ($l-hsl < 50 ? $l-hsl * 2 : 200 - $l-hsl * 2)
7 | return rgba(hsla($h-hsb, $s-hsl, $l-hsl, $a))
8 |
9 | gray($amount) {
10 | lighten(#000000, $amount);
11 | }
--------------------------------------------------------------------------------
/app/styles/mixins/index.styl:
--------------------------------------------------------------------------------
1 | @import "z-index";
2 | @import "position";
3 | @import "color";
4 | @import "padding-margin";
5 | @import "misc";
6 | @import "scrollbar";
7 |
--------------------------------------------------------------------------------
/app/styles/mixins/misc.styl:
--------------------------------------------------------------------------------
1 | noSelect() {
2 | -webkit-user-select: none;
3 | -moz-user-select: none;
4 | -ms-user-select: none;
5 | -o-user-select: none;
6 | user-select: none;
7 | cursor: default;
8 | }
9 |
10 | codingMonkeyBackground() {
11 | background-image: url("https://dn-coding-net-production-static.qbox.me/static/cb75498a0ffd36e2c694da62f1bdb86c.svg");
12 | background-size: contain;
13 | background-repeat: no-repeat;
14 | background-position: center center;
15 | }
16 |
17 | // https://css-tricks.com/snippets/css/clear-fix/
18 | clearfix() {
19 | &:after {
20 | content: "";
21 | display: table;
22 | clear: both;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/styles/mixins/padding-margin.styl:
--------------------------------------------------------------------------------
1 | margin-lr($margin) {
2 | margin-left: $margin;
3 | margin-right: $margin;
4 | }
5 |
6 | margin-tb($margin) {
7 | margin-top: $margin;
8 | margin-bottom: $margin;
9 | }
10 |
11 | padding-lr($padding) {
12 | padding-left: $padding;
13 | padding-right: $padding;
14 | }
15 |
16 | padding-tb($padding) {
17 | padding-top: $padding;
18 | padding-bottom: $padding;
19 | }
20 |
--------------------------------------------------------------------------------
/app/styles/mixins/scrollbar.styl:
--------------------------------------------------------------------------------
1 | scrollBar() {
2 | &::-webkit-scrollbar {
3 | width: 8px;
4 | height: 8px;
5 | -webkit-border-radius: 8px;
6 | display: block;
7 | }
8 | &::-webkit-scrollbar-button {
9 | width: 0;
10 | height: 0;
11 | display: none;
12 | }
13 |
14 | &::-webkit-scrollbar-corner {
15 | background-color: transparent;
16 | }
17 | &::-webkit-scrollbar-thumb {
18 | background-color: rgba(91, 91, 91, 0.5);
19 | }
20 | &::-webkit-scrollbar-thumb:hover {
21 | background-color: rgba(170, 170, 170, 0.8);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/styles/mixins/z-index.styl:
--------------------------------------------------------------------------------
1 | // Mind the "Stacking Contexts" problem: https://philipwalton.com/articles/what-no-one-told-you-about-z-index/
2 | // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index
3 | // https://developer.mozilla.org/en-US/docs/Web/CSS/isolation
4 | z($e) {
5 | $z-index = null;
6 | for $sublist, $i in $z-index-list {
7 | if index($sublist, $e) != null {
8 | $z-index = index($sublist, $e)+$i*100;
9 | }
10 | }
11 |
12 | if ($z-index!=null) {
13 | return $z-index;
14 | } else {
15 | error('Missing element "'+$e+'" in $z-index-list');
16 | }
17 | }
18 |
19 | $z-index-list = \
20 | (placeholder),
21 | (main-pane-view modal-container),
22 | (pane-layout-overlay pane-resize-bar),
23 | (filetree-node-bg filetree-node-label),
24 | (modal-backdrop modal),
25 | (context-menu menu menu-bar-item-container),
26 | (utilities-container),
27 | (workspace-list);
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/styles/normalize.styl:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 | *:before,
5 | *:after {
6 | box-sizing: border-box;
7 | }
8 |
9 | input,
10 | button,
11 | select,
12 | textarea {
13 | font-family: inherit;
14 | font-size: inherit;
15 | line-height: inherit;
16 | }
17 |
18 | ul, ol {
19 | list-style-type: none;
20 | list-style-image: none;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 |
26 | hr {
27 | border: 0;
28 | border-top 1px solid grey;
29 | }
30 |
--------------------------------------------------------------------------------
/app/styles/workstation.styl:
--------------------------------------------------------------------------------
1 | @import 'bootstrap/variables';
2 | @import 'bootstrap/mixins';
3 | // @import 'bootstrap/scaffolding';
4 |
5 | @import 'bootstrap/grid';
6 | @import 'bootstrap/buttons';
7 | @import 'bootstrap/forms';
8 | @import 'bootstrap/alerts';
9 |
10 | @import 'ui-variables';
11 | @import "mixins";
12 | @import 'scaffolding';
13 |
14 | @import "core-ui"
15 |
16 | @import "../../node_modules/fixed-data-table-2/dist/fixed-data-table.min.css"
17 |
18 | @import "../../node_modules/codemirror/lib/codemirror.css"
19 | @import "../../node_modules/codemirror/theme/monokai.css"
20 | @import "../../node_modules/codemirror/theme/material.css"
21 | @import "../../node_modules/codemirror/theme/neo.css"
22 | @import "../../node_modules/codemirror/theme/eclipse.css"
23 | @import "../../node_modules/file-icons-js/css/style.css"
24 |
25 |
26 |
27 | .hidden {display: none !important;}
28 |
29 | .ide-container {
30 | @import 'bootstrap/normalize';
31 | @import 'normalize';
32 | width: 100%;
33 | height: 100%;
34 | display: flex;
35 | flex-direction: column;
36 | align-items: stretch;
37 | }
38 |
39 | .utilities-container {
40 | isolation: isolate;
41 | z-index: z(utilities-container);
42 | }
43 |
--------------------------------------------------------------------------------
/app/utils/RandColors.js:
--------------------------------------------------------------------------------
1 | const COLORS = [
2 | // the git tower palettes [mod]
3 | '#58c6f3', // blue
4 | '#6de6e9', // aqua
5 | '#ace09e', // grean
6 | '#59b38a', // olive
7 | '#f7d56b', // yellow
8 | '#f5a95f', // orange
9 | '#fe7874', // red
10 | '#967acc', // purple
11 | '#ea75f3', // violet
12 | '#d3d7ed', // light purplish gray
13 | ]
14 |
15 | export default class RandColors {
16 | constructor () {
17 | this.colors = COLORS.slice()
18 | this._index = 0
19 | this._period = this.colors.length
20 | }
21 |
22 | get () {
23 | return this.colors[this._index++ % this._period]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/utils/actions/createAction.js:
--------------------------------------------------------------------------------
1 | import dispatch from './dispatch'
2 |
3 | const actionCreatorFactory = genActionData => function createAction (eventName, actionPayloadCreator = (x => x)) {
4 | function action (...args) {
5 | const payload = actionPayloadCreator(...args)
6 | const actionData = genActionData(eventName, payload)
7 | dispatch(actionData)
8 | return actionData
9 | }
10 | action.toString = () => eventName
11 | return action
12 | }
13 |
14 | const createAction = actionCreatorFactory(
15 | (eventName, payload) => ({ type: eventName, payload })
16 | )
17 |
18 | createAction.promise = actionCreatorFactory((eventName, payload) => {
19 | let resolve, reject
20 | const promise = new Promise((rs, rj) => { resolve = rs; reject = rj })
21 | const meta = { promise, resolve, reject }
22 |
23 | promise.type = eventName
24 | promise.payload = payload
25 | promise.meta = meta
26 | promise.resolve = resolve
27 | promise.reject = reject
28 |
29 | return promise
30 | })
31 |
32 | export default createAction
33 |
--------------------------------------------------------------------------------
/app/utils/actions/dispatch.js:
--------------------------------------------------------------------------------
1 | import emitter from '../emitter'
2 |
3 | function isValidActionObj (actionObj) {
4 | if (!actionObj) return false
5 | if (typeof actionObj !== 'object') return false
6 | if (typeof actionObj.type !== 'string' || !actionObj.type) return false
7 | return true
8 | }
9 |
10 | function dispatch (actionObj) {
11 | if (isValidActionObj(actionObj)) {
12 | if (!actionObj.isDispatched) {
13 | emitter.emit(actionObj.type, actionObj)
14 | actionObj.isDispatched = true
15 | }
16 | } else if (__DEV__) {
17 | console.warn('Invalid action object is dispatched', actionObj)
18 | }
19 | return actionObj
20 | }
21 |
22 | export default dispatch
23 |
--------------------------------------------------------------------------------
/app/utils/actions/emitterMiddleware.js:
--------------------------------------------------------------------------------
1 | import emitterDispatch from './dispatch'
2 | export default function emitterMiddleware ({ dispatch, getState }) {
3 | return next => (action) => {
4 | emitterDispatch(action)
5 | return next(action)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/utils/actions/handleAction.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import { action as mobxAction } from 'mobx'
3 | import emitter from '../emitter'
4 |
5 | export default function handleAction (eventName, handler, state) {
6 | handler = mobxAction(eventName, handler)
7 | emitter.on(eventName, (actionMsg) => {
8 | // auto resolve/reject promisified actionMsg
9 | let result
10 | const resolve = actionMsg.resolve || (actionMsg.meta && actionMsg.meta.resolve)
11 | const reject = actionMsg.reject || (actionMsg.meta && actionMsg.meta.reject)
12 | // try {
13 | result = _.isUndefined(state) ? handler(actionMsg.payload, actionMsg)
14 | : handler(state, actionMsg.payload, actionMsg)
15 | if (_.isFunction(resolve)) resolve(result)
16 | // } catch (err) {
17 | // if (_.isFunction(reject)) reject(err)
18 | // else throw err
19 | // }
20 | })
21 | }
22 |
--------------------------------------------------------------------------------
/app/utils/actions/handleActions.js:
--------------------------------------------------------------------------------
1 | import handleAction from './handleAction'
2 |
3 | export default function handleActions (handlers, state) {
4 | const eventNames = Object.keys(handlers)
5 | eventNames.forEach((eventName) => {
6 | handleAction(eventName, handlers[eventName], state)
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/app/utils/actions/index.js:
--------------------------------------------------------------------------------
1 | import dispatch from './dispatch'
2 | import handleAction from './handleAction'
3 | import handleActions from './handleActions'
4 | import createAction from './createAction'
5 | import registerAction from './registerAction'
6 | import emitterMiddleware from './emitterMiddleware'
7 |
8 | export {
9 | dispatch,
10 | handleAction,
11 | handleActions,
12 | createAction,
13 | registerAction,
14 | emitterMiddleware
15 | }
16 |
--------------------------------------------------------------------------------
/app/utils/actions/registerAction.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import createAction from './createAction'
3 | import handleAction from './handleAction'
4 |
5 | function _registerAction (_createAction, ...args) {
6 | let [eventName, actionPayloadCreator, handler] = args
7 | if (!_.isString(eventName) || !_.isFunction(actionPayloadCreator)) throw Error('registerAction syntax error')
8 |
9 | if (!_.isFunction(handler)) {
10 | handler = actionPayloadCreator
11 | actionPayloadCreator = payload => payload
12 | }
13 |
14 | handleAction(eventName, handler)
15 |
16 | // by default we promisify the actionMsg
17 | const actionCreator = _createAction(eventName, actionPayloadCreator)
18 | return actionCreator
19 | }
20 |
21 | function registerActionPromise (...args) {
22 | return _registerAction(createAction.promise, ...args)
23 | }
24 |
25 | function registerActionNormal (...args) {
26 | return _registerAction(createAction, ...args)
27 | }
28 |
29 | const registerAction = registerActionPromise
30 | registerAction.promise = registerActionPromise
31 | registerAction.normal = registerActionNormal
32 |
33 | export default registerAction
34 |
--------------------------------------------------------------------------------
/app/utils/assignProps.js:
--------------------------------------------------------------------------------
1 | import is from './is'
2 |
3 | function assignProps (target, props, opts) {
4 | Object.keys(opts).forEach((key) => {
5 | const type = opts[key]
6 | const isType = typeof type === 'string' ? is[type] : is(type)
7 | if (type === 'any' || is.function(isType) && isType(props[key])) {
8 | target[key] = props[key]
9 | }
10 | })
11 | return target
12 | }
13 |
14 | export default assignProps
15 |
--------------------------------------------------------------------------------
/app/utils/codingPackageJsonp.js:
--------------------------------------------------------------------------------
1 | function codingPackageJsonp (exports) {
2 | codingPackageJsonp.data = exports
3 | }
4 |
5 | Object.defineProperty(codingPackageJsonp, 'data', {
6 | set (value) {
7 | if (this.current && this.current.startsWith('group')) {
8 | // const groupName = this.current.split('_')[1]
9 | const values = Array.isArray(value) ? value : [value]
10 | this.cache = (this.cache || []).concat(values)
11 | } else {
12 | this.cache = value
13 | }
14 | },
15 | get () { return this.cache }
16 | })
17 |
18 | codingPackageJsonp.groups = {}
19 | codingPackageJsonp.current = ''
20 |
21 | window.codingPackageJsonp = codingPackageJsonp
22 |
--------------------------------------------------------------------------------
/app/utils/colors/hueFromString.js:
--------------------------------------------------------------------------------
1 | export default function hueFromString (name) {
2 | let a = 1
3 | for (let i = 0; i < name.length; i++) {
4 | a = 17 * (a + name.charCodeAt(i)) % 360
5 | }
6 | return Math.round(a)
7 | }
8 |
--------------------------------------------------------------------------------
/app/utils/colors/index.js:
--------------------------------------------------------------------------------
1 | import chroma from './chroma'
2 | import hueFromString from './hueFromString'
3 |
4 | export { chroma, hueFromString }
5 |
--------------------------------------------------------------------------------
/app/utils/composeReducers.js:
--------------------------------------------------------------------------------
1 | export default function composeReducers () {
2 | const reducers = [...arguments]
3 | if (reducers.length === 0) return arg => arg
4 | if (reducers.length === 1) return reducers[0]
5 |
6 | const last = reducers[reducers.length - 1]
7 | const rest = reducers.slice(0, -1)
8 | return function composedReducers (state, action) {
9 | return reducers.reduceRight((lastState, reducer) => reducer(lastState, action), state)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/utils/decorators/defaultProps.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function getDisplayName (WrappedComponent) {
4 | return WrappedComponent.displayName || WrappedComponent.name || 'Component'
5 | }
6 |
7 | export default function defaultProps (propsMapper) {
8 | return function decorator (WrappedComponent) {
9 | const displayName = getDisplayName(WrappedComponent)
10 |
11 | function WrapperComponent (props) {
12 | const mergedProps = { ...propsMapper(props), ...props }
13 | return React.createElement(WrappedComponent, mergedProps)
14 | }
15 |
16 | WrapperComponent.displayName = displayName
17 | WrapperComponent.WrappedComponent = WrappedComponent
18 | return WrapperComponent
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/app/utils/decorators/index.js:
--------------------------------------------------------------------------------
1 | import mapEntityFactory from './mapEntity'
2 | import defaultProps from './defaultProps'
3 | import protectedObservable from './protectedObservable'
4 |
5 | export { mapEntityFactory, defaultProps, protectedObservable }
6 |
--------------------------------------------------------------------------------
/app/utils/decorators/mapEntity.js:
--------------------------------------------------------------------------------
1 | const get = (obj, prop) => {
2 | if (typeof obj.get === 'function') return obj.get(prop)
3 | return obj[prop]
4 | }
5 |
6 | function mapEntityFactory (entities) {
7 | // this decorator simply allows getting entity by id
8 | return function mapEntity (entityNames) {
9 | entityNames = Array.isArray(entityNames) ? entityNames : [...arguments]
10 | return function decorator (target, key, descriptor) {
11 | const fn = descriptor.value
12 | return {
13 | ...descriptor,
14 | value () {
15 | let args = [...arguments]
16 | args = args.map((entityId, i) => {
17 | const entityName = entityNames[i]
18 | if (entityName && typeof entityId === 'string') {
19 | return get(entities[entityName], entityId)
20 | }
21 | return entityId
22 | })
23 | return fn.apply(this, args)
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
30 | export default mapEntityFactory
31 |
--------------------------------------------------------------------------------
/app/utils/decorators/protectedObservable.js:
--------------------------------------------------------------------------------
1 | import { observable, computed } from 'mobx'
2 |
3 | /*
4 | * This decorator enforce a pattern that's widely used in this project,
5 | *
6 | * @protectedObservable _foo = 'bar'
7 | *
8 | * is a short hand for:
9 | *
10 | * @observable _foo = 'bar'
11 | * @computed
12 | * get foo () { return this._foo }
13 | * set foo (value) { return this._foo = value }
14 | *
15 | * you can specify publicKey explicitly by calling:
16 | * @protectedObservable('publicFoo') _foo = 'bar'
17 | */
18 | function _protectedObservableDecorator (target, privateKey, descriptor, publicKey) {
19 | if (!publicKey) publicKey = privateKey.replace(/^_/, '')
20 |
21 | const computedDescriptor = computed(target, publicKey, {
22 | get () { return this[privateKey] },
23 | set (v) { return this[privateKey] = v },
24 | })
25 |
26 | Object.defineProperty(target, publicKey, computedDescriptor)
27 |
28 | return observable(target, privateKey, descriptor)
29 | }
30 |
31 | function protectedObservable (optionalPublicKey) {
32 | if (typeof optionalPublicKey === 'string') {
33 | return function protectedObservableDecorator (target, key, descriptor) {
34 | return _protectedObservableDecorator(target, key, descriptor, optionalPublicKey)
35 | }
36 | } else {
37 | return _protectedObservableDecorator.apply(null, arguments)
38 | }
39 | }
40 | export default protectedObservable
41 |
--------------------------------------------------------------------------------
/app/utils/dynamicStyle.js:
--------------------------------------------------------------------------------
1 | import { observable, autorun } from 'mobx'
2 | import mtln from './multiline'
3 |
4 | class DynamicStyle {
5 | constructor () {
6 | const style = document.createElement('style')
7 | document.body.appendChild(style)
8 |
9 | autorun(() => {
10 | style.innerHTML = this.styles.values().reduce((concatStyleText, styleText) => {
11 | return concatStyleText + '\n' + mtln(styleText)
12 | }, '')
13 | })
14 | }
15 |
16 | @observable styles = observable.map()
17 |
18 | get (key) {
19 | return this.styles.get(key)
20 | }
21 |
22 | set (key, value) {
23 | this.styles.set(key, value)
24 | }
25 |
26 | }
27 |
28 | const dynamicStyle = new DynamicStyle()
29 |
30 | export default dynamicStyle
31 |
--------------------------------------------------------------------------------
/app/utils/emitter/index.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'eventemitter3'
2 |
3 | export default new EventEmitter()
4 |
5 | export const PANEL_RESIZED = 'PANEL_RESIZED'
6 | export const PANEL_SHOW = 'PANEL_SHOW'
7 | export const PANEL_HIDE = 'PANEL_HIDE'
8 | export const THEME_CHANGED = 'THEME_CHANGED'
9 | export const SOCKET_TRIED_FAILED = 'SOCKET_TRIED_FAILED'
10 | export const SOCKET_RETRY = 'SOCKET_RETRY'
11 | export const FILE_CHANGE = 'FILE_CHANGE'
12 | export const FILE_HIGHLIGHT = 'FILE_HIGHLIGHT'
13 | export const TERM_ENV_HIDE = 'TERM_ENV_HIDE'
14 | export const TERM_ENV_SHOW = 'TERM_ENV_SHOW'
15 | export const TERMINAL_SHOW = 'TERMINAL_SHOW'
16 | export const GITGRAPH_SHOW = 'GITGRAPH_SHOW'
17 |
--------------------------------------------------------------------------------
/app/utils/exports.js:
--------------------------------------------------------------------------------
1 | export * as actions from './actions'
2 | export * as assignProps from './assignProps'
3 | export * as codingPackageJsonp from './codingPackageJsonp'
4 | export * as colors from './colors'
5 | export * as decorators from './decorators'
6 | export * as dnd from './dnd'
7 | export * as emitter from './emitter'
8 | export * as extendObservableStrict from './extendObservableStrict'
9 | export * as getBackoff from './getBackoff'
10 | export * as getCookie from './getCookie'
11 | export * as getTabType from './getTabType'
12 | export * as handleActions from './handleActions'
13 | export * as hasVimium from './hasVimium'
14 | export * as immutableUpdate from './immutableUpdate'
15 | export * as is from './is'
16 | export * as loadStyle from './loadStyle'
17 | export * as multiline from './multiline'
18 | export * as path from './path'
19 | export * as RandColors from './RandColors'
20 | export * as request from './request'
21 | export * as setSelectionRange from './setSelectionRange'
22 | export * as stepFactory from './stepFactory'
23 | export * as qs from './qs'
24 | export { default as withTheme } from './withTheme'
25 | export * as maskActions from '../components/Mask/actions'
26 |
--------------------------------------------------------------------------------
/app/utils/extendObservableStrict.js:
--------------------------------------------------------------------------------
1 | import { extendObservable } from 'mobx'
2 |
3 | // extendObservableStrict is like extendObservable,
4 | // except it only accept extension properties that appeared in defaults
5 | export default function extendObservableStrict (obj, defaults, extensions) {
6 | const acceptableKeys = Object.keys(defaults)
7 | const acceptables = Object.keys(extensions).reduce((acc, key) => {
8 | if (acceptableKeys.includes(key)) acc[key] = extensions[key]
9 | return acc
10 | }, {})
11 | return extendObservable(obj, defaults, acceptables)
12 | }
--------------------------------------------------------------------------------
/app/utils/getBackoff.js:
--------------------------------------------------------------------------------
1 | import Backoff from 'backo2'
2 |
3 | const defaultConfig = {
4 | delayMin: 1500,
5 | delayMax: 10000,
6 | }
7 |
8 | export default function getBackoff (config = {}) {
9 | config = { ...defaultConfig, ...config }
10 | const backoff = new Backoff({
11 | min: config.delayMin,
12 | max: config.delayMax,
13 | jitter: 0.5,
14 | })
15 |
16 | return backoff
17 | }
18 |
--------------------------------------------------------------------------------
/app/utils/getCookie.js:
--------------------------------------------------------------------------------
1 | import memoize from 'lodash/memoize'
2 |
3 | function getCookie (name) {
4 | const value = `; ${document.cookie}`
5 | const parts = value.split(`; ${name}=`)
6 | if (parts.length == 2) return parts.pop().split(';').shift()
7 | }
8 |
9 | export default memoize(getCookie, name => `${name}@${document.cookie}`)
10 |
--------------------------------------------------------------------------------
/app/utils/getTabType.js:
--------------------------------------------------------------------------------
1 | export default function getTabType (node) {
2 | if (
3 | /^text\/[^/]+/.test(node.contentType) || (
4 | node.contentType === 'application/xml'
5 | ) || (
6 | node.contentType === 'application/x-sh'
7 | ) || (
8 | node.contentType === 'application/xhtml+xml'
9 | )) {
10 | return 'TEXT'
11 | } else if (/^image\/[^/]+/.test(node.contentType)) {
12 | if (node.contentType === 'image/vnd.adobe.photoshop') {
13 | return 'UNKNOWN'
14 | }
15 | if (node.contentType === 'image/jpeg' || node.contentType === 'image/png' || node.contentType === 'image/bmp' || node.contentType === 'image/gif') {
16 | return 'IMAGE'
17 | }
18 | }
19 | // Unknown
20 | return 'UNKNOWN'
21 | }
22 |
--------------------------------------------------------------------------------
/app/utils/handleActions.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 |
3 | const wrapHandler = handler => function (state, action) {
4 | return handler(state, action.payload, action)
5 | }
6 |
7 | export default function newHandleActions (handlers, defaultState) {
8 | const wrappedHandlers = Object.keys(handlers).reduce(
9 | (h, key) => {
10 | h[key] = wrapHandler(handlers[key])
11 | return h
12 | }, {})
13 |
14 | return handleActions(wrappedHandlers, defaultState)
15 | }
16 |
--------------------------------------------------------------------------------
/app/utils/hasVimium.js:
--------------------------------------------------------------------------------
1 | export default function hasVimium () {
2 | try {
3 | const shadowRoot = document.querySelector('html > div').shadowRoot
4 | return Boolean(shadowRoot.querySelector('style').textContent.match(/vimium/))
5 | } catch (e) {
6 | return false
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/app/utils/immutableUpdate.js:
--------------------------------------------------------------------------------
1 | import update from 'immutability-helper'
2 | import _ from 'lodash'
3 |
4 | // global update extends
5 | const removeValue = (valueToRemove, original) => {
6 | if (_.isArray(original)) {
7 | return _.without(original, valueToRemove)
8 | }
9 | if (_.isObject(original)) {
10 | return _.reduce(original, (result, value, key) => {
11 | if (value !== valueToRemove) result[key] = value
12 | return result
13 | }, {})
14 | }
15 | return original
16 | }
17 |
18 | update.extend('$removeValue', removeValue)
19 | update.extend('$without', removeValue)
20 |
21 | const removeKey = (keysToRemove, original) => {
22 | if (!_.isArray(original) && !_.isObject(original)) return original
23 | if (!_.isArray(keysToRemove)) keysToRemove = [keysToRemove]
24 | return _.reduce(original, (result, value, key) => {
25 | if (!keysToRemove.includes(key)) result[key] = value
26 | return result
27 | }, _.isArray(original) ? [] : {})
28 | }
29 |
30 | update.extend('$removeKey', removeKey)
31 | update.extend('$delete', removeKey)
32 |
33 | update.extend('$map', (fn, original) => {
34 | if (_.isArray(original)) return _.map(original, fn)
35 | if (_.isObject(original)) return _.mapValues(original, fn)
36 | return original
37 | })
38 |
39 | export default update
40 |
--------------------------------------------------------------------------------
/app/utils/index.js:
--------------------------------------------------------------------------------
1 | export path from './path'
2 | export request from './request'
3 | export setSelectionRange from './setSelectionRange'
4 | export composeReducers from './composeReducers'
5 | export update from './immutableUpdate'
6 | export qs from './qs'
7 | export { PluginRegistry } from './plugins.js'
8 | export stepFactory from './stepFactory'
9 | export loadStyle from './loadStyle'
10 | export codingPackageJsonp from './codingPackageJsonp'
11 | export emitter, * as E from './emitter'
12 | export handleActions from './handleActions'
13 | export getTabType from './getTabType'
14 | export dnd from './dnd'
15 | export getCookie from './getCookie'
16 | export getBackoff from './getBackoff'
17 | export multiline from './multiline'
18 | export is from './is'
19 | // export i18n from './createI18n'
20 |
--------------------------------------------------------------------------------
/app/utils/is.js:
--------------------------------------------------------------------------------
1 | import isNull from 'lodash/isNull'
2 | import isUndefined from 'lodash/isUndefined'
3 | import isString from 'lodash/isString'
4 | import isBoolean from 'lodash/isBoolean'
5 | import isFunction from 'lodash/isFunction'
6 | import isArray from 'lodash/isArray'
7 | import isPlainObject from 'lodash/isPlainObject'
8 | import isNaN from 'lodash/isNaN'
9 | import _isNumber from 'lodash/isNumber'
10 |
11 | const isNumber = n => {
12 | if (isNaN(n)) return false
13 | return _isNumber(n)
14 | }
15 |
16 | function is (type) {
17 | if (isUndefined(type)) return isUndefined
18 | if (isNull(type)) return isNull
19 | switch (type) {
20 | case String:
21 | return isString
22 | case Number:
23 | return isNumber
24 | case Boolean:
25 | return isBoolean
26 | case Function:
27 | return isFunction
28 | case Array:
29 | return isArray
30 | case Object:
31 | return isPlainObject
32 | default:
33 | return undefined
34 | }
35 | }
36 |
37 | export default Object.assign(is, {
38 | null: isNull,
39 | undefined: isUndefined,
40 | string: isString,
41 | number: isNumber,
42 | boolean: isBoolean,
43 | function: isFunction,
44 | array: isArray,
45 | pojo: isPlainObject,
46 | plainObject: isPlainObject,
47 | })
48 |
49 | export {
50 | isNull,
51 | isUndefined,
52 | isString,
53 | isNumber,
54 | isBoolean,
55 | isFunction,
56 | isArray,
57 | isPlainObject,
58 | }
59 |
--------------------------------------------------------------------------------
/app/utils/loadStyle.js:
--------------------------------------------------------------------------------
1 | function loadStyle (cssUrl) {
2 | const styleElement = document.createElement('link')
3 | styleElement.rel = 'stylesheet'
4 | styleElement.type = 'text/css'
5 | styleElement.href = cssUrl
6 |
7 | const head = document.getElementsByTagName('head')[0]
8 | return {
9 | use () {
10 | head.appendChild(styleElement)
11 | },
12 |
13 | unuse () {
14 | head.removeChild(styleElement)
15 | }
16 | }
17 | }
18 |
19 | export default loadStyle
20 |
--------------------------------------------------------------------------------
/app/utils/multiline.js:
--------------------------------------------------------------------------------
1 | import flattenDeep from 'lodash/flattenDeep'
2 | import zip from 'lodash/zip'
3 |
4 | export default function multiline (strings, ...keys) {
5 | const oStr = Array.isArray(strings) ? flattenDeep(zip(strings, keys)).join('') : strings
6 | const reIndent = /^[\ \t]+/
7 | let minIndentWidth = Infinity
8 | let minIndent = ''
9 | const oStrSplitted = oStr.split('\n')
10 | // discard first empty line
11 | if (oStrSplitted[0].replace(/\s*/g, '') === '') oStrSplitted.shift()
12 | return oStrSplitted.reduce((acc, str, idx) => {
13 | const matchOfIndent = reIndent.exec(str)
14 | const indent = matchOfIndent ? matchOfIndent[0] : ''
15 | if (indent.length !== str.length && indent.length < minIndentWidth) {
16 | minIndentWidth = indent.length
17 | minIndent = indent
18 | }
19 | acc.push(str)
20 | return acc
21 | }, []).map(str => str.replace(RegExp(`^${minIndent}`), '')).join('\n')
22 | }
23 |
--------------------------------------------------------------------------------
/app/utils/path.js:
--------------------------------------------------------------------------------
1 | export default {
2 | join () {
3 | const path = Array.prototype.join.call(arguments, '/')
4 | return path.split(/\/+/).join('/')
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/app/utils/plugins.js:
--------------------------------------------------------------------------------
1 | export const PluginRegistry = {
2 | _plugins: {},
3 | get packages () {
4 | return this._plugins
5 | },
6 | delete (key) {
7 | delete this._plugins[key]
8 | },
9 | get (key) {
10 | return this._plugins[key]
11 | },
12 | set (key, plugin) {
13 | this._plugins[key] = plugin
14 | },
15 | find (pkgId) {
16 | return Object.values(this._plugins).find(value => value.pkgId === pkgId)
17 | },
18 | findAll (pkgId) {
19 | return Object.values(this._plugins).filter(value => value.pkgId === pkgId)
20 | },
21 | findAllByType (loadType) {
22 |
23 | return Object.values(this._plugins).filter(value => value.loadType === loadType)
24 | }
25 | }
26 | // for test
27 | window.PluginRegistry = PluginRegistry
--------------------------------------------------------------------------------
/app/utils/promise.prototype.finalCatch.js:
--------------------------------------------------------------------------------
1 | if (typeof Promise !== 'function' && typeof Promise !== 'object') {
2 | throw `Cannot polyfill Promise when it is ${JSON.stringify(Promise)}`
3 | }
4 |
5 | const protoThen = Promise.prototype.then
6 | const protoCatch = Promise.prototype.catch
7 |
8 | const wrappedCatch = function (onRejected) {
9 | const original = this
10 | const newPromise = protoCatch.call(this, onRejected)
11 |
12 | if (original._finalCatchHandler) {
13 | original._isInherited = true
14 | newPromise.finalCatch(original._finalCatchHandler)
15 | }
16 |
17 | return newPromise
18 | }
19 |
20 | const wrappedThen = function () {
21 | const original = this
22 | const newPromise = protoThen.apply(this, arguments)
23 |
24 | if (original._finalCatchHandler) {
25 | original._isInherited = true
26 | newPromise.finalCatch(original._finalCatchHandler)
27 | }
28 |
29 | return newPromise
30 | }
31 |
32 | Promise.prototype.finalCatch = function (onRejected) {
33 | _this = this
34 | this._finalCatchHandler = function () {
35 | onRejected(...arguments)
36 | _this._finalCatchHandler = ((f) => {})
37 | }
38 | protoThen.call(this, res => res, (err) => {
39 | if (!this._isInherited) {
40 | this._finalCatchHandler(err)
41 | }
42 | })
43 | // this.then = wrappedThen
44 | // this.catch = wrappedCatch
45 | }
46 |
47 | Promise.prototype.then = wrappedThen
48 | Promise.prototype.catch = wrappedCatch
49 |
--------------------------------------------------------------------------------
/app/utils/qs.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs'
2 |
3 | // source: https://github.com/jfromaniello/url-join
4 | function urlJoin () {
5 | let str = [].slice.call(arguments, 0).join('/')
6 |
7 | // make sure protocol is followed by two slashes
8 | str = str.replace(/:\//g, '://')
9 |
10 | // remove consecutive slashes
11 | str = str.replace(/([^:\s])\/+/g, '$1/')
12 |
13 | // remove trailing slash before parameters or hash
14 | str = str.replace(/\/(\?|&|#[^!])/g, '$1')
15 |
16 | // replace ? in parameters with &
17 | str = str.replace(/(\?.+)\?/g, '$1&')
18 |
19 | return str
20 | }
21 |
22 | const _stringify = qs.stringify.bind(qs)
23 | qs.stringify = function (queryObject) {
24 | // https://github.com/ljharb/qs#stringifying
25 | return _stringify(queryObject, { arrayFormat: 'brackets' })
26 | }
27 |
28 | export default qs
29 |
--------------------------------------------------------------------------------
/app/utils/setSelectionRange.js:
--------------------------------------------------------------------------------
1 | export default function setSelectionRange (input, start, end) {
2 | if (input.setSelectionRange) {
3 | input.focus()
4 | input.setSelectionRange(start, end)
5 | } else if (typeof input.selectionStart !== 'undefined') {
6 | input.selectionStart = start
7 | input.selectionEnd = end
8 | input.focus()
9 | } else if (input.createTextRange) {
10 | const selRange = input.createTextRange()
11 | selRange.collapse(true)
12 | selRange.moveStart('character', start)
13 | selRange.moveEnd('character', end)
14 | selRange.select()
15 | input.focus()
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/app/utils/toJS.js:
--------------------------------------------------------------------------------
1 | import { toJS } from 'mobx'
2 |
3 | export const mapToJS = map => map.entries()
4 | .reduce((p, v) => {
5 | const newValue = v[1].toJS ? v[1].toJS() : toJS(v[1])
6 | if (newValue) {
7 | p[v[0]] = newValue
8 | }
9 | return p
10 | }, {})
11 |
--------------------------------------------------------------------------------
/app/utils/withTheme.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { ThemeProvider } from 'styled-components'
3 | import { observer } from 'mobx-react'
4 |
5 | export default Com => observer((props) => {
6 | const theme = window.themeVariables.toJS()
7 | return (
8 |
9 |
10 |
11 | )
12 | })
13 |
--------------------------------------------------------------------------------
/app/workspaces_standalone/backendAPI.js:
--------------------------------------------------------------------------------
1 | import { request } from '../utils'
2 |
3 | export function getWorkspaces () {
4 | return request.get('/workspaces')
5 | }
6 |
7 | export function createWorkspace (url) {
8 | return request.post('/workspaces', { url })
9 | }
10 |
11 | export function deleteWorkspace (spaceKey) {
12 | return request.delete(`/workspaces/${spaceKey}`)
13 | }
14 |
15 | export function getPublicKey () {
16 | return request.get('/user?public_key')
17 | }
18 |
--------------------------------------------------------------------------------
/app/workspaces_standalone/bootstrapping.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/app/workspaces_standalone/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Coding WebIDE 开启云端开发模式! - Coding.net
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/workspaces_standalone/index.jsx:
--------------------------------------------------------------------------------
1 | import { AppContainer } from 'react-hot-loader'
2 | import React from 'react'
3 | import { render } from 'react-dom'
4 | import { Provider } from 'react-redux'
5 | import WorkspaceList from './WorkspaceList'
6 | import bootstrapping from './bootstrapping'
7 | import '../styles/main.styl'
8 | import '../styles/base-theme/index.styl'
9 | import store from './store'
10 |
11 | // first bootstrap the state, load the store
12 | bootstrapping()
13 |
14 | // finally render the component
15 | const rootElement = document.getElementById('root')
16 |
17 | if (__DEV__) {
18 | const hotLoaderRender = () =>
19 | render(
20 |
21 |
22 |
23 | , rootElement)
24 |
25 | hotLoaderRender()
26 | if (module.hot) module.hot.accept('./WorkspaceList', hotLoaderRender)
27 | } else {
28 | render(, rootElement)
29 | }
30 |
--------------------------------------------------------------------------------
/app/workspaces_standalone/reducer.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 | import {
3 | WORKSPACE_FETCH_PUBLIC_KEY,
4 | WORKSPACE_FETCH_LIST,
5 | WORKSPACE_OPEN,
6 | WORKSPACE_CREATING,
7 | WORKSPACE_CREATING_ERROR
8 | } from './actions'
9 |
10 | const defaultState = {
11 | route: 'WORKSPACES',
12 | workspaces: [],
13 | currentWorkspace: null,
14 | publicKey: null,
15 | fingerprint: null,
16 | isCreating: false,
17 | errMsg: null
18 | }
19 |
20 | export default handleActions({
21 | [WORKSPACE_FETCH_PUBLIC_KEY]: (state, action) => {
22 | const { publicKey, fingerprint } = action.payload
23 | return { ...state, publicKey, fingerprint }
24 | },
25 |
26 | [WORKSPACE_FETCH_LIST]: (state, action) => ({ ...state, workspaces: action.payload }),
27 |
28 | [WORKSPACE_OPEN]: (state, action) => ({ ...state, route: 'IDE', currentWorkspace: action.payload }),
29 |
30 | [WORKSPACE_CREATING]: (state, action) => ({ ...state, isCreating: action.payload }),
31 |
32 | [WORKSPACE_CREATING_ERROR]: (state, action) => ({ ...state, isCreating: false, errMsg: action.payload })
33 | }, defaultState)
34 |
--------------------------------------------------------------------------------
/app/workspaces_standalone/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux'
2 | import thunkMiddleware from 'redux-thunk'
3 | import WorkspaceReducer from './reducer'
4 |
5 | const enhancer = compose(
6 | applyMiddleware(thunkMiddleware),
7 | window.devToolsExtension ? window.devToolsExtension({
8 | serialize: {
9 | replacer: (key, value) => {
10 | if (key === 'editor') return {}
11 | if (key === 'DOMNode') return {}
12 | return value
13 | }
14 | }
15 | }) : f => f
16 | )
17 | const store = createStore(WorkspaceReducer, enhancer)
18 |
19 | export default store
20 | export const getState = store.getState
21 | export const dispatch = store.dispatch
22 |
--------------------------------------------------------------------------------
/app/workstation.jsx:
--------------------------------------------------------------------------------
1 | import { AppContainer } from 'react-hot-loader'
2 | import React from 'react'
3 | import { render } from 'react-dom'
4 | import Root from './containers/Root'
5 | import WorkStation from './workstation/workstationFull'
6 | import './styles/workstation.styl'
7 | import initialize from './initialize'
8 | import InitializeContainer from './containers/Initialize'
9 | import SettingState from 'components/Setting/state'
10 | const uiTheme = SettingState.settings.appearance.ui_theme.value
11 | if (uiTheme === 'base-theme') {
12 | const baseTheme = require('!!style-loader/useable!css-loader!stylus-loader!./styles/base-theme/index.styl')
13 | baseTheme.use()
14 | window.themes = { '@current': baseTheme }
15 | } else {
16 | const darkTheme = require('!!style-loader/useable!css-loader!stylus-loader!./styles/dark/index.styl')
17 | darkTheme.use()
18 | window.themes = { '@current': darkTheme }
19 | }
20 |
21 | const rootElement = document.getElementById('root')
22 | render(, rootElement)
23 |
24 | async function startApp (module) {
25 | if (__DEV__) {
26 | const hotLoaderRender = () =>
27 | render(, rootElement)
28 |
29 | hotLoaderRender()
30 | if (module.hot) module.hot.accept('./workstation/workstationFull', hotLoaderRender)
31 | } else {
32 | render(, rootElement)
33 | }
34 | }
35 |
36 | startApp(module)
37 |
38 | const log = (...args) => console.log(...args) || (x => x)
39 | if (__VERSION__) log(`[VERSION] ${__VERSION__}`)
40 |
--------------------------------------------------------------------------------
/app/workstation/index.js:
--------------------------------------------------------------------------------
1 | import WorkStation from './workstationFull'
2 |
3 | export default WorkStation
4 |
--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 |
5 | root /usr/share/nginx/html;
6 |
7 | location / {
8 | index index.html index.htm;
9 | }
10 |
11 | location /ws {
12 | rewrite ^/ws/.*$ /workspace.html break;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding/WebIDE-Frontend/fab3d991cd90be0c04ec7c57332bca32c6e9cddf/static/favicon.ico
--------------------------------------------------------------------------------
/task.yaml.tpl:
--------------------------------------------------------------------------------
1 | apps:
2 | - script : npm
3 | name : 'main-project'
4 | args: start
5 | exec_mode: fork_mode
6 | exec_interpreter: node
7 |
8 | - script : npm
9 | name : 'main-packageList'
10 | args: run packageList
11 | exec_mode: fork_mode
12 | exec_interpreter: node
13 |
14 | - script : npm
15 | name : 'plugin-platform'
16 | cwd: /Users/zhengxinqi/mycodingjob/webide/WebIDE-Plugin-Platform
17 | args: start
18 | env:
19 | PORT: 4001
20 | exec_mode: fork_mode
21 | exec_interpreter: node
22 |
23 | - script : npm
24 | name : 'plugin-debugger'
25 | cwd: /Users/zhengxinqi/mycodingjob/webide/WebIDE-Plugin-Debugger
26 | args: start
27 | env:
28 | PORT: 4002
29 | exec_mode: fork_mode
30 | exec_interpreter: node
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /*eslint-disable*/
2 | require('dotenv').config()
3 | var production = ['production', 'prod', 'qprod'];
4 |
5 | // module.exports = (process.env.NODE_ENV && production.indexOf(process.env.NODE_ENV) > -1) ?
6 | // require('./webpack_configs/webpack.prod.config.js')
7 | // : (process.env.NODE_ENV === 'staging' ? require('./webpack_configs/webpack.staging.config.js') :
8 | // require('./webpack_configs/webpack.dev.config.js'))
9 | if (process.env.RUN_MODE === 'lib') {
10 | if (process.env.NODE_ENV && process.env.NODE_ENV === 'dev') {
11 | module.exports = require('./webpack_configs/webpack.lib.dev.config.js')
12 | } else {
13 | module.exports = require('./webpack_configs/webpack.lib.config.js')
14 | }
15 | } else if (process.env.NODE_ENV && production.indexOf(process.env.NODE_ENV) > -1) {
16 | module.exports = require('./webpack_configs/webpack.prod.config.js')
17 | } else if (process.env.NODE_ENV === 'staging') {
18 | module.exports = require('./webpack_configs/webpack.staging.config.js')
19 | } else {
20 | module.exports = require('./webpack_configs/webpack.dev.config.js')
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/webpack_configs/devServer.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 |
3 | // 调用平台版后台
4 | const isPlatform = Boolean(process.env.RUN_MODE);
5 |
6 | module.exports = function (options) {
7 | return {
8 | devServer: {
9 | hot: true,
10 | inline: true,
11 | host: options.host || '0.0.0.0',
12 | port: options.port || 8060,
13 | historyApiFallback: {
14 | rewrites: [
15 | { from: /\/ws/, to: '/workspace.html' },
16 | { from: /\/login/, to: '/login.html' }
17 | ]
18 | }
19 | },
20 | plugins: [
21 | new webpack.HotModuleReplacementPlugin({ multiStep: false }),
22 | new webpack.NamedModulesPlugin(),
23 | ]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/webpack_configs/loaders/regexp-replace-loader.js:
--------------------------------------------------------------------------------
1 | module.exports = function (source) {
2 | var query = this.query;
3 | var re = new RegExp(query.match.pattern, query.match.flags);
4 | return source.replace(re, query.replaceWith);
5 | };
6 |
--------------------------------------------------------------------------------
/webpack_configs/stylesheet.config.js:
--------------------------------------------------------------------------------
1 | const bootstrap = require('bootstrap-styl')
2 | const stylusLoader = require('stylus-loader')
3 |
4 | module.exports = function (paths) {
5 | return {
6 | module: {
7 | rules: [
8 | {
9 | test: /\.woff2?\??([a-f\d]+)?(v=\d+\.\d+\.\d+)?$/,
10 | use: ['file-loader']
11 | // loader: "url?limit=10000&mimetype=application/font-woff"
12 | }, {
13 | test: /\.ttf\??([a-f\d]+)?(v=\d+\.\d+\.\d+)?$/,
14 | use: ['file-loader']
15 | // loader: "url?limit=10000&mimetype=application/octet-stream"
16 | }, {
17 | test: /\.eot\??([a-f\d]+)?(v=\d+\.\d+\.\d+)?$/,
18 | use: ['file-loader']
19 | }, {
20 | test: /\.svg\??([a-f\d]+)?(v=\d+\.\d+\.\d+)?$/,
21 | use: ['file-loader']
22 | // loader: "url?limit=10000&mimetype=image/svg+xml"
23 | }, {
24 | test: /\.styl$/,
25 | use: [
26 | 'style-loader',
27 | 'css-loader',
28 | 'stylus-loader'
29 | ]
30 | }, {
31 | test: /\.css$/,
32 | use: ['style-loader', 'css-loader']
33 | }
34 | ]
35 | },
36 | // https://github.com/shama/stylus-loader/issues/149
37 | // https://github.com/shama/stylus-loader/pull/154/files#diff-0444c5b7c3bc2c340b3654c507443b06R35
38 | plugins: [
39 | new (stylusLoader.OptionsPlugin)({
40 | default: { use: [bootstrap()] }
41 | })
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/webpack_configs/uglify.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 |
3 | module.exports = function (options) {
4 | return {
5 | plugins: [
6 | new webpack.optimize.UglifyJsPlugin({
7 | beautify: false,
8 | comments: false,
9 | compress: {
10 | warnings: false,
11 | drop_console: true
12 | },
13 | mangle: {
14 | except: ['$'],
15 | screw_ie8: true
16 | }
17 | })
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/webpack_configs/webpack.lib.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const merge = require('webpack-merge')
4 | const str = JSON.stringify
5 | const commonConfig = require('./workstation.config.js')
6 |
7 | const stylesheet = require('./stylesheet.lib.config')
8 | const uglify = require('./uglify.config')
9 |
10 | module.exports = merge(
11 | commonConfig({
12 | staticDir: '/',
13 | }),
14 | stylesheet(),
15 | uglify(),
16 | {
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | __DEV__: false,
20 | __RUN_MODE__: str(process.env.RUN_MODE || ''),
21 | __BACKEND_URL__: str(process.env.BACKEND_URL || ''),
22 | __WS_URL__: str(process.env.WS_URL || ''),
23 | __STATIC_SERVING_URL__: str(process.env.STATIC_SERVING_URL || ''),
24 | __PACKAGE_DEV__: false,
25 | __PACKAGE_SERVER__: str(process.env.PACKAGE_SERVER || process.env.HTML_BASE_URL || ''),
26 | __NODE_ENV__: str(process.env.NODE_ENV || ''),
27 | }),
28 | ]
29 | }
30 | )
31 |
--------------------------------------------------------------------------------
/webpack_configs/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const merge = require('webpack-merge')
4 | const str = JSON.stringify
5 | const commonConfig = require('./common.config.js')
6 |
7 | const stylesheet = require('./stylesheet.config')
8 | const uglify = require('./uglify.config')
9 |
10 | module.exports = merge(
11 | commonConfig({
12 | staticDir: process.env.RUN_MODE ? 'rs2' : 'rs',
13 | }),
14 | stylesheet(),
15 | uglify(),
16 | {
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | __DEV__: false,
20 | __RUN_MODE__: str(process.env.RUN_MODE || ''),
21 | __BACKEND_URL__: str(process.env.BACKEND_URL || ''),
22 | __WS_URL__: str(process.env.WS_URL || ''),
23 | __STATIC_SERVING_URL__: str(process.env.STATIC_SERVING_URL || ''),
24 | __PACKAGE_DEV__: false,
25 | __PACKAGE_SERVER__: str(process.env.PACKAGE_SERVER || process.env.HTML_BASE_URL || ''),
26 | __NODE_ENV__: str(process.env.NODE_ENV || ''),
27 | }),
28 | ]
29 | }
30 | )
31 |
--------------------------------------------------------------------------------
/webpack_configs/webpack.staging.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const merge = require('webpack-merge')
4 |
5 | const str = JSON.stringify
6 | const commonConfig = require('./common.config.js')
7 | const stylesheet = require('./stylesheet.config')
8 | // const uglify = require('./uglify.config')
9 |
10 | module.exports = merge(
11 | commonConfig({
12 | staticDir: process.env.RUN_MODE ? 'rs2' : 'rs',
13 | }),
14 | stylesheet(),
15 | { devtool: 'cheap-module-eval-source-map' },
16 | {
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | __DEV__: false,
20 | __RUN_MODE__: str(process.env.RUN_MODE || ''),
21 | __BACKEND_URL__: str(process.env.BACKEND_URL || ''),
22 | __WS_URL__: str(process.env.WS_URL || ''),
23 | __STATIC_SERVING_URL__: str(process.env.STATIC_SERVING_URL || ''),
24 | __PACKAGE_DEV__: false,
25 | __PACKAGE_SERVER__: str(process.env.PACKAGE_SERVER || process.env.HTML_BASE_URL || ''),
26 | __NODE_ENV__: str(process.env.NODE_ENV || ''),
27 | }),
28 | ]
29 | }
30 | )
31 |
--------------------------------------------------------------------------------