├── .babelrc ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── fonts ├── Droid_Sans_Mono │ ├── DroidSansMono.ttf │ └── LICENSE.txt ├── FiraCode │ ├── FiraCode-Regular.otf │ └── LICENSE └── Josefin_Sans │ ├── JosefinSans-Bold.ttf │ ├── JosefinSans-BoldItalic.ttf │ ├── JosefinSans-Italic.ttf │ ├── JosefinSans-Light.ttf │ ├── JosefinSans-LightItalic.ttf │ ├── JosefinSans-Regular.ttf │ ├── JosefinSans-SemiBold.ttf │ ├── JosefinSans-SemiBoldItalic.ttf │ ├── JosefinSans-Thin.ttf │ ├── JosefinSans-ThinItalic.ttf │ └── OFL.txt ├── gulpfile.babel.js ├── icons ├── mancy.icns ├── mancy.ico └── mancy.png ├── images ├── auto-suggestion-2.png ├── auto-suggestion.png ├── await-progress.png ├── await-resolved.png ├── babel.png ├── base64-viz.png ├── buffer-viz.png ├── chart-area-spline.png ├── chart-area.png ├── chart-bar.png ├── chart-flip.png ├── chart-line.png ├── chart-pie.png ├── chart-rotate.png ├── chart-spline.png ├── color-viz.png ├── console-dups.png ├── console-window-2.png ├── console-window.png ├── dark-theme-2.png ├── dark-theme.png ├── date-visualization.png ├── font-preference.png ├── global-env-dark.png ├── global-env-light.png ├── grid-dark.png ├── grid-light.png ├── grid-transpose.png ├── html-viz.png ├── integer-viz.png ├── light-theme-2.png ├── light-theme.png ├── live-highlight.png ├── node-modules-preference.png ├── object-output.png ├── preference-dark.png ├── preference-light.png ├── promise-pending.png ├── promise-resolved.png ├── regex-viz.png ├── source-2.png ├── source-track.png ├── source.png ├── store-as-global-after.png ├── store-as-global-before.png ├── timeout-in-progress.png ├── timeout.png ├── transpiled-view.png ├── underscore.png ├── warn-quit.png └── zoom-preference.png ├── index.html ├── logos ├── cljs.png ├── coffee.png ├── js.png ├── ls.png └── ts.png ├── menus ├── context-menu.json ├── darwin.json ├── linux.json └── win32.json ├── package.json ├── src ├── Startup.js ├── actions │ ├── ReplActions.js │ ├── ReplActiveInputActions.js │ ├── ReplConsoleActions.js │ ├── ReplPreferencesActions.js │ ├── ReplStatusBarActions.js │ └── ReplSuggestionActions.js ├── app.js ├── common │ ├── ReplCommon.js │ ├── ReplConsoleHook.js │ ├── ReplContext.js │ ├── ReplDOM.js │ ├── ReplDOMEvents.js │ ├── ReplFonts.js │ ├── ReplInput.js │ ├── ReplOutput.js │ ├── ReplStreamHook.js │ ├── ReplType.js │ └── ReplUndo.js ├── components │ ├── Repl.js │ ├── ReplActiveIcon.js │ ├── ReplActiveInput.js │ ├── ReplConsole.js │ ├── ReplConsoleEnvironmentWatcher.js │ ├── ReplConsoleMessageFilters.js │ ├── ReplEntries.js │ ├── ReplEntry.js │ ├── ReplEntryIcon.js │ ├── ReplEntryMessage.js │ ├── ReplEntryOutputError.js │ ├── ReplEntryStatus.js │ ├── ReplFontFamily.js │ ├── ReplNotebook.js │ ├── ReplOutputArray.js │ ├── ReplOutputBuffer.js │ ├── ReplOutputBufferExplorer.js │ ├── ReplOutputChartViewer.js │ ├── ReplOutputColor.js │ ├── ReplOutputCrypto.js │ ├── ReplOutputDate.js │ ├── ReplOutputFunction.js │ ├── ReplOutputGridViewer.js │ ├── ReplOutputHTML.js │ ├── ReplOutputInteger.js │ ├── ReplOutputObject.js │ ├── ReplOutputPromise.js │ ├── ReplOutputRegex.js │ ├── ReplOutputString.js │ ├── ReplOutputTranspile.js │ ├── ReplOutputURL.js │ ├── ReplPageZoom.js │ ├── ReplPreferences.js │ ├── ReplPrompt.js │ ├── ReplSourceFile.js │ ├── ReplStatusBar.js │ ├── ReplSuggestions.js │ ├── __mocks__ │ │ ├── clipboard.js │ │ └── shell.js │ ├── __tests__ │ │ ├── ReplActiveIcon-test.js │ │ ├── ReplConsole-test.js │ │ ├── ReplConsoleMessageFilters-test.js │ │ ├── ReplEntries-test.js │ │ ├── ReplEntry-test.js │ │ ├── ReplEntryIcon-test.js │ │ ├── ReplEntryMessage-test.js │ │ ├── ReplEntryOutputError-test.js │ │ ├── ReplEntryStatus-test.js │ │ ├── ReplOutputArray-test.js │ │ ├── ReplOutputFunction-test.js │ │ ├── ReplOutputObject-test.js │ │ ├── ReplPrompt-test.js │ │ └── ReplStatusBar-test.js │ └── clojurescript │ │ ├── ReplOutputCljsDoc.js │ │ ├── ReplOutputCljsDocs.js │ │ ├── ReplOutputCljsFun.js │ │ ├── ReplOutputCljsMeta.js │ │ ├── ReplOutputCljsSeq.js │ │ ├── ReplOutputCljsSource.js │ │ ├── ReplOutputCljsVal.js │ │ ├── ReplOutputCljsVar.js │ │ └── ReplOutputCljsWrapper.js ├── constants │ └── ReplConstants.js ├── languages │ ├── ReplClojureScript.js │ ├── ReplLangWrapper.js │ ├── ReplLanguages.js │ ├── ReplLiveScript.js │ ├── ReplTypeScript.js │ └── typescript │ │ └── node.d.ts ├── main │ ├── MenuManager.js │ └── index.js └── stores │ ├── ReplActiveInputStore.js │ ├── ReplConsoleStore.js │ ├── ReplPreferencesStore.js │ ├── ReplStatusBarStore.js │ ├── ReplStore.js │ └── ReplSuggestionStore.js └── stylesheets ├── clojurescript ├── repl-output-cljs-docs.scss ├── repl-output-cljs-meta.scss ├── repl-output-cljs-source.scss ├── repl-output-cljs-val.scss ├── repl-output-cljs-var.scss └── repl-output-cljs-wrapper.scss ├── configs.scss ├── dark-theme.scss ├── highlight.scss ├── light-theme.scss ├── repl-common.scss ├── repl-console-environment-watcher.scss ├── repl-console-message.scss ├── repl-container-left.scss ├── repl-container-right.scss ├── repl-editor.scss ├── repl-entries.scss ├── repl-entry-message.scss ├── repl-entry-status.scss ├── repl-output-array.scss ├── repl-output-buffer-explorer.scss ├── repl-output-chart-viewer.scss ├── repl-output-color.scss ├── repl-output-crypto.scss ├── repl-output-grid-viewer.scss ├── repl-output-html.scss ├── repl-output-integer.scss ├── repl-output-object.scss ├── repl-output-regex.scss ├── repl-output-string.scss ├── repl-output-url.scss ├── repl-output.scss ├── repl-preferences.scss ├── repl-prompt.scss ├── repl-source-file.scss ├── repl-status-bar.scss ├── repl-suggestions.scss ├── repl.scss └── themes.scss /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-0"], 3 | "plugins": ["add-module-exports", [ 4 | "transform-runtime", { 5 | "polyfill": false, 6 | "regenerator": true 7 | }] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | dist 4 | tmp 5 | devtools 6 | coverage 7 | build 8 | .DS_Store 9 | *.log 10 | .* 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | - "4.0" 5 | - "0.12" 6 | - "0.11" 7 | - "0.10" 8 | - "iojs" 9 | #before_script: 10 | # - npm install -g gulp 11 | #script: gulp 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Prince John Wesley (princejohnwesley@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #  Mancy 2 | 3 | A cross platform NodeJS REPL application based on electron and react frameworks. 4 | 5 | [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/princejwesley/Mancy) 6 | [![Subscribe](https://img.shields.io/badge/subscribe-%F0%9F%94%8A-f5f5f5.svg?style=flat&maxAge=2592000)](https://github.com/princejwesley/Mancy/issues/126) 7 | 8 | ##  [Language Support](http://mancy-re.pl) 9 | 10 | - [JavaScript](https://en.wikipedia.org/wiki/JavaScript) 11 | - [CoffeeScript](http://coffeescript.org/) 12 | - [TypeScript](http://www.typescriptlang.org/) 13 | - [LiveScript](http://livescript.net/) 14 | - [ClojureScript](http://clojure.org/about/clojurescript) 15 | 16 | ##  [Features](http://mancy-re.pl) 17 | 18 | - Syntax Highlighting 19 | - Dark and light themes 20 | - Load and save session history 21 | - Separate console window for async stdout/stderr logs 22 | - Notification for async console logs 23 | - console output filter support 24 | - Traversable output with fold/unfold options 25 | - Support for adding directory to node path 26 | - Expand/Collapse/reload command options 27 | - History traversal support 28 | - Multiple window 29 | - Multiline prompt support with shift + enter 30 | - Auto suggestion 31 | - Tab completion 32 | - Code format support 33 | - Support to toggle REPL mode 34 | - Preferences for theme and REPL mode 35 | 36 | ####  [Version II :star2:](https://github.com/princejwesley/Mancy/wiki/Version-II) 37 | 38 | - Download npm modules on demand 39 | - Babel support 40 | - await with auto async wrapper 41 | - Data visualization support 42 | - Integer representation (bin/oct/dec/hex and signed/unsigned) 43 | - Regular expression live editor 44 | - Buffer explorer 45 | - HTML view 46 | - CSS color view 47 | - base64 detection 48 | - Basic chart representation of data 49 | - Image detection / display 50 | - Download buffers support 51 | - Support to break long lasting commands 52 | - Preference window 53 | - Promise output tracking 54 | - Source file open support for node modules(`.source name`) 55 | - No special meaning for `_` 56 | - Syntax highlight as we type 57 | 58 | For more detailed documentation, [read here](https://github.com/princejwesley/Mancy/wiki/Version-II) 59 | 60 | ##  [Wiki](http://github.com/princejwesley/Mancy/wiki) 61 | [wiki](http://github.com/princejwesley/Mancy/wiki) page has documentation for new features. 62 | 63 | ##  [Screenshots](http://mancy-re.pl) 64 | 65 | #### light theme 66 | 67 | 68 | #### dark theme 69 | 70 | 71 | #### console section 72 | 73 | 74 | #### auto suggestion 75 | 76 | 77 | #### function source 78 | 79 | 80 | 81 | #  Installation 82 | - `npm install -g mancy` (or) 83 | - Download from [latest release](https://github.com/princejwesley/Mancy/releases/latest) (or) 84 | - [Clone](https://github.com/princejwesley/Mancy/) and run `npm install` & `npm run package`. Executable file will be created inside `./dist/` directory. 85 | - To build native modules, make sure `node` >=4.x and `npm` >= 3.x installed. 86 | - Refer [node-gyp](https://github.com/nodejs/node-gyp#installation) for native module build failure. 87 | 88 | ##  Developers 89 | 90 | [Fork](https://github.com/princejwesley/Mancy/) and run `npm run debug` or `gulp debug` to debug this application. 91 | 92 | ##  Notification 93 | Subscribe [this thread](https://github.com/princejwesley/Mancy/issues/126) for new features and release notification. 94 | 95 | ##  Ideas & Suggestions 96 | Find consolidated ideas and suggestions [here](https://github.com/princejwesley/Mancy/wiki/Ideas-&-Suggestions). 97 | 98 | ##  License 99 | [MIT License](https://github.com/princejwesley/Mancy/blob/master/LICENSE) 100 | -------------------------------------------------------------------------------- /fonts/Droid_Sans_Mono/DroidSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Droid_Sans_Mono/DroidSansMono.ttf -------------------------------------------------------------------------------- /fonts/FiraCode/FiraCode-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/FiraCode/FiraCode-Regular.otf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-Bold.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-Italic.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-Light.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-Regular.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-SemiBold.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-Thin.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/JosefinSans-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/fonts/Josefin_Sans/JosefinSans-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/Josefin_Sans/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Santiago Orozco (hi@typemade.mx) 2 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 3 | This license is copied below, and is also available with a FAQ at: 4 | http://scripts.sil.org/OFL 5 | 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font creation 14 | efforts of academic and linguistic communities, and to provide a free and 15 | open framework in which fonts may be shared and improved in partnership 16 | with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply 25 | to any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software components as 36 | distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, deleting, 39 | or substituting -- in part or in whole -- any of the components of the 40 | Original Version, by changing formats or by porting the Font Software to a 41 | new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 49 | redistribute, and sell modified and unmodified copies of the Font 50 | Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, 53 | in Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the corresponding 64 | Copyright Holder. This restriction only applies to the primary font name as 65 | presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created 77 | using the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /icons/mancy.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/icons/mancy.icns -------------------------------------------------------------------------------- /icons/mancy.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/icons/mancy.ico -------------------------------------------------------------------------------- /icons/mancy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/icons/mancy.png -------------------------------------------------------------------------------- /images/auto-suggestion-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/auto-suggestion-2.png -------------------------------------------------------------------------------- /images/auto-suggestion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/auto-suggestion.png -------------------------------------------------------------------------------- /images/await-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/await-progress.png -------------------------------------------------------------------------------- /images/await-resolved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/await-resolved.png -------------------------------------------------------------------------------- /images/babel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/babel.png -------------------------------------------------------------------------------- /images/base64-viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/base64-viz.png -------------------------------------------------------------------------------- /images/buffer-viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/buffer-viz.png -------------------------------------------------------------------------------- /images/chart-area-spline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-area-spline.png -------------------------------------------------------------------------------- /images/chart-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-area.png -------------------------------------------------------------------------------- /images/chart-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-bar.png -------------------------------------------------------------------------------- /images/chart-flip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-flip.png -------------------------------------------------------------------------------- /images/chart-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-line.png -------------------------------------------------------------------------------- /images/chart-pie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-pie.png -------------------------------------------------------------------------------- /images/chart-rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-rotate.png -------------------------------------------------------------------------------- /images/chart-spline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/chart-spline.png -------------------------------------------------------------------------------- /images/color-viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/color-viz.png -------------------------------------------------------------------------------- /images/console-dups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/console-dups.png -------------------------------------------------------------------------------- /images/console-window-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/console-window-2.png -------------------------------------------------------------------------------- /images/console-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/console-window.png -------------------------------------------------------------------------------- /images/dark-theme-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/dark-theme-2.png -------------------------------------------------------------------------------- /images/dark-theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/dark-theme.png -------------------------------------------------------------------------------- /images/date-visualization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/date-visualization.png -------------------------------------------------------------------------------- /images/font-preference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/font-preference.png -------------------------------------------------------------------------------- /images/global-env-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/global-env-dark.png -------------------------------------------------------------------------------- /images/global-env-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/global-env-light.png -------------------------------------------------------------------------------- /images/grid-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/grid-dark.png -------------------------------------------------------------------------------- /images/grid-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/grid-light.png -------------------------------------------------------------------------------- /images/grid-transpose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/grid-transpose.png -------------------------------------------------------------------------------- /images/html-viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/html-viz.png -------------------------------------------------------------------------------- /images/integer-viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/integer-viz.png -------------------------------------------------------------------------------- /images/light-theme-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/light-theme-2.png -------------------------------------------------------------------------------- /images/light-theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/light-theme.png -------------------------------------------------------------------------------- /images/live-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/live-highlight.png -------------------------------------------------------------------------------- /images/node-modules-preference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/node-modules-preference.png -------------------------------------------------------------------------------- /images/object-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/object-output.png -------------------------------------------------------------------------------- /images/preference-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/preference-dark.png -------------------------------------------------------------------------------- /images/preference-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/preference-light.png -------------------------------------------------------------------------------- /images/promise-pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/promise-pending.png -------------------------------------------------------------------------------- /images/promise-resolved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/promise-resolved.png -------------------------------------------------------------------------------- /images/regex-viz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/regex-viz.png -------------------------------------------------------------------------------- /images/source-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/source-2.png -------------------------------------------------------------------------------- /images/source-track.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/source-track.png -------------------------------------------------------------------------------- /images/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/source.png -------------------------------------------------------------------------------- /images/store-as-global-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/store-as-global-after.png -------------------------------------------------------------------------------- /images/store-as-global-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/store-as-global-before.png -------------------------------------------------------------------------------- /images/timeout-in-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/timeout-in-progress.png -------------------------------------------------------------------------------- /images/timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/timeout.png -------------------------------------------------------------------------------- /images/transpiled-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/transpiled-view.png -------------------------------------------------------------------------------- /images/underscore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/underscore.png -------------------------------------------------------------------------------- /images/warn-quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/warn-quit.png -------------------------------------------------------------------------------- /images/zoom-preference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/images/zoom-preference.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /logos/cljs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/logos/cljs.png -------------------------------------------------------------------------------- /logos/coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/logos/coffee.png -------------------------------------------------------------------------------- /logos/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/logos/js.png -------------------------------------------------------------------------------- /logos/ls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/logos/ls.png -------------------------------------------------------------------------------- /logos/ts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/princejwesley/Mancy/ff61e6e1603b5c94658084024a47cd4505ba6373/logos/ts.png -------------------------------------------------------------------------------- /menus/context-menu.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "Cut", 4 | "accelerator": "CmdOrCtrl+X", 5 | "role": "cut" 6 | }, 7 | { 8 | "label": "Copy", 9 | "accelerator": "CmdOrCtrl+C", 10 | "role": "copy" 11 | }, 12 | { 13 | "label": "Paste", 14 | "accelerator": "CmdOrCtrl+V", 15 | "role": "paste" 16 | }, 17 | { 18 | "label": "Select All", 19 | "accelerator": "CmdOrCtrl+A", 20 | "role": "selectall" 21 | }, 22 | { 23 | "type": "separator" 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mancy", 3 | "private": true, 4 | "version": "3.2.0", 5 | "description": "A GUI REPL for Javascript & more…", 6 | "main": "main/index.js", 7 | "scripts": { 8 | "preinstall": "npm prune", 9 | "postinstall": "electron-rebuild && gulp clean && gulp copy", 10 | "test": "jest", 11 | "cov": "jest --coverage", 12 | "start": "gulp start", 13 | "debug": "gulp debug", 14 | "package": "npm dedupe && gulp package", 15 | "package-all": "npm dedupe && gulp packageAll", 16 | "release": "gulp release", 17 | "watch": "gulp watch", 18 | "build": "gulp build" 19 | }, 20 | "jest": { 21 | "scriptPreprocessor": "../node_modules/babel-jest", 22 | "unmockedModulePathPatterns": [ 23 | "./node_modules/react", 24 | "./node_modules/reflux", 25 | "./node_modules/lodash" 26 | ], 27 | "verbose": true, 28 | "rootDir": "./src" 29 | }, 30 | "keywords": [ 31 | "REPL", 32 | "JavaScript", 33 | "CoffeeScript", 34 | "TypeScript", 35 | "LiveScript", 36 | "ClojureScript" 37 | ], 38 | "author": "Prince John Wesley ", 39 | "license": "MIT", 40 | "devDependencies": { 41 | "babel": "^6.3.13", 42 | "babel-jest": "^6.0.1", 43 | "babel-polyfill": "^6.3.14", 44 | "del": "^2.0.2", 45 | "devtron": "^1.0.0", 46 | "electron-packager": "^8.0.0", 47 | "electron": "^1.4.15", 48 | "electron-rebuild": "^1.5.7", 49 | "gulp": "^3.9.0", 50 | "gulp-babel": "^6.1.1", 51 | "gulp-cached": "^1.1.0", 52 | "gulp-cli": "^0.3.0", 53 | "gulp-concat": "^2.6.0", 54 | "gulp-env": "^0.2.0", 55 | "gulp-livereload": "^3.8.0", 56 | "gulp-load-plugins": "^0.10.0", 57 | "gulp-react": "^3.0.1", 58 | "gulp-sass": "^2.0.4", 59 | "gulp-util": "^3.0.6", 60 | "jest-cli": "^0.5.8", 61 | "node-sass": "^3.3.2", 62 | "react-tools": "0.13.3", 63 | "run-sequence": "^1.1.2", 64 | "semver": "^5.0.3" 65 | }, 66 | "dependencies": { 67 | "babel-core": "^6.7.0", 68 | "babel-runtime": "^6.9.1", 69 | "babel-plugin-add-module-exports": "^0.1.1", 70 | "babel-plugin-transform-runtime": "^6.3.13", 71 | "babel-preset-es2015": "^6.3.13", 72 | "babel-preset-react": "^6.3.13", 73 | "babel-preset-stage-0": "^6.3.13", 74 | "c3": "https://github.com/princejwesley/c3.git", 75 | "cljs-mancy": "^1.0.0", 76 | "codemirror": "^5.13.0", 77 | "coffee-script": "https://github.com/princejwesley/coffeescript.git", 78 | "font-awesome": "^4.5.0", 79 | "font-manager": "^0.2.2", 80 | "github": "^0.2.4", 81 | "is-css-color": "^1.0.0", 82 | "livescript": "^1.4.0", 83 | "lodash": "^3.10.1", 84 | "md5": "^2.0.0", 85 | "parinfer": "^1.7.0", 86 | "react": "^0.13.3", 87 | "reflux": "^0.2.12", 88 | "typescript": "^1.8.2", 89 | "yargs": "^3.32.0" 90 | }, 91 | "settings": { 92 | "disable-font-manager": true 93 | }, 94 | "repository": { 95 | "type": "git", 96 | "url": "https://github.com/princejwesley/Mancy.git" 97 | }, 98 | "bugs": { 99 | "url": "https://github.com/princejwesley/Mancy/issues/new" 100 | }, 101 | "homepage": "https://github.com/princejwesley/Mancy" 102 | } 103 | -------------------------------------------------------------------------------- /src/Startup.js: -------------------------------------------------------------------------------- 1 | 2 | require('./app'); 3 | -------------------------------------------------------------------------------- /src/actions/ReplActions.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | 3 | const ReplActions = Reflux.createActions([ 4 | "addEntry", 5 | "updateEntry", 6 | "removeEntry", 7 | "reloadPrompt", 8 | "reloadPromptByIndex", 9 | "toggleCommandEntryView", 10 | "toggleEntryView", 11 | "setREPLMode", 12 | "setEditorMode", 13 | "overrideLastOutput", 14 | "bindObjectToContext", 15 | ]); 16 | export default ReplActions; 17 | -------------------------------------------------------------------------------- /src/actions/ReplActiveInputActions.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | 3 | const ReplActiveInputActions = Reflux.createActions([ 4 | "tabCompleteSuggestion", 5 | "resetTabCompleteSuggestion", 6 | "fillTabCompleteSuggestion", 7 | "breakPrompt", 8 | "formatCode", 9 | "playCommands", 10 | "updateSuggestionDelay", 11 | "performAutoComplete", 12 | "setTheme", 13 | "setEditorOption", 14 | "undo", 15 | "redo", 16 | "selectAll", 17 | "focus", 18 | "setMode" 19 | ]); 20 | export default ReplActiveInputActions; 21 | -------------------------------------------------------------------------------- /src/actions/ReplConsoleActions.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | 3 | const ReplConsoleActions = Reflux.createActions([ 4 | "addEntry", 5 | "clear" 6 | ]); 7 | export default ReplConsoleActions; 8 | -------------------------------------------------------------------------------- /src/actions/ReplPreferencesActions.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | 3 | const ReplPreferencesActions = Reflux.createActions([ 4 | "togglePreferences", 5 | "openPreferences", 6 | "closePreferences", 7 | "setTheme", 8 | "setREPLMode", 9 | "changeFontFamily", 10 | "changePageZoomFactor", 11 | ]); 12 | export default ReplPreferencesActions; 13 | -------------------------------------------------------------------------------- /src/actions/ReplStatusBarActions.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | 3 | const ReplStatusBarActions = Reflux.createActions([ 4 | "updateRunCommand", 5 | "newRelease", 6 | "updateLanguage", 7 | "updateMode", 8 | "refresh", 9 | "cursorActivity" 10 | ]); 11 | export default ReplStatusBarActions; 12 | -------------------------------------------------------------------------------- /src/actions/ReplSuggestionActions.js: -------------------------------------------------------------------------------- 1 | import Reflux from 'reflux'; 2 | 3 | const ReplSuggestionActions = Reflux.createActions([ 4 | "addSuggestion", 5 | "removeSuggestion" 6 | ]); 7 | export default ReplSuggestionActions; 8 | -------------------------------------------------------------------------------- /src/common/ReplConsoleHook.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import EventEmitter from 'events'; 3 | 4 | let console = require('console'); 5 | class ReplConsoleHook extends EventEmitter { 6 | constructor() { 7 | super(); 8 | 9 | _.each(['error', 'warn', 'info', 'log', 'debug'], (fun) => { 10 | this[fun] = (...rest) => { 11 | this.emit('console', {type: fun, data: rest}); 12 | }; 13 | console[fun] = this[fun]; 14 | }); 15 | } 16 | } 17 | let hook = new ReplConsoleHook(); 18 | export default hook; 19 | -------------------------------------------------------------------------------- /src/common/ReplDOMEvents.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import ReplConstants from '../constants/ReplConstants'; 3 | 4 | const navigation = { 5 | 'keyLeft': 37, 6 | 'keyup': 38, 7 | 'keyRight': 39, 8 | 'keydown': 40, 9 | }; 10 | 11 | const events = { 12 | 'blurEvent': 'blur', 13 | 'keydownEvent': 'keydown', 14 | 'keyupEvent': 'keyup', 15 | }; 16 | 17 | const keyNameBindings = { 18 | 'tab': 9, 19 | 'enter': 13, 20 | 'escape': 27, 21 | 'space': 32, 22 | 'quote': 222, 23 | 'backTick': 192, 24 | 'backSpace': 8, 25 | }; 26 | 27 | let combined = _.extend({}, keyNameBindings, navigation); 28 | 29 | let reducer = (input, attr) => { 30 | return _.chain(input) 31 | .keys() 32 | .map((key) => [key, (e) => e[attr] === input[key]]) 33 | .reduce((result, [key, fun]) => { 34 | result['is' + _.capitalize(key)] = fun; 35 | return result; 36 | }, {}) 37 | .value(); 38 | }; 39 | 40 | 41 | let ReplDOMEvents = _.extend({}, reducer(combined, 'which'), reducer(events, 'type')); 42 | 43 | ReplDOMEvents.isNavigation = (() => { 44 | let values = _.values(navigation); 45 | return (e) => values.indexOf(e.which) !== -1; 46 | })(); 47 | 48 | ReplDOMEvents.autoFillPairCharacters = { 49 | '"' : '"', 50 | "'" : "'", 51 | '`' : '`', 52 | '{' : '}', 53 | '[' : ']', 54 | '(' : ')', 55 | '}' : '{', 56 | ']' : '[', 57 | ')' : '(' 58 | }; 59 | 60 | ReplDOMEvents.autoCloseKeyIdentifiers = { 61 | '"' : "U+0022", 62 | "'" : "U+0027", 63 | "[" : "U+005B", 64 | "]" : "U+005D", 65 | "{" : "U+007B", 66 | "}" : "U+007D", 67 | "(" : "U+0028", 68 | ")" : "U+0029", 69 | "`" : "U+0060", 70 | }; 71 | 72 | ReplDOMEvents.duplicate = (e) => new e.constructor(e.type, e); 73 | 74 | // keyCodes for alphabets A-Z 75 | const A = 'A'.charCodeAt(0); 76 | _.each('ABCDEFGHIJKLMNOPQRSTUVWXYZ', (c, pos) => { 77 | ReplDOMEvents[c] = A + pos; 78 | }); 79 | 80 | ReplDOMEvents.zero = '0'.charCodeAt(0); //48 81 | ReplDOMEvents.nine = ReplDOMEvents.zero + 9; //'9'.charCodeAt(0); 82 | ReplDOMEvents.isNumber = (e) => e.which >= ReplDOMEvents.zero && e.which <= ReplDOMEvents.nine; 83 | 84 | export default ReplDOMEvents; 85 | -------------------------------------------------------------------------------- /src/common/ReplFonts.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | const weights = { 4 | 100: 'Thin', 5 | 200: 'Ultra Light', 6 | 300: 'Light', 7 | 400: 'Normal', 8 | 500: 'Medium', 9 | 600: 'Semi Bold', 10 | 700: 'Bold', 11 | 800: 'Ultra Bold', 12 | 900: 'Heavy', 13 | }; 14 | 15 | const widths = { 16 | 1: 'Ultra Condensed', 17 | 2: 'Extra Condensed', 18 | 3: 'Condensed', 19 | 4: 'Semi Condensed', 20 | 5: 'Normal', 21 | 6: 'Semi Expanded', 22 | 7: 'Expanded', 23 | 8: 'Extra Expanded', 24 | 9: 'Ultra Expanded', 25 | }; 26 | 27 | const systemFonts = (() => { 28 | try { 29 | const settings = require('./../package.json').settings; 30 | const locals = [ 31 | { family: 'Droid Sans Mono' }, 32 | { family: 'FiraCode' }, 33 | { family: 'Josefin Sans' } 34 | ]; 35 | return _.chain(settings['disable-font-manager'] ? locals : require('font-manager').getAvailableFontsSync()) 36 | .tap((fonts) => { 37 | locals.forEach(f => fonts.push(f)); 38 | }) 39 | .sortBy("family") 40 | .map((f) => f.family) 41 | .uniq(true) 42 | .value(); 43 | } catch(e) { 44 | // disable font preferences 45 | return []; 46 | } 47 | })(); 48 | 49 | const getSystemFonts = () => systemFonts; 50 | const setFontFamily = (family = 'monospace', defaults = 'sans-serif') => document.body.style.fontFamily = `${family}, ${defaults}`; 51 | 52 | export default { getSystemFonts, setFontFamily }; 53 | -------------------------------------------------------------------------------- /src/common/ReplInput.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import ReplConstants from '../constants/ReplConstants'; 3 | import ReplCommon from './ReplCommon'; 4 | import ReplOutput from '../common/ReplOutput'; 5 | let babel = require('babel-core'); 6 | 7 | const functionMatcher = /^\s*\bfunction\s+(..*?)\(/; 8 | const awaitMatcher = /^(?:\s*(?:(?:let|var|const)\s)?\s*([^=]+)=\s*|^\s*)(await\s[\s\S]*)/; 9 | const sourceMatcher = /^\s*(\.source)\s+([^\s]+)\s*$/; 10 | const importMatcher = /\bimport\s+(?:(?:{(.+?)})|(.+?))\s+from\s+(['"])(.+?)\3/g; 11 | const bindAsMatcher = /(.*)\s+as\s+(.*)/; 12 | const asDefaultMatcher = /(?:\*|default)\s+as/; 13 | const USE_STRICT_LENGTH = "'user strict;'".length; 14 | 15 | 16 | let funTransformer = (source, find, replace) => 17 | `var ${replace} = function ${replace}(${source.substring(find.length)}`; 18 | let asyncWrapper = (code, binder) => { 19 | // Babel is not happy, ensure that async is not top level 20 | // let assign = binder ? `root.${binder} = result;` : ''; 21 | // return `(async function() { let result = (${code}); ${assign} return result; }())`; 22 | let assign = binder ? `root.${binder} = ` : ''; 23 | return `(function(){ async function _wrap() { return ${assign}${code} } return _wrap();})()`; 24 | }; 25 | 26 | let importToRequire = (prefix, bindings, asBinding, __, modname) => { 27 | if(asBinding) { 28 | return `var ${asBinding.replace(asDefaultMatcher, '')} = (require('${modname}').default || require('${modname}'));`; 29 | } 30 | let result = Array.join((bindings).trim() 31 | .split(',') 32 | .map(m => { 33 | const asM = m.trim(); 34 | const asBound = asM.match(bindAsMatcher); 35 | const [x, y] = asBound ? [asBound[1], asBound[2]] : [asM, asM]; 36 | const attr = x === 'default' || x === '*' ? '' : `.${x}`; 37 | return `root.${y} = (require('${modname}').default || require('${modname}'))${attr};\n` 38 | }), ''); 39 | return `${result}; void 0;`; 40 | }; 41 | 42 | 43 | let cook = (plain) => { 44 | let tplain = plain.trim(); 45 | let source = tplain.match(sourceMatcher); 46 | if(source) { 47 | let mod = source[2]; 48 | return { 49 | local: true, 50 | output: ReplOutput.source(mod), 51 | input: `.source ${mod}` 52 | } 53 | } 54 | 55 | let output = plain, force = false; 56 | 57 | if(global.Mancy.session.lang === 'js') { 58 | let funMatch = output.match(functionMatcher); 59 | if(funMatch) { 60 | output = `${funTransformer(output, funMatch[0], funMatch[1])}`; 61 | } 62 | if(global.Mancy.preferences.asyncWrap) { 63 | // bare await 64 | let match = output.match(awaitMatcher); 65 | if(match) { 66 | output = `${asyncWrapper(match[2], match[1])}`; 67 | force = true; 68 | } 69 | } 70 | if(!force && plain.indexOf('import') !== -1) { 71 | output = plain.replace(importMatcher, importToRequire); 72 | } 73 | } 74 | 75 | return { 76 | force, 77 | local: false, 78 | output: global.Mancy.session.babel && global.Mancy.session.lang === 'js' ? babelTransfrom(output) : output 79 | }; 80 | }; 81 | 82 | let babelTransfrom = (plain) => { 83 | try { 84 | let matchCommonJS = (opt) => opt === 'transform-es2015-modules-commonjs'; 85 | let strict = false; 86 | 87 | if(global.Mancy.session.mode === 'Strict') { 88 | strict = true; 89 | if(_.findIndex(ReplConstants.BABEL_OPTIONS.plugins, matchCommonJS) === -1) { 90 | ReplConstants.BABEL_OPTIONS.plugins.push('transform-es2015-modules-commonjs'); 91 | } 92 | } else { _.remove(ReplConstants.BABEL_OPTIONS.plugins, matchCommonJS); } 93 | 94 | let code = babel 95 | .transform(plain, ReplConstants.BABEL_OPTIONS) 96 | .code; 97 | 98 | // transform imports added by babel scripts into require 99 | if(!strict && code.indexOf('import') !== -1) { 100 | code = code.replace(importMatcher, importToRequire); 101 | } 102 | 103 | return strict ? code.substring(USE_STRICT_LENGTH - 1) : code; 104 | } catch(e) { 105 | return e; 106 | } 107 | } 108 | 109 | let ReplInput = { 110 | transform: (plain) => { 111 | return cook(plain); 112 | } 113 | }; 114 | 115 | export default ReplInput; 116 | -------------------------------------------------------------------------------- /src/common/ReplStreamHook.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import EventEmitter from 'events'; 3 | 4 | // only stdout and stderr 5 | class ReplStreamHook extends EventEmitter { 6 | constructor() { 7 | super(); 8 | _.each([['stdout', process.stdout], ['stderr', process.stderr]], ([name, stream]) => { 9 | stream.write = ((stream) => { 10 | return (chunk, encoding, fd) => { 11 | this.emit(name, { data: chunk, encoding: encoding, fd: fd }); 12 | }; 13 | })(stream); 14 | }); 15 | } 16 | } 17 | export default new ReplStreamHook(); 18 | -------------------------------------------------------------------------------- /src/common/ReplUndo.js: -------------------------------------------------------------------------------- 1 | 2 | class ReplUndoItem { 3 | constructor(ts, action, data) { 4 | this.ts = ts; 5 | this.action = action; 6 | this.data = data; 7 | } 8 | } 9 | 10 | // seconds pulse 11 | export default class ReplUndo { 12 | constructor(context=null, pulse = 1000) { 13 | this.stack = []; 14 | this.pos = -1; 15 | this.pulse = Math.abs(pulse); 16 | this.context = context; 17 | } 18 | 19 | add(data, action) { 20 | let nts = parseInt(Date.now() / this.pulse); 21 | let item = new ReplUndoItem(nts, action, data); 22 | if(this.pos > 0) { 23 | let {ts} = this.stack[this.pos]; 24 | if(ts !== nts) { 25 | this.pos++; 26 | this.stack.splice(this.pos); 27 | this.stack.push(item); 28 | } else { 29 | this.stack[this.pos] = item; 30 | } 31 | } else { 32 | this.pos = 0; 33 | this.stack.push(item); 34 | } 35 | } 36 | 37 | undo(data, action) { 38 | if(this.pos >= 0) { 39 | let {action, data} = this.stack[this.pos]; 40 | this.pos--; 41 | action.call(this.context, data, ReplUndo.Undo); 42 | } 43 | } 44 | 45 | redo(data, action) { 46 | const item = this.stack[this.pos + 1]; 47 | if(item) { 48 | let {action, data} = item; 49 | this.pos++; 50 | action.call(this.context, data, ReplUndo.Redo); 51 | } 52 | } 53 | 54 | reset() { 55 | this.stack = []; 56 | this.pos = -1; 57 | } 58 | 59 | static Undo = Symbol('undo'); 60 | static Redo = Symbol('redo'); 61 | } 62 | -------------------------------------------------------------------------------- /src/components/ReplActiveIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ReplActiveIcon extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | } 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/ReplConsoleEnvironmentWatcher.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplContext from '../common/ReplContext'; 3 | import _ from 'lodash'; 4 | import ReplOutput from '../common/ReplOutput'; 5 | 6 | export default class ReplConsoleEnvironmentWatcher extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.context = ReplContext.getContext(); 10 | this.getProperties = this.getProperties.bind(this); 11 | this.getENV = this.getENV.bind(this); 12 | this.toggleFunsView = this.toggleFunsView.bind(this); 13 | this.toggleValuesView = this.toggleValuesView.bind(this); 14 | this.state = { 15 | funView: true, 16 | valueView: true 17 | }; 18 | } 19 | 20 | getProperties(names) { 21 | let context = ReplContext.getContext(); 22 | return _.map(names, (key) => { 23 | let value = ReplOutput.readProperty(context, key); 24 | let keyClass = Object.prototype.propertyIsEnumerable.call(context, key) ? 'env-key' : 'env-key dull'; 25 | return ( 26 |
27 | { 28 | 29 | {key.toString()} 30 | 31 | } 32 | 33 | { 34 | value && value._isReactElement 35 | ? {value} 36 | : ReplOutput.transformObject(value) 37 | } 38 | 39 |
40 | ) 41 | }) 42 | } 43 | 44 | getENV() { 45 | let context = ReplContext.getContext(); 46 | let userDefinedNames = _.sortBy(_.difference(Object.getOwnPropertyNames(context), ReplContext.alphaNames)); 47 | return _.partition(userDefinedNames, (name) => typeof context[name] === 'function'); 48 | } 49 | 50 | toggleValuesView() { 51 | this.setState({ 52 | valueView: !this.state.valueView 53 | }); 54 | } 55 | 56 | toggleFunsView() { 57 | this.setState({ 58 | funView: !this.state.funView 59 | }); 60 | } 61 | 62 | render() { 63 | let [funs, vals] = this.getENV(); 64 | let valueClass = `repl-console-environment-listing-title fa ${this.state.valueView ? 'fa-minus-square-o': 'fa-plus-square-o'}`; 65 | let funClass = `repl-console-environment-listing-title fa ${this.state.funView ? 'fa-minus-square-o': 'fa-plus-square-o'}`; 66 | return ( 67 |
68 |
69 | 70 | Global Environment 71 | 72 |
73 |
74 |
75 | Values 76 | 77 |
78 | { 79 | this.state.valueView 80 | ? vals.length 81 | ?
82 | { 83 | this.getProperties(vals) 84 | } 85 |
86 | :
No values
87 | : null 88 | } 89 |
90 | Functions 91 | 92 |
93 | { 94 | this.state.funView 95 | ? funs.length 96 | ?
97 | { 98 | this.getProperties(funs) 99 | } 100 |
101 | :
No functions
102 | : null 103 | } 104 |
105 |
106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/components/ReplConsoleMessageFilters.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ReplConsoleMessageFilters extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | 7 | } 8 | 9 | render() { 10 | return ( 11 |
12 | 13 | 14 | 17 | 18 | A 19 | 20 | 21 | 25 | 26 | E 27 | 28 | 29 | 33 | 34 | W 35 | 36 | 37 | 41 | 42 | I 43 | 44 | 45 | 49 | 50 | L 51 | 52 | 53 | 57 | 58 | D 59 | 60 | 61 | 62 | 63 | 64 |
65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/components/ReplEntries.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplEntry from './ReplEntry'; 4 | 5 | export default class ReplEntries extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | render() { 10 | return ( 11 |
12 | { 13 | _.chain(this.props.entries) 14 | .filter(entry => entry.plainCode && entry.plainCode.trim().length) 15 | .map((entry, pos) => { 16 | return ; 17 | }) 18 | .value() 19 | } 20 |
21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/ReplEntry.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplEntryIcon from './ReplEntryIcon'; 3 | import ReplEntryMessage from './ReplEntryMessage'; 4 | import ReplEntryStatus from './ReplEntryStatus'; 5 | import ReplActions from '../actions/ReplActions'; 6 | import ReplNotebook from './ReplNotebook'; 7 | import _ from 'lodash'; 8 | 9 | export default class ReplEntry extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | _.each([ 13 | 'onToggle', 'onReload', 14 | 'onCommandCollapse', 'onRemove', 15 | ], (field) => { 16 | this[field] = this[field].bind(this); 17 | }); 18 | } 19 | onToggle() { 20 | ReplActions.toggleEntryView(this.props.index); 21 | } 22 | onReload() { 23 | ReplActions.reloadPromptByIndex(this.props.index); 24 | } 25 | onRemove() { 26 | ReplActions.removeEntry(this.props.index, this.props.log); 27 | } 28 | onCommandCollapse() { 29 | ReplActions.toggleCommandEntryView(this.props.index); 30 | } 31 | 32 | renderNotebook() { 33 | return ( 34 |
35 | 36 |
37 | ); 38 | } 39 | 40 | renderREPL() { 41 | return ( 42 |
43 | 45 | 47 | 51 |
52 | ); 53 | } 54 | 55 | render() { 56 | return global.Mancy.preferences.editor === 'REPL' ? this.renderREPL() : this.renderNotebook(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/ReplEntryIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ReplEntryIcon extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | } 7 | render() { 8 | return ( 9 |
10 | { 11 | this.props.collapse 12 | ? 13 | : 14 | } 15 |
16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/ReplEntryMessage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {EOL} from 'os'; 3 | import ReplCommon from '../common/ReplCommon'; 4 | import ReplConstants from '../constants/ReplConstants'; 5 | 6 | export default class ReplEntryMessage extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | getTimeStr(v, u) { 12 | return v ? `${v}${u}` : null; 13 | } 14 | 15 | showExecutionTime() { 16 | if(!this.props.message.time || !global.Mancy.preferences.executionTime) { 17 | return null; 18 | } 19 | 20 | let [s, n] = this.props.message.time; 21 | let m = parseInt(n / 1e6); 22 | let mi = parseInt((n - m * 1e6) / 1e3); 23 | n = n % 1e3; 24 | let time = [ 25 | this.getTimeStr(s, 's'), this.getTimeStr(m, 'ms'), 26 | this.getTimeStr(mi,'µs'), this.getTimeStr(n, 'ns') 27 | ].filter(x => x !== null).join(':'); 28 | 29 | let clazz = `fa fa-clock-o execution-time ${s > 10 ? 'red' : (s > 5 ? 'orange' : 'green')}` 30 | return 31 | } 32 | 33 | render() { 34 | let shortEntry; 35 | if(this.props.commandCollapse) { 36 | let lines = this.props.message.plainCode.trim().split(EOL); 37 | if(lines.length > 1 || lines[0].length > ReplConstants.COMMAND_TRUNCATE_LENGTH){ 38 | shortEntry = ReplCommon.highlight(lines[0].slice(0, ReplConstants.COMMAND_TRUNCATE_LENGTH)); 39 | } 40 | } 41 | return ( 42 |
43 |
44 | { 45 | this.props.commandCollapse && shortEntry 46 | ?
48 |
49 | :
51 |
52 | } 53 | { this.props.message.ns ? {this.props.message.ns} : null } 54 | { this.showExecutionTime() } 55 |
56 | { this.props.collapse ? 57 | null : 58 |
59 | {this.props.message.transpiledOutput} 60 | {this.props.message.formattedOutput} 61 |
62 | } 63 |
64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/ReplEntryOutputError.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplSourceFile from './ReplSourceFile'; 4 | import ReplContext from '../common/ReplContext'; 5 | import ReplCommon from '../common/ReplCommon'; 6 | 7 | const STACK_TRACE_PRIMARY_PATTERN = /(?:at\s*)([^(]+)\(?([^:]+):(\d+):(\d+)\)?/; 8 | const STACK_TRACE_SECONDARY_PATTERN = /(?:at\s*)()([^:]+):(\d+):(\d+)/; 9 | export default class ReplEntryOutputError extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | this.state = { 13 | collapse: true 14 | } 15 | 16 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 17 | 18 | this.message = this.highlightMessage(this.props.message); 19 | 20 | if (this.props.syntaxError) { 21 | let {error, caret, file} = this.props.syntaxError; 22 | let caretPosition = caret.indexOf('^'); 23 | let [start, mid, end] = [ 24 | error.substring(0, caretPosition), 25 | error.substring(caretPosition, caret.length), 26 | error.substring(caret.length) 27 | ]; 28 | let errorFile = this.highlightMessage(/mancy-repl:/.test(file) ? '' : file.trim()); 29 | this.syntaxError =
30 | {errorFile} 31 | 32 | {mid} 33 | 34 |
{this.message}
35 |
36 | this.stacktrace = []; 37 | } else { 38 | this.stacktrace = this.highlightException(this.props.trace); 39 | } 40 | } 41 | 42 | onToggleCollapse() { 43 | this.setState({ 44 | collapse: !this.state.collapse 45 | }); 46 | } 47 | 48 | highlightMessage(msg) { 49 | let output = msg; 50 | let filler = (match, p1, p2) => { 51 | if(p1 && p2) { 52 | output = 53 | 54 | {p1}:{p2} 55 | 56 | } 57 | }; 58 | msg.replace(/^([^:]+):(.*)$/, filler); 59 | return output; 60 | } 61 | 62 | highlightException(stack) { 63 | // revisit: top two stacks are ours ? 64 | // stack = stack.slice(2); 65 | let output = []; 66 | let filler = (match, p1, p2, p3, p4) => { 67 | let openBrace = '', closeBrace = ''; 68 | if(p1.trim().length) { 69 | openBrace = '('; 70 | closeBrace = ')'; 71 | } 72 | let context = ReplContext.getContext(); 73 | let location = ReplCommon.getModuleSourcePath(p2, context.module.paths); 74 | if(location) { p2 = } 75 | 76 | output.push( 77 |
78 |   at  79 | {p1} 80 | {openBrace} 81 | {p2}: 82 | {p3}: 83 | {p4} 84 | {closeBrace} 85 |
86 | ); 87 | return ''; 88 | }; 89 | 90 | _.each(stack, (s) => { 91 | s.replace(s.indexOf('(') !== -1 ? STACK_TRACE_PRIMARY_PATTERN : STACK_TRACE_SECONDARY_PATTERN, filler); 92 | }); 93 | return output; 94 | } 95 | 96 | render() { 97 | return ( 98 | 99 | { 100 | !this.stacktrace.length 101 | ? 102 | {this.syntaxError} 103 | 104 | : this.state.collapse 105 | ? 106 | 107 | {this.message} 108 | 109 | : 110 | 111 | {this.message} 112 | 113 | {this.stacktrace} 114 | 115 | 116 | } 117 | 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/components/ReplEntryStatus.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class ReplEntryStatus extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | } 7 | render() { 8 | return ( 9 |
10 | { 11 | this.props.message.status 12 | ? null 13 | : 15 | } 16 | 17 | { 18 | this.props.collapse 19 | ? 21 | : 22 | } 23 | 24 |
25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/ReplFontFamily.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplPreferencesActions from '../actions/ReplPreferencesActions'; 4 | import RepFonts from '../common/ReplFonts'; 5 | 6 | export default class ReplFonts extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.onChangeFontFamily = this.onChangeFontFamily.bind(this); 10 | } 11 | 12 | onChangeFontFamily(e) { 13 | ReplPreferencesActions.changeFontFamily(e.target.value); 14 | } 15 | 16 | render() { 17 | let fonts = RepFonts.getSystemFonts(); 18 | let font = global.Mancy.preferences.fontFamily; 19 | return ( 20 | 21 | { 22 | fonts.length 23 | ?
24 |
25 | Font 26 |
27 |
28 | 35 |
36 |
37 | : null 38 | } 39 |
40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/ReplNotebook.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplPrompt from './ReplPrompt'; 3 | 4 | export default class ReplNotebook extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | render() { 10 | let {id, tag, plainCode, cursor, transpiledOutput, formattedOutput} = this.props.message; 11 | return ( 12 |
13 | { 14 | 21 | } 22 |
23 | {transpiledOutput} 24 | {formattedOutput} 25 |
26 |
27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/ReplOutputArray.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplOutput from '../common/ReplOutput'; 4 | import ReplOutputObject from './ReplOutputObject'; 5 | import ReplCommon from '../common/ReplCommon'; 6 | import ReplActions from '../actions/ReplActions'; 7 | 8 | export default class ReplOutputArray extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | collapse: true 13 | } 14 | 15 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 16 | this.getKeysButLength = this.getKeysButLength.bind(this); 17 | this.getArrayRecords = this.getArrayRecords.bind(this); 18 | this.getType = this.getType.bind(this); 19 | this.getPrototype = this.getPrototype.bind(this); 20 | this.bindObjectToContext = this.bindObjectToContext.bind(this); 21 | } 22 | 23 | shouldComponentUpdate(nextProps, nextState) { 24 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 25 | } 26 | 27 | onToggleCollapse() { 28 | this.setState({ 29 | collapse: !this.state.collapse 30 | }); 31 | } 32 | 33 | getType() { 34 | let type = ReplCommon.type(this.props.proto); 35 | return ` ${type !== 'Undefined' ? type : 'Array[0]'} {}`; 36 | } 37 | 38 | getKeysButLength() { 39 | // cljs won't return length property since I enumerate the original container 40 | let keys = Object.getOwnPropertyNames(this.props.array).filter( x => x !== 'length'); 41 | return keys.slice(0, keys.length); 42 | } 43 | 44 | getPrototype() { 45 | return this.props.proto || 46 | (this.props.array.length && 47 | this.props.array[0] && 48 | this.props.array[0]._isReactElement && 49 | this.props.array[0]._isReactElement.props.proto 50 | ) || 51 | Object.getPrototypeOf(this.props.array); 52 | } 53 | 54 | getArrayRecords() { 55 | let continuation = this.props.label.indexOf(' … ') !== -1; 56 | return ( 57 | 58 | { 59 | _.map(this.getKeysButLength(), (key) => { 60 | let value = ReplOutput.readProperty(this.props.array, key); 61 | let idx = parseInt(key, 10); 62 | return ( 63 |
64 | { 65 | this.props.noIndex || (value && value._isReactElement) 66 | ? null 67 | : 68 | { this.props.start + idx} 69 | : 70 | 71 | } 72 | { 73 | value && value._isReactElement 74 | ? value 75 | : ReplOutput.transformObject(value) 76 | } 77 |
78 | ) 79 | }) 80 | } 81 | { 82 | continuation 83 | ? null 84 | :
85 | length: {this.props.length ? this.props.length : this.props.array.length} 86 |
87 | } 88 | { 89 | continuation 90 | ? null 91 | :
92 | __proto__ 93 | : 94 | 95 |
96 | } 97 |
98 | ); 99 | } 100 | 101 | bindObjectToContext() { 102 | ReplActions.bindObjectToContext(this.props.array, ReplOutput.transformObject(this.props.array)); 103 | } 104 | 105 | render() { 106 | return ( 107 | 108 | { 109 | this.state.collapse 110 | ? 111 | 112 | {this.props.label} 113 | 114 | : 115 | 116 | {this.props.label} 117 | 118 | {this.getArrayRecords()} 119 | 120 | } 121 | 122 | ); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/components/ReplOutputColor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplOutputString from './ReplOutputString'; 3 | import _ from 'lodash'; 4 | 5 | export default class ReplOutputColor extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | 10 | shouldComponentUpdate(nextProps, nextState) { 11 | return !_.isEqual(nextProps, this.props); 12 | } 13 | 14 | render() { 15 | let colorCode = { backgroundColor: this.props.str }; 16 | return ( 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/ReplOutputCrypto.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplCommon from '../common/ReplCommon'; 3 | import _ from 'lodash'; 4 | 5 | export default class ReplOutputCrypto extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | lock: true 10 | }; 11 | this.toggleLock = this.toggleLock.bind(this); 12 | this.encodeId = _.uniqueId('crpto-'); 13 | this.decodeId = _.uniqueId('crpto-'); 14 | } 15 | 16 | shouldComponentUpdate(nextProps, nextState) { 17 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 18 | } 19 | 20 | toggleLock() { 21 | this.setState({ 22 | lock: !this.state.lock 23 | }); 24 | } 25 | render() { 26 | let data = this.state.lock ? this.props.encode : this.props.decode; 27 | let clazz = `fa ${this.state.lock ? 'fa-lock' : 'fa-unlock'}`; 28 | let cryptoClazz = `repl-output-crypto ${this.state.lock && Buffer.isBuffer(this.props.decode) ? 'extend' : ''}`; 29 | let key = `${this.state.lock ? this.encodeId : this.decodeId}-key`; 30 | return ( 31 | 32 | { 33 | 34 | {data} 35 | 36 | 37 | } 38 | 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/ReplOutputDate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplOutputObject from './ReplOutputObject'; 3 | import _ from 'lodash'; 4 | 5 | export default class ReplOutputDate extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | collapse: true 10 | }; 11 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 12 | } 13 | 14 | shouldComponentUpdate(nextProps, nextState) { 15 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 16 | } 17 | 18 | onToggleCollapse() { 19 | this.setState({ 20 | collapse: !this.state.collapse 21 | }); 22 | } 23 | 24 | render() { 25 | return ( 26 | 27 | { 28 | this.state.collapse 29 | ? 30 | 31 | 32 | {this.props.date.toString()} 33 | 34 | : 35 | 36 | 37 | {this.props.date.toString()} 38 | 39 | { 40 | this.props.date.__proto__ 41 | ?
42 | __proto__ 43 | : 44 | 45 |
46 | : null 47 | } 48 |
49 |
50 | } 51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/ReplOutputGridViewer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplOutput from '../common/ReplOutput'; 4 | import ReplCommon from '../common/ReplCommon'; 5 | 6 | export default class ReplOutputGridViewer extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | collapse: true, 11 | transpose: false 12 | } 13 | 14 | this.gridViewable = this.props.gridViewable || false; 15 | if(!this.gridViewable) { 16 | try{ this.gridViewable = ReplCommon.candidateForGrid(this.props.grid); } 17 | catch(e) {} 18 | } 19 | 20 | _.each([ 21 | 'onToggleCollapse', 'onToggleTranspose', 'renderGrid', 'transposeGridData', 22 | 'gridData', 'getCellString' 23 | ], (field) => { 24 | this[field] = this[field].bind(this); 25 | }); 26 | } 27 | 28 | shouldComponentUpdate(nextProps, nextState) { 29 | return this.gridViewable && !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 30 | } 31 | 32 | onToggleCollapse() { 33 | this.setState({ 34 | collapse: !this.state.collapse 35 | }); 36 | } 37 | 38 | onToggleTranspose() { 39 | this.setState({ 40 | transpose: !this.state.transpose 41 | }); 42 | } 43 | 44 | getCellString(cell) { 45 | return _.isDate(cell) ? cell.toDateString().substring(4) : cell.toString(); 46 | } 47 | 48 | transposeGridData() { 49 | let toTranspose = () => { 50 | let gridValues = this.gridData(); 51 | let data = _.reduce(gridValues, (o, row) => { 52 | _.each(row, (r, pos) => { 53 | o[pos] || o.push([]); 54 | o[pos].push(r); 55 | }); 56 | return o; 57 | }, []); 58 | return (this.transposedGridData = data); 59 | }; 60 | return this.transposedGridData || toTranspose(); 61 | } 62 | 63 | gridData() { 64 | let grid = this.props.grid; 65 | return this.gridValues || (this.gridValues = _.map(_.keys(grid), (key) => _.values(grid[key]))); 66 | } 67 | 68 | renderGrid() { 69 | let grid = this.props.grid; 70 | this.colHeaders = this.colHeaders || _.keys(grid); 71 | this.rowHeaders = this.rowHeaders || _.keys(grid[this.colHeaders[0]]); 72 | 73 | let [rowHeaders, colHeaders] = this.state.transpose 74 | ? [this.colHeaders, this.rowHeaders] : [this.rowHeaders, this.colHeaders]; 75 | 76 | let data = this.state.transpose ? this.transposeGridData() : this.gridData(); 77 | const clazzMap = { 78 | number: 'cm-number', 79 | boolean: 'cm-atom', 80 | object: 'cm-variable', 81 | string: 'cm-string' 82 | }; 83 | 84 | return ( 85 |
86 | 87 | 94 | 95 | 96 | {} 97 | { 98 | _.map(rowHeaders, (head) => { 99 | return 100 | }) 101 | } 102 | 103 | 104 | 105 | { 106 | _.map(colHeaders, (head, pos) => { 107 | return ( 108 | 109 | {} 110 | { 111 | _.map(data[pos], (cell) => { 112 | return 113 | }) 114 | } 115 | 116 | ) 117 | }) 118 | } 119 | 120 |
88 | Transpose Grid 93 |
{head}
{head}{this.getCellString(cell)}
121 |
122 | ); 123 | } 124 | 125 | render() { 126 | if(!this.gridViewable) { return null; } 127 | return ( 128 | 129 | { 130 | this.state.collapse 131 | ? 132 | 133 | Grid Viewer 134 | 135 | : 136 | 137 | Grid Viewer 138 | {this.renderGrid()} 139 | 140 | } 141 | 142 | ); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/components/ReplOutputHTML.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplConstants from '../constants/ReplConstants'; 4 | import md5 from 'md5'; 5 | 6 | export default class ReplOutputHTML extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | html: false 11 | }; 12 | this.onToggleHTMLView = this.onToggleHTMLView.bind(this); 13 | this.onLoadIFrame = this.onLoadIFrame.bind(this); 14 | this.id = md5(this.props.body.innerText + `-${Date.now()}`); 15 | } 16 | 17 | shouldComponentUpdate(nextProps, nextState) { 18 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 19 | } 20 | 21 | componentDidMount() { 22 | this.element = React.findDOMNode(this); 23 | } 24 | 25 | onToggleHTMLView() { 26 | this.setState({ 27 | html: !this.state.html 28 | }); 29 | } 30 | 31 | onLoadIFrame() { 32 | let iframe = document.getElementById(this.id); 33 | let doc = iframe.contentDocument; 34 | doc.body = this.props.body; 35 | let styles = window.getComputedStyle(doc.body); 36 | let height = parseInt(styles.height) + parseInt(styles.marginTop) + parseInt(styles.marginBottom); 37 | // fix max height 38 | iframe.height = Math.min(height, ReplConstants.IFRAME_MAX_HEIGHT) + 'px'; 39 | doc.body.style.color = (document.body.className.indexOf('dark-theme') !== -1 ? 'whitesmoke' : 'darkslategrey'); 40 | } 41 | 42 | render() { 43 | let clazz = `fa fa-html5 ${this.state.html ? 'html' : 'nohtml'}`; 44 | return ( 45 | 46 | {this.props.source} 47 | 48 | { 49 | this.state.html 50 | ? 53 | : null 54 | } 55 | 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/ReplOutputInteger.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | const mode = { 5 | 'bin' : 2, 6 | 'oct' : 8, 7 | 'dec' : 10, 8 | 'hex' : 16 9 | }; 10 | 11 | export default class ReplOutputInteger extends React.Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | mode: 'dec', 16 | type: 'signed', 17 | collapse: 'true' 18 | }; 19 | 20 | _.each([ 21 | 'setMode', 'toIntString', 'getClazz', 'getTypedClazz', 'onSignedMode', 'onUnsignedMode', 'onToggleCollapse' 22 | ], (field) => { 23 | this[field] = this[field].bind(this); 24 | }); 25 | 26 | _.each(_.keys(mode), (m) => { 27 | let n = `on${_.capitalize(m)}Mode`; 28 | this[n] = () => this.setMode(m); 29 | this[n].bind(this); 30 | }); 31 | } 32 | 33 | shouldComponentUpdate(nextProps, nextState) { 34 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 35 | } 36 | 37 | setMode(mode) { 38 | this.setState({ 39 | mode: mode 40 | }); 41 | } 42 | 43 | onToggleCollapse() { 44 | this.setState({ 45 | collapse: !this.state.collapse, 46 | mode: 'dec', 47 | type: 'signed' 48 | }); 49 | } 50 | 51 | onSignedMode() { 52 | this.setState({ 53 | type: 'signed' 54 | }) 55 | } 56 | 57 | onUnsignedMode() { 58 | this.setState({ 59 | type: 'unsigned' 60 | }) 61 | } 62 | 63 | toIntString(n) { 64 | let num = this.state.type === 'signed' ? n : (n >>> 0); 65 | return (num).toString(mode[this.state.mode]); 66 | } 67 | 68 | getClazz(m) { 69 | return `mode ${this.state.mode === m ? 'selected' : ''}`; 70 | } 71 | 72 | getTypedClazz(m) { 73 | return `mode ${this.state.type === m ? 'selected' : ''}`; 74 | } 75 | 76 | isOutOfRange() { 77 | return this.props.int > -1 && this.props.int < 2; 78 | } 79 | 80 | hide() { 81 | return this.state.collapse || this.isOutOfRange(); 82 | } 83 | 84 | render() { 85 | let hide = this.hide(); 86 | let outOfRange = this.isOutOfRange(); 87 | let clazz = `mode-group ${ hide ? 'hide' : 'show'}`; 88 | let tips = outOfRange ? '' : 'Click to Toggle Base/Sign Converter'; 89 | let numClazz = `cm-number ${outOfRange ? '' : 'toggle-number'}`; 90 | return ( 91 | 92 | {this.toIntString(this.props.int)} 93 | 94 | b 95 | o 96 | d 97 | x 98 | 99 | 100 | s 101 | u 102 | 103 | 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/components/ReplOutputPromise.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {EOL} from 'os'; 3 | import ReplCommon from '../common/ReplCommon'; 4 | import ReplOutput from '../common/ReplOutput'; 5 | import ReplConstants from '../constants/ReplConstants'; 6 | import ReplOutputObject from './ReplOutputObject'; 7 | import _ from 'lodash'; 8 | import ReplActions from '../actions/ReplActions'; 9 | 10 | export default class ReplOutputPromise extends React.Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | status: this.props.initStatus, 15 | value: this.props.initValue, 16 | reason: null, 17 | collapse: true 18 | }; 19 | 20 | this.resolve = this.resolve.bind(this); 21 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 22 | this.bindObjectToContext = this.bindObjectToContext.bind(this); 23 | } 24 | 25 | componentDidMount() { 26 | this.resolve(); 27 | } 28 | 29 | shouldComponentUpdate(nextProps, nextState) { 30 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 31 | } 32 | 33 | onToggleCollapse() { 34 | this.setState({ 35 | collapse: !this.state.collapse 36 | }); 37 | } 38 | 39 | resolve() { 40 | let promise = this.props.promise; 41 | promise.then((value) => { 42 | this.setState({ 43 | value: value, 44 | status: ReplConstants.PROMISE.RESOLVED 45 | }); 46 | }).catch((reason) => { 47 | this.setState({ 48 | value: reason, 49 | status: ReplConstants.PROMISE.REJECTED 50 | }); 51 | }); 52 | } 53 | 54 | bindObjectToContext() { 55 | ReplActions.bindObjectToContext(this.props.promise, ReplOutput.transformObject(this.props.promise)); 56 | } 57 | 58 | render() { 59 | let label = ' Promise {}'; 60 | 61 | return ( 62 | 63 | { 64 | this.state.collapse 65 | ? 66 | 67 | 68 | 69 | : 70 | 71 | 72 | 73 | 74 | { 75 |
76 | { 77 | 78 | [[PromiseStatus]] 79 | : 80 | {this.state.status} 81 | 82 | } 83 |
84 | } 85 | { 86 |
87 | { 88 | 89 | [[PromiseValue]] 90 | : 91 | 92 | } 93 | { ReplOutput.transformObject(this.state.value) } 94 |
95 | } 96 | { 97 | this.props.promise.__proto__ 98 | ?
99 | __proto__ 100 | : 101 | 102 |
103 | : null 104 | } 105 |
106 |
107 | } 108 |
); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/components/ReplOutputRegex.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplDOM from '../common/ReplDOM'; 4 | import ReplDOMEvents from '../common/ReplDOMEvents'; 5 | import ReplActions from '../actions/ReplActions'; 6 | import ReplOutput from '../common/ReplOutput'; 7 | 8 | export default class ReplOutputRegex extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | collapse: true, 13 | input: '' 14 | } 15 | 16 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 17 | this.onKeyUp = this.onKeyUp.bind(this); 18 | this.onHighlight = this.onHighlight.bind(this); 19 | this.bindObjectToContext = this.bindObjectToContext.bind(this); 20 | } 21 | 22 | shouldComponentUpdate(nextProps, nextState) { 23 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 24 | } 25 | 26 | componentDidMount() { 27 | this.element = React.findDOMNode(this); 28 | } 29 | 30 | onToggleCollapse() { 31 | this.setState({ 32 | collapse: !this.state.collapse 33 | }); 34 | } 35 | 36 | onHighlight() { 37 | let re = this.props.regex; 38 | let replacer = (match) => { 39 | return match.length ? `${match}` : ''; 40 | }; 41 | return this.state.input.replace(re, replacer); 42 | } 43 | 44 | onKeyUp(e) { 45 | if(e.shiftKey) { return; } 46 | let playGround = this.element.querySelector('.repl-regex-play-ground'); 47 | this.state.input = playGround.innerText; 48 | let cursor = ReplDOM.getCursorPositionRelativeTo(playGround); 49 | let output = this.onHighlight(); 50 | playGround.innerHTML = output; 51 | ReplDOM.setCursorPositionRelativeTo(cursor, playGround); 52 | } 53 | 54 | bindObjectToContext() { 55 | ReplActions.bindObjectToContext(this.props.regex, ReplOutput.transformObject(this.props.regex)); 56 | } 57 | 58 | render() { 59 | return ( 60 | 61 | { 62 | this.state.collapse 63 | ? 64 | 65 | {this.props.regex.toString()} 66 | 67 | : 68 | 69 | {this.props.regex.toString()} 70 | 71 | { 72 |
76 |
77 | } 78 |
79 | } 80 |
81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/components/ReplOutputString.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplConstants from '../constants/ReplConstants'; 3 | import _ from 'lodash'; 4 | 5 | export default class ReplOutputString extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | collapse: true 10 | }; 11 | 12 | const str = this.props.str; 13 | let len = str.length; 14 | let limit = this.props.limit || ReplConstants.OUTPUT_TRUNCATE_LENGTH; 15 | this.collapsable = len > limit - 1; 16 | if(this.collapsable) { 17 | this.prefix = str.slice(0, limit/2); 18 | this.suffix = str.slice(Math.max(limit/2, len - (limit/2))); 19 | } 20 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 21 | } 22 | 23 | shouldComponentUpdate(nextProps, nextState) { 24 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 25 | } 26 | 27 | onToggleCollapse() { 28 | this.setState({ 29 | collapse: !this.state.collapse 30 | }); 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | { 37 | this.collapsable 38 | ? this.state.collapse 39 | ? 40 | 41 | "{this.prefix} 42 | {this.suffix}" 43 | 44 | : 45 | 46 | "{this.props.str}" 47 | 48 | : 49 | "{this.props.str}" 50 | 51 | } 52 | 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/ReplOutputTranspile.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {EOL} from 'os'; 3 | import ReplCommon from '../common/ReplCommon'; 4 | import ReplConstants from '../constants/ReplConstants'; 5 | import _ from 'lodash'; 6 | 7 | export default class ReplOutputTranspile extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | this.state = { 11 | collapse: true, 12 | }; 13 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 14 | 15 | let lines = this.props.output.trim().split(EOL); 16 | if(lines.length > 1 || lines[0].length > ReplConstants.COMMAND_TRUNCATE_LENGTH){ 17 | this.shortEntry = ReplCommon.highlight(lines[0].slice(0, ReplConstants.COMMAND_TRUNCATE_LENGTH)); 18 | } 19 | } 20 | 21 | shouldComponentUpdate(nextProps, nextState) { 22 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 23 | } 24 | 25 | onToggleCollapse() { 26 | this.setState({ 27 | collapse: !this.state.collapse 28 | }); 29 | } 30 | 31 | render() { 32 | return ( 33 |
34 | { 35 | this.shortEntry 36 | ? this.state.collapse 37 | ? 38 | 39 | 40 | 41 | 42 | : 43 | 44 | 45 | 46 | 47 | : 48 | 49 | 50 | 51 | } 52 |
53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/components/ReplOutputURL.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shell} from 'electron'; 3 | import ReplCommon from '../common/ReplCommon'; 4 | import url from 'url'; 5 | import _ from 'lodash'; 6 | 7 | export default class ReplOutputURL extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | this.openExternalFile = this.openExternalFile.bind(this); 11 | } 12 | 13 | shouldComponentUpdate(nextProps, nextState) { 14 | return !_.isEqual(nextProps, this.props); 15 | } 16 | 17 | openExternalFile() { 18 | let u = url.parse(this.props.url); 19 | if(u.protocol) { 20 | shell.openExternal(this.props.url); 21 | } else if(ReplCommon.isFile(this.props.url)) { 22 | shell.openExternal(`file://${this.props.url}`); 23 | } else { 24 | shell.openExternal(`http://${this.props.url}`); 25 | } 26 | } 27 | render() { 28 | return ( 29 | 30 | { 31 | 32 | {this.props.url} 33 | 34 | 35 | } 36 | 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/ReplPageZoom.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplPreferencesActions from '../actions/ReplPreferencesActions'; 4 | 5 | const zoomOptions = [0.75, 0.9, 1, 1.1, 1.2, 1.25, 1.3, 1.4, 1.5, 1.6, 1.7, 1.75, 1.8, 1.9, 2, 2.1, 2.2, 2.25, 2.3, 2.4, 2.5]; 6 | export default class ReplPageZoom extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.onChangePageZoomFactor = this.onChangePageZoomFactor.bind(this); 10 | this.getZoomPercentage = this.getZoomPercentage.bind(this); 11 | } 12 | 13 | onChangePageZoomFactor(e) { 14 | ReplPreferencesActions.changePageZoomFactor(parseFloat(e.target.value)); 15 | } 16 | 17 | getZoomPercentage(zoom) { 18 | return `${(zoom * 100).toFixed()}%`; 19 | } 20 | render() { 21 | let zoom = global.Mancy.preferences.pageZoomFactor; 22 | return ( 23 |
24 |
25 | Page zoom 26 |
27 |
28 | 35 |
36 |
37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/ReplPrompt.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplActiveIcon from './ReplActiveIcon'; 3 | import ReplActiveInput from './ReplActiveInput'; 4 | 5 | export default class ReplPrompt extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | render() { 10 | let key = this.props.tag || `prompt-${(Math.random() * Math.pow(10, 9)) | 0}`; 11 | return ( 12 |
13 | 14 | 22 |
23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/ReplSourceFile.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {shell} from 'electron'; 3 | import ReplCommon from '../common/ReplCommon'; 4 | import ReplActiveInput from '../components/ReplActiveInput'; 5 | 6 | export default class ReplSourceFile extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.openExternalFile = this.openExternalFile.bind(this); 10 | } 11 | openExternalFile() { 12 | shell.openItem(this.props.location); 13 | } 14 | render() { 15 | let isNativeModule = false; 16 | if(!this.props.location) { 17 | const nativeModules = ReplCommon.getNativeModules(ReplActiveInput.getRepl().context); 18 | isNativeModule = nativeModules.indexOf(this.props.name) !== -1; 19 | } 20 | 21 | return ( 22 | 23 | { 24 | this.props.location 25 | ? 26 | 27 | {this.props.name} 28 | 29 | 30 | : 31 | ( 32 | isNativeModule 33 | ? '{this.props.name}' is native module 34 | : Unable to find source file for '{this.props.name}' module 35 | ) 36 | } 37 | 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/__mocks__/clipboard.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | writeText: jest.genMockFunction(), 4 | readText: jest.genMockFunction(), 5 | readHTML: jest.genMockFunction(), 6 | writeHTML: jest.genMockFunction(), 7 | clear: jest.genMockFunction() 8 | }; 9 | -------------------------------------------------------------------------------- /src/components/__mocks__/shell.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { beep: jest.genMockFunction() }; 3 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplActiveIcon-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplActiveIcon.js'); 3 | 4 | describe('ReplActiveIcon', () => { 5 | let React = require('react/addons'); 6 | let ReplActiveIcon = require('../ReplActiveIcon.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | 9 | it('should rendered properly', () => { 10 | let component = TestUtils.renderIntoDocument( 11 | 12 | ); 13 | let icon = TestUtils.findRenderedDOMComponentWithTag(component, 'i'); 14 | expect(React.findDOMNode(icon).className).toEqual('fa fa-angle-right'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplConsole-test.js: -------------------------------------------------------------------------------- 1 | import { getStore } from '../../stores/ReplConsoleStore.js'; 2 | 3 | jest.dontMock('../ReplConsole.js'); 4 | describe('ReplConsole', () => { 5 | let React = require('react/addons'); 6 | let ReplConsole = require('../ReplConsole.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | 9 | it('should render console entries', () => { 10 | let storeContent = { entries: [ 11 | { type: 'error', data: 'Error msg', time: Math.random() }, 12 | { type: 'warn', data: 'Warning msg', time: Math.random() }, 13 | { type: 'info', data: 'Info msg', time: Math.random() }, 14 | { type: 'log', data: 'Log msg', time: Math.random() }, 15 | { type: 'debug', data: 'Debug msg', time: Math.random() } 16 | ]}; 17 | getStore.mockReturnValue(storeContent); 18 | 19 | let component = TestUtils.renderIntoDocument( ); 20 | expect(getStore).toBeCalled(); 21 | 22 | let contents = TestUtils.scryRenderedDOMComponentsWithClass(component, 'repl-console-message-entry-content'); 23 | expect(contents.length).toEqual(storeContent.entries.length); 24 | 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplConsoleMessageFilters-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplConsoleMessageFilters.js'); 3 | 4 | describe('ReplConsoleMessageFilters', () => { 5 | let React = require('react/addons'); 6 | let ReplConsoleMessageFilters = require('../ReplConsoleMessageFilters.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | let labels = ['All', 'Error', 'Warning', 'Info', 'Log', 'Debug', 'Clear']; 9 | let displayLabels = ['A', 'E', 'W', 'I', 'L', 'D']; 10 | 11 | it('should render console labels', () => { 12 | let fun = jest.genMockFunction(); 13 | let state = { all: true, error: true, warn: true, info: true, log: true, debug: true }; 14 | let component = TestUtils.renderIntoDocument( 15 | 24 | ); 25 | let filters = TestUtils.scryRenderedDOMComponentsWithClass(component, 'message-filter'); 26 | expect(filters.length).toBe(labels.length); 27 | filters.forEach((filter) => { 28 | let node = React.findDOMNode(filter); 29 | expect(labels.indexOf(node.title)).not.toBe(-1); 30 | }); 31 | }); 32 | 33 | it('should toggle labels', () => { 34 | let fun = jest.genMockFunction(); 35 | let state = { all: false, error: true, warn: true, info: true, log: true, debug: true }; 36 | let component = TestUtils.renderIntoDocument( 37 | 46 | ); 47 | let filters = TestUtils.scryRenderedDOMComponentsWithTag(component, 'input'); 48 | filters.forEach((filter, idx) => { 49 | let node = React.findDOMNode(filter); 50 | expect(node.disabled).toBe(false); 51 | expect(labels.indexOf(node.value)).not.toBe(-1); 52 | TestUtils.Simulate.change(node); 53 | expect(fun.mock.calls.length).toBe(idx + 1); 54 | }); 55 | }); 56 | 57 | it('should check display labels', () => { 58 | let fun = jest.genMockFunction(); 59 | let state = { all: false, error: true, warn: true, info: true, log: true, debug: true }; 60 | let component = TestUtils.renderIntoDocument( 61 | 70 | ); 71 | let filters = TestUtils.scryRenderedDOMComponentsWithClass(component, 'label'); 72 | filters.forEach((filter, idx) => { 73 | let node = React.findDOMNode(filter); 74 | expect(displayLabels.indexOf(node.textContent.trim())).not.toBe(-1); 75 | }); 76 | }); 77 | 78 | it('should click on clear', () => { 79 | let fun = jest.genMockFunction(); 80 | let clear = jest.genMockFunction(); 81 | let state = { all: false, error: true, warn: true, info: true, log: true, debug: true }; 82 | let component = TestUtils.renderIntoDocument( 83 | 92 | ); 93 | let action = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-ban'); 94 | TestUtils.Simulate.click(React.findDOMNode(action)); 95 | expect(fun).not.toBeCalled(); 96 | expect(clear).toBeCalled(); 97 | expect(clear.mock.calls.length).toBe(1); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplEntries-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplEntries.js'); 3 | jest.dontMock('../ReplEntry.js'); 4 | jest.dontMock('md5'); 5 | 6 | describe('ReplEntries', () => { 7 | let React = require('react/addons'); 8 | let ReplEntries = require('../ReplEntries.js'); 9 | let TestUtils = React.addons.TestUtils; 10 | let entries = [{ 11 | formattedOutput: ' undefined ', 12 | plainCode: 'let name = "mancy"', 13 | status: true, 14 | command: 'let name = "mancy"' 15 | }]; 16 | 17 | it('should rendered properly', () => { 18 | let component = TestUtils.renderIntoDocument( 19 | 20 | ); 21 | let children = TestUtils.scryRenderedDOMComponentsWithClass(component, 'repl-entry'); 22 | expect(children.length).toBe(entries.length); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplEntry-test.js: -------------------------------------------------------------------------------- 1 | import ReplActions from '../../actions/ReplActions'; 2 | 3 | jest.dontMock('../ReplEntry.js'); 4 | jest.dontMock('../ReplEntryIcon.js'); 5 | jest.dontMock('../ReplEntryStatus.js'); 6 | 7 | describe('ReplEntry', () => { 8 | let React = require('react/addons'); 9 | let ReplEntry = require('../ReplEntry.js'); 10 | let TestUtils = React.addons.TestUtils; 11 | let entry = { 12 | formattedOutput: ' undefined ', 13 | plainCode: 'let name = "mancy"', 14 | status: true, 15 | command: 'let name = "mancy"' 16 | }; 17 | 18 | it('should collapse', () => { 19 | let component = TestUtils.renderIntoDocument( 20 | 21 | ); 22 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-entry-icon'); 23 | let action = TestUtils.findRenderedDOMComponentWithTag(icon, 'i'); 24 | TestUtils.Simulate.click(action); 25 | expect(ReplActions.toggleCommandEntryView).toBeCalled(); 26 | }); 27 | 28 | it('should toggle entry', () => { 29 | let component = TestUtils.renderIntoDocument( 30 | 31 | ); 32 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'fa fa-minus-circle'); 33 | TestUtils.Simulate.click(icon); 34 | expect(ReplActions.toggleEntryView).toBeCalled(); 35 | }); 36 | 37 | it('should remove', () => { 38 | let component = TestUtils.renderIntoDocument( 39 | 40 | ); 41 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-times-circle'); 42 | TestUtils.Simulate.click(icon); 43 | expect(ReplActions.removeEntry).toBeCalled(); 44 | }); 45 | 46 | it('should reload prompt', () => { 47 | let component = TestUtils.renderIntoDocument( 48 | 49 | ); 50 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'repeat'); 51 | TestUtils.Simulate.click(icon); 52 | expect(ReplActions.reloadPrompt).toBeCalled(); 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplEntryIcon-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplEntryIcon.js'); 3 | 4 | describe('ReplEntryIcon', () => { 5 | let React = require('react/addons'); 6 | let ReplEntryIcon = require('../ReplEntryIcon.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | 9 | it('should have expand icon', () => { 10 | let fun = jest.genMockFunction(); 11 | let component = TestUtils.renderIntoDocument( 12 | 13 | ); 14 | let icon = TestUtils.findRenderedDOMComponentWithTag(component, 'i'); 15 | expect(React.findDOMNode(icon).title).toEqual('expand command'); 16 | 17 | TestUtils.Simulate.click(icon); 18 | expect(fun).toBeCalled(); 19 | }); 20 | 21 | it('should have collapse icon', () => { 22 | let fun = jest.genMockFunction(); 23 | let component = TestUtils.renderIntoDocument( 24 | 25 | ); 26 | let icon = TestUtils.findRenderedDOMComponentWithTag(component, 'i'); 27 | expect(React.findDOMNode(icon).title).toEqual('collapse command'); 28 | 29 | TestUtils.Simulate.click(icon); 30 | expect(fun).toBeCalled(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplEntryMessage-test.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import {highlight} from '../../common/ReplCommon.js'; 3 | jest.dontMock('../ReplEntryMessage.js'); 4 | 5 | describe('ReplEntryMessage', () => { 6 | let React = require('react/addons'); 7 | let ReplEntryMessage = require('../ReplEntryMessage.js'); 8 | let TestUtils = React.addons.TestUtils; 9 | let msg = { 10 | formattedOutput: ' undefined ', 11 | plainCode: 'let name = "mancy"', 12 | status: true, 13 | command: 'let name = "mancy"' 14 | }; 15 | 16 | it('should display command', () => { 17 | let component = TestUtils.renderIntoDocument( 18 | 20 | ); 21 | let command = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-entry-message-command'); 22 | let id = React.findDOMNode(command)._attributes['data-reactid']._valueForAttrModified; 23 | expect(id.endsWith('long')).toBe(true); 24 | TestUtils.findRenderedDOMComponentWithClass(component, 'repl-entry-message-output') 25 | }); 26 | 27 | it('should display command with collapsed output', () => { 28 | let component = TestUtils.renderIntoDocument( 29 | 31 | ); 32 | let command = TestUtils.scryRenderedDOMComponentsWithClass(component, 'repl-entry-message-output'); 33 | expect(command.length).toBe(0); 34 | }); 35 | 36 | it('should display with collapsed command', () => { 37 | let longMsg = _.extend(msg, { 38 | plainCode : "let fun = () => {\n return; \n}" 39 | }); 40 | highlight.mockImpl(() => 'let fun = () => {'); 41 | let component = TestUtils.renderIntoDocument( 42 | 44 | ); 45 | TestUtils.findRenderedDOMComponentWithClass(component, 'ellipsis'); 46 | }); 47 | 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplEntryOutputError-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplEntryOutputError.js'); 3 | 4 | describe('ReplEntryOutputError', () => { 5 | let React = require('react/addons'); 6 | let ReplEntryOutputError = require('../ReplEntryOutputError.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | let error = 'name is not defined'; 9 | let errorFile = 'repl.js'; 10 | let errorFunction = 'REPLServer.defaultEval'; 11 | let errorLine = '166'; 12 | let errorColumn = '27'; 13 | let msg = `ReferenceError: ${error}`; 14 | let trace = [`at ${errorFunction} (${errorFile}:${errorLine}:${errorColumn})`]; 15 | 16 | it('should rendered properly', () => { 17 | let component = TestUtils.renderIntoDocument( 18 | 19 | 20 | ); 21 | let errorMsg = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-entry-output-error-message'); 22 | let errorTraceFile = TestUtils.findRenderedDOMComponentWithClass(component, 'stack-error-file'); 23 | let errorTraceFunction = TestUtils.findRenderedDOMComponentWithClass(component, 'stack-error-function'); 24 | let errorTraceLine = TestUtils.findRenderedDOMComponentWithClass(component, 'stack-error-row'); 25 | let errorTraceColumn = TestUtils.findRenderedDOMComponentWithClass(component, 'stack-error-column'); 26 | 27 | expect(React.findDOMNode(errorMsg).textContent).toContain(error); 28 | expect(React.findDOMNode(errorTraceFile).textContent).toContain(errorFile); 29 | expect(React.findDOMNode(errorTraceFunction).textContent).toContain(errorFunction); 30 | expect(React.findDOMNode(errorTraceLine).textContent).toContain(errorLine); 31 | expect(React.findDOMNode(errorTraceColumn).textContent).toContain(errorColumn); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplEntryStatus-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplEntryStatus.js'); 3 | 4 | describe('ReplEntryStatus', () => { 5 | let React = require('react/addons'); 6 | let ReplEntryStatus = require('../ReplEntryStatus.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | 9 | it('should render entry status collapsed without error', () => { 10 | let fun = jest.genMockFunction(); 11 | let component = TestUtils.renderIntoDocument( 12 | 16 | ); 17 | let errorIcon = TestUtils.scryRenderedDOMComponentsWithClass(component, 'fa-exclamation-triangle'); 18 | expect(errorIcon.length).toBe(0); 19 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'plus'); 20 | TestUtils.Simulate.click(icon); 21 | expect(fun).toBeCalled(); 22 | }); 23 | 24 | it('should render entry status expanded without error', () => { 25 | let fun = jest.genMockFunction(); 26 | let component = TestUtils.renderIntoDocument( 27 | 31 | ); 32 | let errorIcon = TestUtils.scryRenderedDOMComponentsWithClass(component, 'fa-exclamation-triangle'); 33 | expect(errorIcon.length).toBe(0); 34 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'minus'); 35 | TestUtils.Simulate.click(icon); 36 | expect(fun).toBeCalled(); 37 | }); 38 | 39 | it('should render entry status with error', () => { 40 | let fun = jest.genMockFunction(); 41 | let component = TestUtils.renderIntoDocument( 42 | 46 | ); 47 | TestUtils.findRenderedDOMComponentWithClass(component, 'fa-exclamation-triangle'); 48 | }); 49 | 50 | it('should trigger reload', () => { 51 | let fun = jest.genMockFunction(); 52 | let reload = jest.genMockFunction(); 53 | let component = TestUtils.renderIntoDocument( 54 | 58 | ); 59 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'repeat'); 60 | TestUtils.Simulate.click(icon); 61 | expect(reload).toBeCalled(); 62 | expect(fun).not.toBeCalled(); 63 | }); 64 | 65 | it('should trigger remove', () => { 66 | let fun = jest.genMockFunction(); 67 | let remove = jest.genMockFunction(); 68 | let component = TestUtils.renderIntoDocument( 69 | 73 | ); 74 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'remove'); 75 | TestUtils.Simulate.click(icon); 76 | expect(remove).toBeCalled(); 77 | expect(fun).not.toBeCalled(); 78 | }); 79 | 80 | it('should trigger toggle', () => { 81 | let fun = jest.genMockFunction(); 82 | let toggle = jest.genMockFunction(); 83 | let component = TestUtils.renderIntoDocument( 84 | 88 | ); 89 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'minus'); 90 | TestUtils.Simulate.click(icon); 91 | expect(toggle).toBeCalled(); 92 | expect(fun).not.toBeCalled(); 93 | }); 94 | 95 | }); 96 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplOutputArray-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplOutputArray.js'); 3 | jest.dontMock('../../common/ReplOutput.js'); 4 | 5 | describe('ReplOutputArray', () => { 6 | let React = require('react/addons'); 7 | let ReplOutputArray = require('../ReplOutputArray.js'); 8 | let TestUtils = React.addons.TestUtils; 9 | let label = 'Array[7]'; 10 | let arr = [0, true, 'yes', null, undefined, [1], {fun: () => {}}]; 11 | 12 | it('should have collapsed array', () => { 13 | let component = TestUtils.renderIntoDocument( 14 | 15 | ); 16 | 17 | let desc = TestUtils.findRenderedDOMComponentWithClass(component, 'array-desc'); 18 | expect(React.findDOMNode(desc).textContent).toEqual(label); 19 | let entry = TestUtils.scryRenderedDOMComponentsWithClass(component, 'array-entry'); 20 | expect(entry.length).toBe(0); 21 | }); 22 | 23 | it('should expand array', () => { 24 | let component = TestUtils.renderIntoDocument( 25 | 26 | ); 27 | 28 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-play'); 29 | TestUtils.Simulate.click(icon); 30 | 31 | let entries = TestUtils.scryRenderedDOMComponentsWithClass(component, 'array-entry'); 32 | expect(entries.length).toBe(arr.length); 33 | }); 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplOutputFunction-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplOutputFunction.js'); 3 | jest.dontMock('../../common/ReplOutput.js'); 4 | 5 | describe('ReplOutputFunction', () => { 6 | let React = require('react/addons'); 7 | let ReplOutputFunction = require('../ReplOutputFunction.js'); 8 | let TestUtils = React.addons.TestUtils; 9 | let label = ' function() {}'; 10 | let f = function test() { 11 | return 'test'; 12 | }; 13 | f.desc = 'testMe'; 14 | let funElement = ` 15 | function test() { 16 | return 'test'; 17 | } 18 | 19 | `; 20 | let shortElement = ` 21 | function test() { 22 | 23 | `; 24 | 25 | it('should have collapsed function', () => { 26 | let component = TestUtils.renderIntoDocument( 27 | 28 | ); 29 | 30 | let desc = TestUtils.findRenderedDOMComponentWithClass(component, 'object-desc'); 31 | expect(React.findDOMNode(desc).textContent).toEqual(label); 32 | let entry = TestUtils.scryRenderedDOMComponentsWithClass(component, 'object-entry'); 33 | expect(entry.length).toBe(0); 34 | }); 35 | 36 | it('should expand function', () => { 37 | let component = TestUtils.renderIntoDocument( 38 | 39 | ); 40 | 41 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-play'); 42 | TestUtils.Simulate.click(icon); 43 | 44 | let entries = TestUtils.scryRenderedDOMComponentsWithClass(component, 'object-entry'); 45 | expect(entries.length).toBe(Object.getOwnPropertyNames(f).length); 46 | }); 47 | 48 | it('should expand function source', () => { 49 | let component = TestUtils.renderIntoDocument( 50 | 51 | ); 52 | 53 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-play'); 54 | TestUtils.Simulate.click(icon); 55 | 56 | let source = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-plus-square-o'); 57 | TestUtils.Simulate.click(source); 58 | 59 | TestUtils.scryRenderedDOMComponentsWithClass(component, 'fa-minus-square-o'); 60 | }); 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplOutputObject-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplOutputObject.js'); 3 | jest.dontMock('../../common/ReplOutput.js'); 4 | 5 | describe('ReplOutputObject', () => { 6 | let React = require('react/addons'); 7 | let ReplOutputObject = require('../ReplOutputObject.js'); 8 | let TestUtils = React.addons.TestUtils; 9 | let label = ' Object {}'; 10 | let o = { name: 'mancy' }; 11 | 12 | it('should have collapsed object', () => { 13 | let component = TestUtils.renderIntoDocument( 14 | 15 | ); 16 | 17 | let desc = TestUtils.findRenderedDOMComponentWithClass(component, 'object-desc'); 18 | expect(React.findDOMNode(desc).textContent).toEqual(label); 19 | let entry = TestUtils.scryRenderedDOMComponentsWithClass(component, 'object-entry'); 20 | expect(entry.length).toBe(0); 21 | }); 22 | 23 | it('should expand object', () => { 24 | let component = TestUtils.renderIntoDocument( 25 | 26 | ); 27 | 28 | let icon = TestUtils.findRenderedDOMComponentWithClass(component, 'fa-play'); 29 | TestUtils.Simulate.click(icon); 30 | 31 | let entries = TestUtils.scryRenderedDOMComponentsWithClass(component, 'object-entry'); 32 | expect(entries.length).toBe(Object.keys(o).length); 33 | }); 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplPrompt-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplPrompt.js'); 3 | jest.dontMock('../ReplActiveIcon.js'); 4 | jest.dontMock('../ReplActiveInput.js'); 5 | jest.dontMock('../../stores/ReplActiveInputStore.js'); 6 | 7 | describe('ReplPrompt', () => { 8 | let React = require('react/addons'); 9 | let ReplPrompt = require('../ReplPrompt.js'); 10 | let TestUtils = React.addons.TestUtils; 11 | 12 | it('should rendered properly', () => { 13 | let component = TestUtils.renderIntoDocument( 14 | 21 | ); 22 | let icon = TestUtils.findRenderedDOMComponentWithTag(component, 'i'); 23 | expect(React.findDOMNode(icon).className).toEqual('fa fa-angle-right'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/__tests__/ReplStatusBar-test.js: -------------------------------------------------------------------------------- 1 | 2 | jest.dontMock('../ReplStatusBar.js'); 3 | 4 | describe('ReplStatusBar', () => { 5 | let React = require('react/addons'); 6 | let ReplStatusBar = require('../ReplStatusBar.js'); 7 | let TestUtils = React.addons.TestUtils; 8 | let entries = [ 9 | { status: false }, 10 | { status: true }, 11 | { status: true }, 12 | { status: true }, 13 | { status: false } 14 | ]; 15 | 16 | it('should show counts & mode', () => { 17 | let fun = jest.genMockFunction(); 18 | let component = TestUtils.renderIntoDocument( 19 | 24 | ); 25 | let command = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-status-bar-commands'); 26 | let error = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-status-bar-errors'); 27 | let mode = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-status-bar-mode'); 28 | 29 | expect(React.findDOMNode(command).textContent).toEqual('3'); 30 | expect(React.findDOMNode(error).textContent).toEqual('2'); 31 | expect(React.findDOMNode(mode).textContent).toEqual('magic'); 32 | }); 33 | 34 | it('should show console bell', () => { 35 | let fun = jest.genMockFunction(); 36 | let component = TestUtils.renderIntoDocument( 37 | 42 | ); 43 | TestUtils.findRenderedDOMComponentWithClass(component, 'console-notification'); 44 | }); 45 | 46 | it('should toggle console', () => { 47 | let fun = jest.genMockFunction(); 48 | let component = TestUtils.renderIntoDocument( 49 | 54 | ); 55 | let console = TestUtils.findRenderedDOMComponentWithClass(component, 'repl-status-bar-console'); 56 | TestUtils.Simulate.click(console); 57 | expect(fun).toBeCalled(); 58 | }); 59 | 60 | it('should show console', () => { 61 | let fun = jest.genMockFunction(); 62 | let component = TestUtils.renderIntoDocument( 63 | 68 | ); 69 | TestUtils.findRenderedDOMComponentWithClass(component, 'text-danger'); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsDoc.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | export default class ReplOutputCljsDoc extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | collapse: !!this.props.open, 9 | }; 10 | 11 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 12 | } 13 | 14 | shouldComponentUpdate(nextProps, nextState) { 15 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 16 | } 17 | 18 | onToggleCollapse() { 19 | this.setState({ 20 | collapse: !this.state.collapse 21 | }); 22 | } 23 | 24 | render() { 25 | let clazz = this.state.collapse ? 'fa fa-minus-square-o' : 'fa fa-plus-square-o'; 26 | return ( 27 |
28 | { 29 | 30 | 31 | {this.props.name} 32 | { 33 | this.state.collapse 34 | ?
35 | 36 |
37 | {this.props.description} 38 |
39 |
40 | : null 41 | } 42 |
43 | } 44 |
45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsDocs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | export default class ReplOutputCljsDocs extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | collapse: true, 9 | }; 10 | 11 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 12 | } 13 | 14 | shouldComponentUpdate(nextProps, nextState) { 15 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 16 | } 17 | 18 | onToggleCollapse() { 19 | this.setState({ 20 | collapse: !this.state.collapse 21 | }); 22 | } 23 | 24 | render() { 25 | let clazz = this.state.collapse ? 'fa fa-minus-square-o' : 'fa fa-plus-square-o'; 26 | return ( 27 | 28 | { 29 | 30 | 31 | Documentation Viewer 32 | {this.state.collapse ? this.props.docs : null} 33 | 34 | } 35 | 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsFun.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplOutput from '../../common/ReplOutput'; 3 | 4 | export default class ReplOutputCljsVal extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | const type = this.props.token.type; 8 | const parts = type.split(/[\s.]/); 9 | this.type = parts[parts.length - 1]; 10 | } 11 | 12 | render() { 13 | return ( 14 | 15 | {this.props.token.prefix} 16 | { 17 | this.props.token.keywordPrefix 18 | ? {this.props.token.keywordPrefix} 19 | : null 20 | } 21 | {this.props.value} 22 | {this.props.token.suffix} 23 | {this.type} 24 | 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsMeta.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplOutput from '../../common/ReplOutput'; 4 | 5 | export default class ReplOutputCljsMeta extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | iCollapse: true, 10 | collapse: true 11 | }; 12 | 13 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 14 | this.onToggleICollapse = this.onToggleICollapse.bind(this); 15 | this.getMetaData = this.getMetaData.bind(this); 16 | this.buildMetaData = this.buildMetaData.bind(this); 17 | } 18 | 19 | shouldComponentUpdate(nextProps, nextState) { 20 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 21 | } 22 | 23 | onToggleCollapse() { 24 | this.setState({ 25 | collapse: !this.state.collapse 26 | }); 27 | } 28 | 29 | onToggleICollapse() { 30 | this.setState({ 31 | iCollapse: !this.state.iCollapse 32 | }); 33 | } 34 | 35 | buildMetaData(arr, result = []) { 36 | for(let pos = 0; pos + 1 < arr.length; pos += 2) { 37 | // revisit 38 | if(!arr[pos] && !arr[pos + 1]) { return result; } 39 | if(arr[pos] === null) { this.buildMetaData(arr[pos + 1].arr, result); } 40 | else { 41 | result.push( 42 |
43 | {arr[pos].toString()} 44 | {ReplOutput.clojure(arr[pos + 1]).view()} 45 |
46 | ); 47 | } 48 | } 49 | return result; 50 | } 51 | 52 | getMetaData(arr) { 53 | let metaRecords = this.buildMetaData(arr); 54 | return ( 55 |
56 | {_.map(metaRecords, r => r)} 57 |
58 | ); 59 | } 60 | 61 | render() { 62 | const value = this.props.value || {}; 63 | const core = this.props.core; 64 | const iMeta = value._meta; 65 | const meta = value.meta; 66 | return ( 67 |
68 | { 69 | iMeta 70 | ? this.state.iCollapse 71 | ? 72 | 73 | root 74 | 75 | : 76 | 77 | root 78 | {this.getMetaData(iMeta.root.arr)} 79 | 80 | : null 81 | } 82 | { 83 | meta 84 | ? this.state.collapse 85 | ? 86 | 87 | meta 88 | 89 | : 90 | 91 | meta 92 | {this.getMetaData(meta.arr)} 93 | 94 | : null 95 | } 96 |
97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsSeq.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplOutput from '../../common/ReplOutput'; 4 | import ReplConstants from '../../constants/ReplConstants'; 5 | 6 | export default class ReplOutputCljsSeq extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { 10 | collapse: true 11 | } 12 | const type = this.props.token.type; 13 | const parts = type.split(/[\s.]/); 14 | this.type = parts[parts.length - 1]; 15 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 16 | } 17 | 18 | shouldComponentUpdate(nextProps, nextState) { 19 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 20 | } 21 | 22 | onToggleCollapse() { 23 | this.setState({ 24 | collapse: !this.state.collapse 25 | }); 26 | } 27 | 28 | getKeysButLength() { 29 | let keys = Object.keys(this.props.array); 30 | return keys.slice(0, keys.length); 31 | } 32 | 33 | getShortSeq() { 34 | const arr = this.props.array; 35 | const SHORT_LEN = ReplConstants.CLJS_SEQ_TRUNCATE_LENGTH; 36 | const element = 37 | 38 | {this.props.token.prefix} 39 | {this.getSeqRecords(Math.min(arr.length, SHORT_LEN))} 40 | { 41 | arr.length > SHORT_LEN 42 | ? 43 | : null 44 | } 45 | {this.props.token.suffix} 46 | 47 | 48 | return {short: arr.length <= SHORT_LEN, element: element}; 49 | } 50 | 51 | buildMapData(arr, result = []) { 52 | for(let pos = 0; pos + 1 < arr.length; pos += 2) { 53 | const key = arr[pos]; 54 | const value = arr[pos + 1]; 55 | result.push( 56 |
57 | {key.toString()} 58 | 59 | { value && value._isReactElement ? {value} : ReplOutput.clojure(value).view() } 60 | 61 |
62 | ); 63 | } 64 | return result; 65 | } 66 | 67 | 68 | getSeqRecords(len = -1) { 69 | const clazz = `${len !== -1 ? 'inline' : ''} array-rec`; 70 | const type = this.type; 71 | const mapType = this.props.token.arity === 2; 72 | let keys = this.getKeysButLength(); 73 | keys = len !== -1 ? keys.slice(0, len) : keys; 74 | return ( 75 | 76 | { 77 | !mapType 78 | ? _.map(keys, (key) => { 79 | let value = this.props.array[key]; 80 | let idx = parseInt(key, 10); 81 | return ( 82 |
83 | { len === -1 ? {this.props.start + idx}: : null} 84 | { value && value._isReactElement ? {value} : ReplOutput.clojure(value).view() } 85 |
86 | ) 87 | }) 88 | : this.buildMapData(this.props.array.slice(0, keys.length)) 89 | } 90 |
91 | ); 92 | } 93 | 94 | render() { 95 | const {short, element} = this.getShortSeq(); 96 | const title = this.props.length || ""; 97 | return ( 98 | 99 | { 100 | short 101 | ? 102 | {element} 103 | {this.type} 104 | 105 | : this.state.collapse 106 | ? 107 | 108 | {element} 109 | {this.type} 110 | 111 | : 112 | 113 | {element} 114 | {this.type} 115 | {this.getSeqRecords()} 116 | 117 | } 118 | 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsSource.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | 4 | export default class ReplOutputCljsVar extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | collapse: true, 9 | }; 10 | 11 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 12 | } 13 | 14 | shouldComponentUpdate(nextProps, nextState) { 15 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 16 | } 17 | 18 | onToggleCollapse() { 19 | this.setState({ 20 | collapse: !this.state.collapse 21 | }); 22 | } 23 | 24 | render() { 25 | return ( 26 | 27 | { 28 | this.state.collapse 29 | ? 30 | 31 | {this.props.short} 32 | 33 | : 34 | 35 | 36 | 37 | 38 | } 39 | 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsVal.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplOutput from '../../common/ReplOutput'; 3 | 4 | export default class ReplOutputCljsVal extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | const type = this.props.token.type; 8 | const parts = type.split(/[\s.]/); 9 | this.type = parts[parts.length - 1]; 10 | } 11 | 12 | render() { 13 | return ( 14 | 15 | {this.props.token.prefix} 16 | { 17 | this.props.token.keywordPrefix 18 | ? {this.props.token.keywordPrefix} 19 | : null 20 | } 21 | {this.props.value} 22 | {this.props.token.suffix} 23 | {this.type} 24 | 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsVar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import ReplOutput from '../../common/ReplOutput'; 4 | 5 | export default class ReplOutputCljsVar extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | collapse: true 10 | }; 11 | 12 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 13 | } 14 | 15 | shouldComponentUpdate(nextProps, nextState) { 16 | return !(_.isEqual(nextState, this.state) && _.isEqual(nextProps, this.props)); 17 | } 18 | 19 | onToggleCollapse() { 20 | this.setState({ 21 | collapse: !this.state.collapse 22 | }); 23 | } 24 | 25 | getValue() { 26 | try { 27 | return this.props.value.val(); 28 | } catch(e) { 29 | // revist: something went wrong! 30 | return e.message; 31 | } 32 | } 33 | 34 | render() { 35 | return ( 36 | 37 | { 38 | this.state.collapse 39 | ? 40 | 41 | # 42 | ' 43 | {this.props.value.sym.str} 44 | 45 | : 46 | 47 | # 48 | ' 49 | {this.props.value.sym.str} 50 |
51 | {ReplOutput.clojure(this.getValue()).view()} 52 |
53 |
54 | } 55 |
56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/clojurescript/ReplOutputCljsWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReplOutput from '../../common/ReplOutput'; 3 | import ReplCommon from '../../common/ReplCommon'; 4 | import ReplOutputCljsMeta from './ReplOutputCljsMeta'; 5 | import ReplOutputGridViewer from '../ReplOutputGridViewer' 6 | import ReplOutputChartViewer from '../ReplOutputChartViewer' 7 | 8 | export default class ReplOutputCljsWrapper extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | collapse: true 13 | }; 14 | 15 | if(this.props.value) { 16 | const value = this.props.value; 17 | this.hasMeta = value.meta || value._meta; 18 | 19 | this.jsValue = this.props.core.clj__GT_js(value); 20 | try{ this.hasGrid = ReplCommon.candidateForGrid(this.jsValue); } 21 | catch(e) { this.hasGrid = false; } 22 | this.hasChart = ReplCommon.candidateForChart(this.jsValue); 23 | } 24 | this.hasExtra = this.hasMeta || this.hasGrid || this.hasChart; 25 | this.onToggleCollapse = this.onToggleCollapse.bind(this); 26 | } 27 | 28 | onToggleCollapse() { 29 | this.setState({ 30 | collapse: !this.state.collapse 31 | }); 32 | } 33 | 34 | getMeta() { 35 | return ((!this.state.collapse && this.hasMeta) 36 | ? 37 | : null); 38 | } 39 | 40 | getGrid() { 41 | return ((!this.state.collapse && this.hasGrid) 42 | ?
43 | 44 |
45 | : null); 46 | } 47 | 48 | getChart() { 49 | return ((!this.state.collapse && this.hasChart) 50 | ?
51 | 52 |
53 | : null); 54 | } 55 | 56 | render() { 57 | const clazz = `fa fa-${this.state.collapse ? 'plus' : 'minus'}-square-o`; 58 | return ( 59 | 60 | { 61 | this.hasExtra 62 | ? 63 | 64 | {this.props.view} 65 | {this.getMeta()} 66 | {this.getGrid()} 67 | {this.getChart()} 68 | 69 | : this.props.view 70 | } 71 | 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/constants/ReplConstants.js: -------------------------------------------------------------------------------- 1 | const ReplConstants = { 2 | REPL_HISTORY_SIZE: 1000, 3 | REPL_ENCODING: 'utf8', 4 | TAB_WIDTH: 2, 5 | COMMAND_TRUNCATE_LENGTH: 80, 6 | OUTPUT_TRUNCATE_LENGTH: 80, 7 | CLJS_SEQ_TRUNCATE_LENGTH: 10, 8 | PROMISE: { 9 | PENDING: 'pending', 10 | RESOLVED: 'fulfilled', 11 | REJECTED: 'rejected', 12 | }, 13 | REPL_HISTORY_SUGGESTION: 200, 14 | BABEL_OPTIONS: { 15 | "plugins": [ 16 | "transform-es2015-classes", 17 | "transform-es2015-computed-properties", 18 | "transform-es2015-destructuring", 19 | "transform-es2015-for-of", 20 | "transform-es2015-function-name", 21 | "transform-es2015-object-super", 22 | "transform-es2015-parameters", 23 | "transform-es2015-sticky-regex", 24 | "transform-es2015-unicode-regex", 25 | "transform-regenerator", 26 | "transform-do-expressions", 27 | "transform-function-bind", 28 | "transform-class-constructor-call", 29 | "transform-class-properties", 30 | "transform-decorators", 31 | "transform-export-extensions", 32 | "syntax-trailing-function-commas", 33 | "transform-object-rest-spread", 34 | "transform-async-to-generator", 35 | "transform-exponentiation-operator", 36 | "syntax-flow", 37 | "syntax-jsx", 38 | "transform-flow-strip-types", 39 | "transform-react-jsx", 40 | "transform-runtime" 41 | ], 42 | "highlightCode": false, 43 | "filename": `${__dirname}/mancy-repl`, 44 | "env": process.env, 45 | "retainLines": true, 46 | "ast": false, 47 | "babelrc": false, 48 | }, 49 | EXEC_TIMEOUT: 60000, 50 | IFRAME_MAX_HEIGHT: 500, 51 | REPL_WATERMARK_LOGO: '>_', 52 | REPL_WATERMARK_MSG: 'REPL for fun 🙈', 53 | }; 54 | 55 | export default ReplConstants; 56 | -------------------------------------------------------------------------------- /src/languages/ReplLangWrapper.js: -------------------------------------------------------------------------------- 1 | import {Readable, Writable} from 'stream'; 2 | import ReplContext from '../common/ReplContext'; 3 | import ReplConstants from '../constants/ReplConstants'; 4 | import ReplOutput from '../common/ReplOutput'; 5 | import {EOL} from 'os'; 6 | import fs from 'fs'; 7 | 8 | import jsREPL from 'repl'; 9 | import coffeeREPL from 'coffee-script/repl'; 10 | import tsREPL from './ReplTypeScript'; 11 | import lsREPL from './ReplLiveScript'; 12 | import cljsREPL from './ReplClojureScript'; 13 | 14 | const REPL = (repl) => { 15 | let readable = new Readable(); 16 | let writable = new Writable(); 17 | 18 | readable._read = writable.write = () => {}; 19 | 20 | let nodeRepl = repl.start({ 21 | prompt: '', 22 | input: readable, 23 | output: writable, 24 | terminal: false, 25 | useGlobal: false, 26 | ignoreUndefined: false, 27 | useColors: false, 28 | writer: (obj, opt) => { 29 | nodeRepl.$lastExpression = ReplOutput.some(obj); 30 | // link context 31 | nodeRepl.context = ReplContext.getContext(); 32 | return '<>'; 33 | }, 34 | historySize: ReplConstants.REPL_HISTORY_SIZE, 35 | replMode: repl['REPL_MODE_MAGIC'], 36 | }); 37 | 38 | // remove default repl commands 39 | ['clear', 'help', 'save', 'exit'].forEach(cmd => delete nodeRepl.commands[cmd]); 40 | 41 | // here is our sandbox environment 42 | nodeRepl.context = ReplContext.getContext(); 43 | 44 | return { 45 | getREPL: () => nodeRepl, 46 | setREPL: () => ReplContext.hookContext((context) => { nodeRepl.context = context; }), 47 | repl 48 | }; 49 | }; 50 | 51 | // remove loadAction function after below PR pushed and is available in electron based node. 52 | // lib/repl.js with below PR 53 | // https://github.com/nodejs/node/pull/4170 54 | let replJS = REPL(jsREPL); 55 | let loadAction = function(file) { 56 | try { 57 | let stats = fs.statSync(file); 58 | if (stats && stats.isFile()) { 59 | let self = this; 60 | let data = fs.readFileSync(file, 'utf8'); 61 | let lines = data.split('\n'); 62 | this.displayPrompt(); 63 | lines.forEach(function(line) { 64 | if (line) { 65 | self.write(line + '\n'); 66 | } 67 | }); 68 | } else { 69 | this.outputStream.write('Failed to load:' + file + ' is not a valid file\n'); 70 | } 71 | } catch (e) { 72 | this.outputStream.write('Failed to load:' + file + '\n'); 73 | } 74 | this.displayPrompt(); 75 | }; 76 | replJS.getREPL().commands.load.action = loadAction; 77 | 78 | export default { 79 | js: replJS, 80 | coffee: REPL(coffeeREPL), 81 | ts: REPL(tsREPL), 82 | ls: REPL(lsREPL), 83 | cljs: REPL(cljsREPL), 84 | }; 85 | -------------------------------------------------------------------------------- /src/languages/ReplLanguages.js: -------------------------------------------------------------------------------- 1 | import {js, coffee, ts, ls, cljs} from './ReplLangWrapper'; 2 | 3 | // node repl wrappers 4 | const langs = { 5 | js, 6 | coffee, 7 | ts, 8 | ls, 9 | cljs, 10 | }; 11 | 12 | const repls = Object.keys(langs).map(l => langs[l].getREPL()); 13 | 14 | let repl = langs.js; 15 | repl.setREPL(); 16 | 17 | const getREPL = () => { 18 | return repl.getREPL(); 19 | } 20 | 21 | // being used for repl mode 22 | const getREPLProvider = () => { 23 | return langs.js.repl; 24 | } 25 | 26 | const setREPL = (name) => { 27 | if(!langs[name]) { 28 | throw new Error(`Unsupported lang ${name}`); 29 | } 30 | repl = langs[name]; 31 | repl.setREPL(); 32 | 33 | const langREPL = repl.getREPL(); 34 | if(langREPL.updateCompilerOptions) { 35 | langREPL.updateCompilerOptions(); 36 | } 37 | 38 | return langREPL; 39 | } 40 | 41 | const getNamespace = () => { 42 | const langREPL = getREPL(); 43 | if(typeof langREPL.getNamespace === 'function') { 44 | return langREPL.getNamespace(); 45 | } 46 | return ''; 47 | } 48 | 49 | const aliases = { 50 | js: 'js', json: 'js', node: 'js', 51 | coffee: 'coffee', litcoffee: 'coffee', 'coffee.md': 'coffee', 52 | ls: 'ls', 53 | ts: 'ts', tsx: 'ts', 54 | cljs: 'cljs', 55 | }; 56 | 57 | const qualifiedNames = { 58 | js: 'javascript', json: 'javascript', node: 'javascript', 59 | coffee: 'x-coffeescript', litcoffee: 'x-coffeescript', 'coffee.md': 'x-coffeescript', 60 | ls: 'x-liveScript', 61 | ts: 'typescript', tsx: 'typescript', 62 | cljs: 'x-clojure' 63 | }; 64 | 65 | 66 | export default { 67 | getREPL, 68 | setREPL, 69 | getREPLProvider, 70 | getNamespace, 71 | getLangName: (ext) => aliases[ext], 72 | getLangQualifiedName: (ext) => qualifiedNames[ext], 73 | setLookupPath: (paths) => repls.forEach(repl => repl.setLookupPath && repl.setLookupPath(paths)) 74 | }; 75 | -------------------------------------------------------------------------------- /src/languages/ReplLiveScript.js: -------------------------------------------------------------------------------- 1 | import ls from 'livescript'; 2 | import path from 'path'; 3 | import {EOL} from 'os'; 4 | import nodeREPL from 'repl'; 5 | import _ from 'lodash'; 6 | import child_process from 'child_process'; 7 | import vm from 'vm'; 8 | import fs from 'fs'; 9 | 10 | let nodeLineListener = () => {}; 11 | let promptData = ''; 12 | 13 | let loadFile = (module, filename) => { 14 | let result = ls.compile(fs.readFileSync(fileName).toString(), { bare: false }); 15 | return module._compile(result.toString(), filename); 16 | }; 17 | 18 | // register extensions 19 | let register = () => { 20 | if (require.extensions) { 21 | require.extensions['.ls'] = loadFile; 22 | } 23 | 24 | let fork = child_process.fork; 25 | let binary = require.resolve(path.join(__dirname, '../node_modules/livescript/bin/lsc')); 26 | child_process.fork = (path, args, options) => { 27 | if(/\.ls?$/.test(path)) { 28 | if(!Array.isArray(args)) { 29 | options = args || {}; 30 | args = []; 31 | } 32 | args = [path].concat(args); 33 | path = binary; 34 | } 35 | return fork(path, args, options); 36 | }; 37 | }; 38 | 39 | let evaluate = (input, context, filename, cb) => { 40 | try { 41 | let js = ls.compile(input, { bare: true }).toString(); 42 | return cb(null, vm.runInContext(js, context, filename)); 43 | } catch(e) { 44 | return cb(e); 45 | } 46 | } 47 | 48 | let transpile = (input, context, cb) => { 49 | try { 50 | let js = ls.compile(input, { bare: true }).toString(); 51 | let lines = js.split(/\r?\n/g); 52 | lines.shift(); 53 | return cb(null, lines.join(EOL)); 54 | } catch(e) { 55 | return cb(e); 56 | } 57 | }; 58 | 59 | let addMultilineHandler = ({rli}) => { 60 | nodeLineListener = rli.listeners('line')[0]; 61 | rli.removeListener('line', nodeLineListener); 62 | rli.on('line', (cmd) => { 63 | promptData += cmd + EOL; 64 | }); 65 | }; 66 | 67 | let loadAction = { 68 | help: '?', 69 | action: function(file) { 70 | try { 71 | let stats = fs.statSync(file); 72 | if (stats && stats.isFile()) { 73 | let self = this; 74 | let data = fs.readFileSync(file, 'utf8'); 75 | this.displayPrompt(); 76 | nodeLineListener(data); 77 | promptData = ''; 78 | } else { 79 | this.outputStream.write('Failed to load:' + file + ' is not a file\n'); 80 | } 81 | } catch (e) { 82 | this.outputStream.write('Failed to load:' + file + '\n'); 83 | } 84 | this.displayPrompt(); 85 | } 86 | }; 87 | 88 | /// export repl 89 | export default { 90 | start: (options = {}) => { 91 | register(); 92 | let opts = _.extend({eval: evaluate}, options); 93 | let repl = nodeREPL.start(opts); 94 | repl.on('exit', () => { 95 | if(!repl.rli.closed) { 96 | repl.outputStream.write(EOL); 97 | } 98 | }); 99 | repl.input.on('data', (d) => { 100 | if(d === EOL) { 101 | nodeLineListener(promptData); 102 | promptData = ''; 103 | } 104 | }); 105 | addMultilineHandler(repl); 106 | repl.transpile = transpile; 107 | repl.defineCommand('load', loadAction); 108 | return repl; 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /src/stores/ReplActiveInputStore.js: -------------------------------------------------------------------------------- 1 | import ReplActiveInputActions from '../actions/ReplActiveInputActions'; 2 | import Reflux from 'reflux'; 3 | 4 | let activeSuggestion = null; 5 | let now = false; 6 | let breakPrompt = false; 7 | let format = false; 8 | let autoComplete = false; 9 | let stagedCommands = []; 10 | 11 | const ReplActiveInputStore = Reflux.createStore({ 12 | init() { 13 | this.listenToMany(ReplActiveInputActions); 14 | }, 15 | onTabCompleteSuggestion(suggestion, id) { 16 | activeSuggestion = { suggestion, id }; 17 | now = breakPrompt = format = autoComplete = false; 18 | this.trigger(); 19 | }, 20 | onResetTabCompleteSuggestion() { 21 | activeSuggestion = null; 22 | now = breakPrompt = format = autoComplete = false; 23 | this.trigger(); 24 | }, 25 | onPerformAutoComplete() { 26 | autoComplete = true; 27 | this.trigger(); 28 | }, 29 | onFillTabCompleteSuggestion(suggestion, id) { 30 | activeSuggestion = { suggestion, id }; 31 | breakPrompt = format = autoComplete = false; 32 | now = true; 33 | this.trigger(); 34 | }, 35 | onBreakPrompt() { 36 | activeSuggestion = null; 37 | now = format = autoComplete = false; 38 | breakPrompt = true; 39 | this.trigger(); 40 | }, 41 | onFormatCode() { 42 | format = true; 43 | autoComplete = false; 44 | this.trigger(); 45 | }, 46 | onPlayCommands(commands) { 47 | stagedCommands = commands; 48 | this.trigger(); 49 | }, 50 | tailStagedCommands() { 51 | stagedCommands.shift(); 52 | }, 53 | onSetTheme(t) { 54 | this.trigger({name: 'theme', value: t}); 55 | }, 56 | onSetMode(m) { 57 | this.trigger({name: 'mode', value: m}); 58 | }, 59 | onUpdateSuggestionDelay() { 60 | this.trigger(); 61 | }, 62 | onSetEditorOption(action) { 63 | this.trigger(action); 64 | }, 65 | onUndo() { 66 | this.trigger({action: 'undo'}); 67 | }, 68 | onRedo() { 69 | this.trigger({action: 'redo'}); 70 | }, 71 | onSelectAll() { 72 | this.trigger({action: 'selectAll'}); 73 | }, 74 | onFocus() { 75 | if(global.Mancy.session.editor === 'REPL') { 76 | this.trigger({action: 'focus'}); 77 | } 78 | }, 79 | getStore() { 80 | return { 81 | activeSuggestion, 82 | now, 83 | breakPrompt, 84 | format, 85 | stagedCommands, 86 | autoComplete, 87 | } 88 | } 89 | }); 90 | export default ReplActiveInputStore; 91 | -------------------------------------------------------------------------------- /src/stores/ReplConsoleStore.js: -------------------------------------------------------------------------------- 1 | import ReplConsoleActions from '../actions/ReplConsoleActions'; 2 | import Reflux from 'reflux'; 3 | import _ from 'lodash'; 4 | 5 | let cache = []; 6 | const ReplConsoleStore = Reflux.createStore({ 7 | init() { 8 | this.listenToMany(ReplConsoleActions); 9 | }, 10 | onAddEntry(item) { 11 | let dup = false; 12 | if(cache.length) { 13 | let lastItem = cache[cache.length - 1]; 14 | item.time = lastItem.time; 15 | item.count = lastItem.count; 16 | if(_.isEqual(item, lastItem)) { 17 | lastItem.count = lastItem.count + 1; 18 | dup = true; 19 | } 20 | } 21 | 22 | if(!dup){ 23 | item.count = 1; 24 | item.time = Math.random(); 25 | cache.push(item); 26 | } 27 | this.trigger(); 28 | }, 29 | onClear() { 30 | this.clear(); 31 | }, 32 | clear() { 33 | cache = []; 34 | this.trigger(); 35 | }, 36 | getStore() { 37 | return { 38 | entries: cache 39 | } 40 | } 41 | }); 42 | export default ReplConsoleStore; 43 | -------------------------------------------------------------------------------- /src/stores/ReplStatusBarStore.js: -------------------------------------------------------------------------------- 1 | import ReplStatusBarActions from '../actions/ReplStatusBarActions'; 2 | import Reflux from 'reflux'; 3 | 4 | let newRelease = null; 5 | let language = ''; 6 | let mode = ''; 7 | let cursor = [1, 1]; 8 | const ReplStatusBarStore = Reflux.createStore({ 9 | init() { 10 | this.listenToMany(ReplStatusBarActions); 11 | }, 12 | onUpdateRunCommand() { 13 | this.trigger(); 14 | }, 15 | onNewRelease(release) { 16 | newRelease = release; 17 | this.trigger(); 18 | }, 19 | onUpdateLanguage(lang) { 20 | language = lang; 21 | this.trigger(); 22 | }, 23 | onUpdateMode(m) { 24 | mode = m.toLowerCase(); 25 | this.trigger(); 26 | }, 27 | onRefresh() { 28 | this.trigger(); 29 | }, 30 | onCursorActivity(c) { 31 | cursor = c; 32 | this.trigger(); 33 | }, 34 | getStore() { 35 | let {toggleShiftEnter, lang} = global.Mancy.preferences; 36 | return { 37 | runCommand: toggleShiftEnter, 38 | newRelease, 39 | lang: language || lang, 40 | mode, 41 | cursor 42 | }; 43 | } 44 | }); 45 | export default ReplStatusBarStore; 46 | -------------------------------------------------------------------------------- /src/stores/ReplSuggestionStore.js: -------------------------------------------------------------------------------- 1 | import ReplSuggestionActions from '../actions/ReplSuggestionActions'; 2 | import Reflux from 'reflux'; 3 | 4 | const ReplSuggestionStore = Reflux.createStore({ 5 | init() { 6 | this.listenToMany(ReplSuggestionActions); 7 | }, 8 | onAddSuggestion(item) { 9 | this.trigger(item); 10 | }, 11 | onRemoveSuggestion() { 12 | this.trigger({suggestions:[], input: ''}); 13 | } 14 | }); 15 | export default ReplSuggestionStore; 16 | -------------------------------------------------------------------------------- /stylesheets/clojurescript/repl-output-cljs-docs.scss: -------------------------------------------------------------------------------- 1 | @import '../themes'; 2 | @import '../repl-common'; 3 | 4 | @mixin replOutputCljsDocs($theme) { 5 | 6 | .repl-cljs-docs-fold { 7 | flex: 1; 8 | .repl-cljs-doc-list { 9 | .title { padding-left: 6px; } 10 | } 11 | .repl-cljs-doc { 12 | padding-left: 12px; 13 | .doc-header { 14 | padding-left: 6px; 15 | font-weight: 900; 16 | color:if($theme == $dark-theme, $dark-app-cljs-doc-header-color, $lt-app-cljs-doc-header-color); 17 | } 18 | .doc-body { 19 | .doc-definition { 20 | 21 | } 22 | .doc-description { 23 | padding-top: 6px; 24 | padding-bottom: 6px; 25 | color: if($theme == $dark-theme, $dark-app-cljs-doc-desc-color, $lt-app-cljs-doc-desc-color); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /stylesheets/clojurescript/repl-output-cljs-meta.scss: -------------------------------------------------------------------------------- 1 | @import '../themes'; 2 | @import '../repl-common'; 3 | 4 | @mixin replOutputCljsMeta($theme) { 5 | 6 | .repl-cljs-meta-fold { 7 | flex: 1; 8 | padding-left: 12px; 9 | .repl-cljs-meta { 10 | word-break: break-all; 11 | .fa-minus-square-o, .fa-plus-square-o { 12 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 13 | padding-right: 6px; 14 | position: relative; 15 | top: 1px; 16 | } 17 | .meta-label { 18 | padding-left: 5px; 19 | } 20 | .meta-records { 21 | padding-left: 15px; 22 | .meta-record { 23 | .meta-key { 24 | padding-right: 12px; 25 | } 26 | .meta-value { 27 | 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /stylesheets/clojurescript/repl-output-cljs-source.scss: -------------------------------------------------------------------------------- 1 | @import '../themes'; 2 | @import '../repl-common'; 3 | 4 | @mixin replOutputCljsSource($theme) { 5 | 6 | .repl-cljs-source-fold { 7 | flex: 1; 8 | .repl-cljs-source { 9 | word-break: break-all; 10 | .fa { padding-right: 6px; } 11 | .repl-cljs-source-code { 12 | 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /stylesheets/clojurescript/repl-output-cljs-val.scss: -------------------------------------------------------------------------------- 1 | @import '../themes'; 2 | @import '../repl-common'; 3 | 4 | @mixin replOutputCljsVal($theme) { 5 | 6 | .repl-cljs-val { 7 | flex: 1; 8 | display: inline-flex; 9 | flex-direction: row; 10 | 11 | .prefix { 12 | padding-right: 6px; 13 | } 14 | 15 | .suffix { 16 | padding-left: 6px; 17 | } 18 | 19 | .value { 20 | font-weight: 900; 21 | } 22 | 23 | .tag { 24 | background-color: if($theme == $dark-theme, $dark-app-tag-bg-color, $lt-app-tag-bg-color); 25 | color: if($theme == $dark-theme, $dark-app-tag-color, $lt-app-tag-color); 26 | border-radius: 4px; 27 | padding-left: 5px; 28 | padding-right: 5px; 29 | padding-top: 1px; 30 | padding-bottom: 1px; 31 | font-size: 0.75em; 32 | height: 80%; 33 | position: relative; 34 | top: 3px; 35 | font-weight: 900; 36 | margin-left: 6px; 37 | cursor: default; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /stylesheets/clojurescript/repl-output-cljs-var.scss: -------------------------------------------------------------------------------- 1 | @import '../themes'; 2 | @import '../repl-common'; 3 | 4 | @mixin replOutputCljsVar($theme) { 5 | 6 | .repl-cljs-var-fold { 7 | flex: 1; 8 | .repl-cljs-var { 9 | word-break: break-all; 10 | .fa-play { 11 | font-size: 0.8em; 12 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 13 | padding-right: 3px; 14 | } 15 | .fa-play.fa-rotate-90 { 16 | position: relative; 17 | top: 2px; 18 | padding-top: 5px; 19 | padding-right: 3px; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stylesheets/clojurescript/repl-output-cljs-wrapper.scss: -------------------------------------------------------------------------------- 1 | @import '../themes'; 2 | @import '../repl-common'; 3 | 4 | @mixin replOutputCljsWrapper($theme) { 5 | 6 | .repl-cljs-wrapper { 7 | flex: 1; 8 | flex-direction: column; 9 | 10 | .repl-cljs-annotate { 11 | .fa { 12 | padding-right: 6px; 13 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 14 | } 15 | } 16 | .repl-cljs-grid-annotate, .repl-cljs-chart-annotate { 17 | display: flex; 18 | padding-left: 12px; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /stylesheets/configs.scss: -------------------------------------------------------------------------------- 1 | $app-font-family: 'Droid Sans Mono',sans-serif; 2 | $app-font-size: 11pt; 3 | $app-text-selection-color: whitesmoke; 4 | $app-text-selection-background-color: #B964C7; 5 | $app-text-error-color: tomato; 6 | $app-external-icon-color: #EC348F; 7 | $app-external-link-color: #97AD5A; 8 | 9 | $app-green-execution-color: #25A725; 10 | $app-red-execution-color: red; 11 | $app-orange-execution-color: orange; 12 | 13 | $dark-theme: 'dark-theme'; 14 | $light-theme: 'light-theme'; 15 | -------------------------------------------------------------------------------- /stylesheets/highlight.scss: -------------------------------------------------------------------------------- 1 | @mixin stackTraceHighlight($theme) { 2 | .stack-error-at { 3 | color: if($theme == $dark-theme, $dark-app-stack-error-at-color, $lt-app-stack-error-at-color); 4 | } 5 | .stack-error-function { 6 | color: if($theme == $dark-theme, $dark-app-stack-error-function-color, $lt-app-stack-error-function-color); 7 | } 8 | 9 | .stack-error-file { 10 | color: if($theme == $dark-theme, $dark-app-stack-error-file-color, $lt-app-stack-error-file-color); 11 | } 12 | 13 | .stack-error-row { 14 | color: if($theme == $dark-theme, $dark-app-stack-error-row-color, $lt-app-stack-error-row-color); 15 | } 16 | .stack-error-column { 17 | color: if($theme == $dark-theme, $dark-app-stack-error-column-color, $lt-app-stack-error-column-color); 18 | } 19 | 20 | .error-name { 21 | color: if($theme == $dark-theme, $dark-app-stack-error-name-color, $lt-app-stack-error-name-color); 22 | } 23 | 24 | .error-description { 25 | color: if($theme == $dark-theme, $dark-app-stack-error-description-color, $lt-app-stack-error-description-color); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stylesheets/repl-common.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | 3 | 4 | // google font 5 | @font-face { 6 | font-family: 'Josefin Sans'; 7 | font-style: normal; 8 | font-weight: 400; 9 | src: url('../fonts/Josefin_Sans/JosefinSans-Regular.ttf'); 10 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Droid Sans Mono'; 15 | font-style: normal; 16 | font-weight: 400; 17 | src: url('../fonts/Droid_Sans_Mono/DroidSansMono.ttf'); 18 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; 19 | } 20 | 21 | @font-face { 22 | font-family: 'FiraCode'; 23 | font-style: normal; 24 | font-weight: 400; 25 | src: url('../fonts/FiraCode/FiraCode-Regular.otf'); 26 | } 27 | 28 | @mixin fullHeight() { 29 | height: calc(100vh - (#{$app-font-size} + 20px)); 30 | } 31 | 32 | @mixin notSelectable() { 33 | -webkit-user-select: none; 34 | user-select: none; 35 | } 36 | 37 | @mixin selectable() { 38 | -webkit-user-select: initial; 39 | user-select: initial; 40 | } 41 | 42 | @mixin entryLayout() { 43 | flex: 1; 44 | min-height: $app-font-size; 45 | padding-left: 10px; 46 | margin: 0px; 47 | } 48 | 49 | @mixin arrowIcon() { 50 | width: 10px; 51 | text-align: center; 52 | } 53 | 54 | @mixin editor() { 55 | outline: none; 56 | margin: 0; 57 | word-wrap: break-word; 58 | word-break: break-word; 59 | white-space: pre-wrap; 60 | } 61 | 62 | .dull { 63 | opacity: 0.7; 64 | } 65 | 66 | .close { 67 | color: $app-text-error-color; 68 | } 69 | 70 | .flex { 71 | flex: 1; 72 | } 73 | 74 | button { 75 | margin-right: 5px; 76 | } 77 | 78 | .read-error { 79 | color: $app-text-error-color; 80 | } 81 | 82 | small { 83 | font-size: 8px; 84 | } 85 | 86 | [disabled="true"] { 87 | cursor: not-allowed !important; 88 | } 89 | -------------------------------------------------------------------------------- /stylesheets/repl-console-environment-watcher.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | @import 'repl-output'; 4 | @import 'repl-source-file'; 5 | 6 | @mixin replConsoleEnvironment($theme) { 7 | .repl-console-environment { 8 | flex: 0 0 200px; 9 | display: flex; 10 | flex-direction: column; 11 | } 12 | .repl-console-environment-head { 13 | @include notSelectable(); 14 | width: 100%; 15 | height: 30px; 16 | min-height: 30px; 17 | border-bottom: 1px solid if($theme == $dark-theme, $dark-app-console-message-filter-border-color, $lt-app-console-message-filter-border-color); 18 | font-weight: 900; 19 | font-size: 110%; 20 | display: flex; 21 | align-self: center; 22 | 23 | .repl-console-environment-title { 24 | text-align: center; 25 | align-self: center; 26 | } 27 | } 28 | .repl-console-environment-body { 29 | flex: 1; 30 | overflow: auto; 31 | padding-right: 10px; 32 | .repl-console-environment-body-header { 33 | @include notSelectable(); 34 | font-weight: 900; 35 | display: flex; 36 | width: 100%; 37 | height: 30px; 38 | padding: 0px 5px; 39 | margin-bottom: 10px; 40 | background-color: if($theme == $dark-theme, $dark-app-console-env-data-heading-color, $lt-app-console-env-data-heading-color); 41 | .repl-console-environment-listing-title { 42 | align-self: center; 43 | } 44 | } 45 | 46 | .repl-console-environment-listing { 47 | @include replOutput($theme); 48 | margin: 0 5px; 49 | .env-entry { 50 | display: flex; 51 | padding: 5px 0px; 52 | .env-key, .env-value { 53 | flex: 1; 54 | } 55 | } 56 | } 57 | 58 | .repl-console-environment-no-data { 59 | @include notSelectable(); 60 | margin-bottom: 10px; 61 | text-align: center; 62 | color: if($theme == $dark-theme, $dark-app-console-env-no-data-color, $lt-app-console-env-no-data-color); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /stylesheets/repl-container-left.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | @import 'repl-prompt'; 4 | @import 'repl-entries'; 5 | 6 | 7 | @mixin containerLeft($theme) { 8 | .repl-container-left { 9 | flex: 1; 10 | display: flex; 11 | @include fullHeight(); 12 | overflow: auto; 13 | flex-direction: column; 14 | 15 | .repl-header { 16 | height: 5px; 17 | } 18 | 19 | @include replEntries($theme); 20 | @include replPrompt($theme); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /stylesheets/repl-container-right.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | @import 'repl-console-message'; 4 | 5 | @mixin containerRight($theme) { 6 | .repl-container-right { 7 | display: flex; 8 | max-width: 100vw; 9 | flex: 0 0 30vw; 10 | min-width: 8px; 11 | @include fullHeight(); 12 | overflow: auto; 13 | flex-direction: column; 14 | 15 | .repl-header { 16 | height: 5px; 17 | } 18 | 19 | .repl-console { 20 | min-width: 280px; 21 | position: relative; 22 | top: 0; 23 | background-color: if($theme == $dark-theme, $dark-app-console-background-color, $lt-app-console-background-color); 24 | color: if($theme == $dark-theme, $dark-app-console-color, $lt-app-console-color); 25 | overflow: auto; 26 | flex: 1; 27 | display: flex; 28 | border-top-left-radius: 5px; 29 | border-bottom-left-radius: 5px; 30 | 31 | .repl-console-resizeable { 32 | display: flex; 33 | width: 8px; 34 | position: relative; 35 | @include fullHeight(); 36 | background-color: if($theme == $dark-theme, $dark-app-console-resize-handle-color, $lt-app-console-resize-handle-color); 37 | border-top-left-radius: 5px; 38 | border-bottom-left-radius: 5px; 39 | cursor: ew-resize; 40 | align-items: center; 41 | 42 | .repl-console-drag-lines { 43 | @include notSelectable(); 44 | 45 | &:before { 46 | content: 'ǁ'; 47 | text-align: center; 48 | margin: 1px; 49 | color: if($theme == $dark-theme, $dark-app-console-drag-lines-color, $lt-app-console-drag-lines-color); 50 | } 51 | } 52 | } 53 | @include replConsoleMessage($theme); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /stylesheets/repl-entries.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | @import 'repl-entry-status'; 4 | @import 'repl-entry-message'; 5 | 6 | @mixin replEntries($theme) { 7 | .repl-entries { 8 | .repl-entry { 9 | display: flex; 10 | margin: 0; 11 | padding: 5px; 12 | &.repl-notebook { 13 | padding: 0px; 14 | } 15 | .repl-entry-icon, 16 | .repl-entry-status { 17 | @include notSelectable(); 18 | } 19 | 20 | .repl-entry-icon { 21 | color: if($theme == $dark-theme, $dark-app-entry-icon-color, $lt-app-entry-icon-color); 22 | @include arrowIcon(); 23 | .fa { 24 | cursor: pointer; 25 | } 26 | } 27 | 28 | @include replEntryStatus($theme); 29 | @include replEntryMessage($theme); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /stylesheets/repl-entry-message.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | @import 'highlight'; 4 | @import 'repl-output'; 5 | @import 'repl-source-file'; 6 | 7 | @mixin replEntryMessage($theme) { 8 | .repl-entry-message { 9 | @include entryLayout(); 10 | 11 | &.repl-notebook { 12 | padding: 0px; 13 | .repl-entry-message-output { 14 | padding: 5px 25px 5px 25px; 15 | } 16 | } 17 | .repl-entry-command-container { 18 | display: flex; 19 | .tag { 20 | background-color: if($theme == $dark-theme, $dark-app-tag-bg-color, $lt-app-tag-bg-color); 21 | color: if($theme == $dark-theme, $dark-app-tag-color, $lt-app-tag-color); 22 | border-radius: 4px; 23 | padding-left: 5px; 24 | padding-right: 5px; 25 | padding-top: 1px; 26 | padding-bottom: 1px; 27 | font-size: 0.75em; 28 | height: 80%; 29 | position: relative; 30 | top: 3px; 31 | font-weight: 900; 32 | margin-left: 6px; 33 | cursor: default; 34 | } 35 | 36 | .execution-time { 37 | position: relative; 38 | top: 3px; 39 | &.green { 40 | color: $app-green-execution-color; 41 | } 42 | &.red { 43 | color: $app-red-execution-color; 44 | } 45 | &.orange { 46 | color: $app-orange-execution-color; 47 | } 48 | } 49 | 50 | .repl-entry-message-command { 51 | flex: 1; 52 | min-height: $app-font-size; 53 | word-wrap: break-word; 54 | word-break: break-word; 55 | white-space: pre-wrap; 56 | } 57 | 58 | .ellipsis { 59 | &:after { 60 | content: '\2026'; 61 | color: if($theme == $dark-theme, $dark-app-entry-ellipsis-color, $lt-app-entry-ellipsis-color); 62 | } 63 | } 64 | } 65 | 66 | .repl-entry-message-output { 67 | word-wrap: break-word; 68 | word-break: break-word; 69 | white-space: pre-wrap; 70 | padding: 5px 0; 71 | position: relative; 72 | cursor: default; 73 | 74 | @include replSourceFile($theme); 75 | 76 | .fa-clone { 77 | @include notSelectable(); 78 | position: absolute; 79 | color: if($theme == $dark-theme, $dark-app-entry-icon-copy-color, $lt-app-entry-icon-copy-color); 80 | font-size: 0.7em; 81 | position: relative; 82 | font-weight: 900; 83 | left: -10px; 84 | padding-top: 4px; 85 | top: 0; 86 | cursor: pointer; 87 | 88 | &:active { 89 | top: 3px; 90 | } 91 | } 92 | 93 | .repl-entry-message-output-function { 94 | .fa { 95 | padding-right: 10px; 96 | padding-top: 4px; 97 | color: if($theme == $dark-theme, $dark-app-entry-message-output-expand-collapse-color, $lt-app-entry-message-output-expand-collapse-color); 98 | } 99 | .fa-minus-square-o ~ img ~ span { 100 | display: block; 101 | } 102 | .es-img { 103 | padding-right: 5px; 104 | height: 1em; 105 | width: 1em; 106 | } 107 | } 108 | 109 | @include replOutput($theme); 110 | .repl-entry-output-error { 111 | .repl-entry-output-error-message { 112 | .fa-play { 113 | padding-right: 2px; 114 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 115 | } 116 | .repl-entry-output-error-message-heading { 117 | padding-left: 5px; 118 | } 119 | .syntax-error { 120 | .repl-entry-output-error-message-heading { 121 | padding-left: 0px; 122 | } 123 | .err-underline { 124 | text-decoration: underline; 125 | text-decoration-color: $app-text-error-color; 126 | font-size: 0.9em; 127 | } 128 | } 129 | .repl-entry-output-error-stack { 130 | .repl-entry-output-error-stack-lines { 131 | padding-left: calc(1em + 5px); 132 | } 133 | } 134 | } 135 | 136 | @include stackTraceHighlight($theme); 137 | } 138 | 139 | .promise-object { 140 | .promise-status, .promise-value { 141 | color: if($theme == $dark-theme, $dark-app-entry-message-output-primitive-color, $lt-app-entry-message-output-primitive-color); 142 | } 143 | } 144 | 145 | .primitive-object { 146 | .primitive-key { 147 | color: if($theme == $dark-theme, $dark-app-entry-message-output-primitive-color, $lt-app-entry-message-output-primitive-color); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /stylesheets/repl-entry-status.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replEntryStatus($theme) { 5 | .repl-entry-status { 6 | padding-right: 8px; 7 | 8 | .repeat, 9 | .error, 10 | .plus, 11 | .minus, 12 | .remove { 13 | padding-left: 6px; 14 | cursor: pointer; 15 | } 16 | 17 | .repeat { 18 | color: if($theme == $dark-theme, $dark-app-entry-status-redo-color, $lt-app-entry-status-redo-color); 19 | } 20 | 21 | .error { 22 | color: if($theme == $dark-theme, $dark-app-entry-status-error-color, $lt-app-entry-status-error-color); 23 | } 24 | 25 | .minus { 26 | color: if($theme == $dark-theme, $dark-app-entry-status-minus-color, $lt-app-entry-status-minus-color); 27 | } 28 | 29 | .plus { 30 | color: if($theme == $dark-theme, $dark-app-entry-status-plus-color, $lt-app-entry-status-plus-color); 31 | } 32 | 33 | .remove { 34 | color: if($theme == $dark-theme, $dark-app-entry-status-remove-color, $lt-app-entry-status-remove-color); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /stylesheets/repl-output-array.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputArrayFold($theme) { 5 | .repl-entry-message-output-array-folds { 6 | .repl-entry-message-output-array { 7 | .fa-play { 8 | font-size: 0.8em; 9 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 10 | } 11 | .fa-hashtag { 12 | padding-left: 5px; 13 | font-size: 70%; 14 | cursor: pointer; 15 | color: if($theme == $dark-theme, $dark-app-entry-message-output-bind-color, $lt-app-entry-message-output-bind-color); 16 | } 17 | 18 | .cljs-tag { 19 | background-color: if($theme == $dark-theme, $dark-app-tag-bg-color, $lt-app-tag-bg-color); 20 | color: if($theme == $dark-theme, $dark-app-tag-color, $lt-app-tag-color); 21 | border-radius: 4px; 22 | padding-left: 5px; 23 | padding-right: 5px; 24 | padding-top: 1px; 25 | padding-bottom: 1px; 26 | font-size: 0.8em; 27 | height: 80%; 28 | font-weight: 900; 29 | margin-left: 6px; 30 | cursor: default; 31 | } 32 | 33 | .array-desc { 34 | padding-left: 5px; 35 | .ellipsis { 36 | padding-left: 5px; 37 | } 38 | } 39 | 40 | .array-rec { 41 | display: flex; 42 | flex-direction: column; 43 | &.inline { 44 | display: inline-flex; 45 | flex-direction: row; 46 | .array-entry { 47 | padding-left: 5px; 48 | padding-right: 5px; 49 | } 50 | } 51 | .array-entry { 52 | display: flex; 53 | padding-left: 10px; 54 | .map-key { 55 | padding-right: 12px; 56 | } 57 | .array-idx { 58 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-idx-color, $lt-app-entry-message-output-arr-idx-color); 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /stylesheets/repl-output-buffer-explorer.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin bufferRecord($theme){ 5 | padding: 3px 0px; 6 | .offset { 7 | font-weight: 900; 8 | padding: 0px 10px; 9 | width: 60px; 10 | text-align: right; 11 | display: inline-block; 12 | } 13 | 14 | .offset-pos, .offset-ascii-pos { 15 | padding: 0px 5px; 16 | text-align: center; 17 | &:nth-child(8n + 1) { 18 | margin-right: 15px; 19 | } 20 | } 21 | 22 | .offset-ascii-pos { 23 | padding: 0px; 24 | } 25 | .offset-pos.even.in { 26 | background-color: if($theme == $dark-theme, $dark-app-data-buffer-even-in-color, $lt-app-data-buffer-even-in-color); 27 | } 28 | 29 | .offset-ascii-pos.even.in { 30 | background-color: if($theme == $dark-theme, $dark-app-data-buffer-even-in-ascii-color, $lt-app-data-buffer-even-in-ascii-color); 31 | } 32 | 33 | .offset-pos.odd.in { 34 | background-color: if($theme == $dark-theme, $dark-app-data-buffer-odd-in-color, $lt-app-data-buffer-odd-in-color); 35 | } 36 | 37 | .offset-ascii-pos.odd.in { 38 | background-color: if($theme == $dark-theme, $dark-app-data-buffer-odd-in-ascii-color, $lt-app-data-buffer-odd-in-ascii-color); 39 | } 40 | 41 | .out { 42 | background-color: if($theme == $dark-theme, $dark-app-data-buffer-out-bg-color, $lt-app-data-buffer-out-bg-color);; 43 | } 44 | 45 | .offset-ascii-pos.mask.in { 46 | color: if($theme == $dark-theme, $dark-app-data-buffer-mask-color, $lt-app-data-buffer-mask-color); 47 | } 48 | } 49 | 50 | @mixin replOutputBufferExplorer($theme) { 51 | 52 | .repl-output-buffer-explorer-container { 53 | .data-explorer-label { 54 | color: if($theme == $dark-theme, $dark-app-data-explorer-label-color, $lt-app-data-explorer-label-color); 55 | padding: 0px 0px 0px 5px; 56 | } 57 | } 58 | 59 | .repl-output-data-buffer-explorer { 60 | min-width: 700px; 61 | overflow: auto; 62 | color: if($theme == $dark-theme, $dark-app-data-buffer-explorer-color, $lt-app-data-buffer-explorer-color); 63 | border-radius: 5px; 64 | padding-top: 10px; 65 | border: 1px dashed currentColor; 66 | 67 | .data-buffer-grid-header { 68 | font-weight: 900; 69 | @include bufferRecord($theme); 70 | } 71 | 72 | .data-buffer-grid-body { 73 | .data-buffer-grid-row { 74 | @include bufferRecord($theme); 75 | } 76 | } 77 | 78 | .data-buffer-grid-pagination { 79 | display: flex; 80 | align-items: center; 81 | 82 | i.fa { 83 | font-size: 2em; 84 | padding: 3px 5px; 85 | cursor: pointer; 86 | } 87 | 88 | .fa-caret-right { 89 | text-align: left; 90 | } 91 | .fa-caret-left { 92 | text-align: right; 93 | } 94 | .fa-caret-left.disabled, .fa-caret-right.disabled { 95 | pointer-events: none; 96 | opacity: 0.5; 97 | } 98 | 99 | .placeholder { 100 | flex: 1; 101 | } 102 | 103 | .textbox { 104 | padding: 0px 4px; 105 | input { 106 | height: 0.9em; 107 | outline: none; 108 | } 109 | } 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /stylesheets/repl-output-chart-viewer.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputChartViewer($theme) { 5 | .repl-output-chart-viewer-container { 6 | flex: 1; 7 | .data-explorer-label { 8 | color: if($theme == $dark-theme, $dark-app-data-explorer-label-color, $lt-app-data-explorer-label-color); 9 | padding: 0px 0px 0px 5px; 10 | } 11 | 12 | .repl-output-data-chart-viewer { 13 | .chart-viewer { 14 | display: block; 15 | position: relative; 16 | top: 0px; 17 | left: 0px; 18 | 19 | .c3 line, .c3 path { 20 | stroke: if($theme == $dark-theme, $dark-app-chart-stroke-color, $lt-app-chart-stroke-color); 21 | } 22 | g { 23 | fill: if($theme == $dark-theme, $dark-app-chart-text-color, $lt-app-chart-text-color); 24 | } 25 | .c3-tooltip-container { 26 | color: if($theme == $dark-theme, $dark-app-chart-text-color, $lt-app-color); 27 | } 28 | } 29 | .chart-viewer-preferences { 30 | display: flex; 31 | margin: auto; 32 | align-items: center; 33 | .fa { 34 | color: if($theme == $dark-theme, $dark-app-chart-icon-color, $lt-app-chart-icon-color); 35 | padding: 0px 5px; 36 | cursor: pointer; 37 | } 38 | .placeholder { 39 | flex: 1; 40 | } 41 | .fa.selected { 42 | pointer-events: none; 43 | color: if($theme == $dark-theme, $dark-app-chart-icon-selected-color, $lt-app-chart-icon-selected-color); 44 | } 45 | 46 | .checkbox-group { 47 | padding: 0px 5px; 48 | } 49 | } 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /stylesheets/repl-output-color.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputColor($theme) { 5 | 6 | .repl-color { 7 | .repl-color-box { 8 | width: 1em; 9 | height: 1em; 10 | border-radius: 3px; 11 | display: inline-flex; 12 | align-self: center; 13 | position: relative; 14 | top: 2px; 15 | margin-left: 5px; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /stylesheets/repl-output-crypto.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputCrypto($theme) { 5 | .repl-output-crypto.extend{ 6 | flex: 1; 7 | } 8 | .repl-output-crypto{ 9 | .repl-crypto-data { 10 | display: inline-flex; 11 | .fa-lock, .fa-unlock { 12 | padding-left: 5px; 13 | cursor: pointer; 14 | top: 3px; 15 | position: relative; 16 | } 17 | .fa-lock { 18 | color: if($theme == $dark-theme, $dark-app-crypto-lock-color, $lt-app-crypto-lock-color); 19 | display: flex; 20 | align-self: center; 21 | top: 0px; 22 | } 23 | .fa-unlock { 24 | color: if($theme == $dark-theme, $dark-app-crypto-unlock-color, $lt-app-crypto-unlock-color); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /stylesheets/repl-output-grid-viewer.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputGridViewer($theme) { 5 | .repl-output-grid-viewer-container { 6 | .data-explorer-label { 7 | color: if($theme == $dark-theme, $dark-app-data-explorer-label-color, $lt-app-data-explorer-label-color); 8 | padding: 0px 0px 0px 5px; 9 | } 10 | 11 | .repl-output-grid-viewer { 12 | max-height: 300px; 13 | overflow: auto; 14 | display: flex; 15 | 16 | .grid-viewer { 17 | border-collapse: separate; 18 | border-spacing: 0px; 19 | border-radius: 5px; 20 | margin: 0 auto; 21 | border: 1px solid if($theme == $dark-theme, $dark-app-data-grid-border-color, $lt-app-data-grid-border-color); 22 | 23 | .grid-caption { 24 | min-width: 160px; 25 | padding: 10px; 26 | } 27 | td, th { 28 | text-align: center; 29 | border: 1px solid if($theme == $dark-theme, $dark-app-data-grid-inner-border-color, $lt-app-data-grid-inner-border-color); 30 | border-spacing: 0px; 31 | border-right-style: solid; 32 | border-bottom-style: solid; 33 | border-top-style: none; 34 | border-left-style: none; 35 | padding: 5px; 36 | } 37 | td:last-child, th:last-child { 38 | border-right-style: none; 39 | } 40 | tbody > tr:last-child th, tbody > tr:last-child td{ 41 | border-bottom-style: none; 42 | } 43 | th { 44 | color: if($theme == $dark-theme, $dark-app-data-grid-header-color, $lt-app-data-grid-header-color); 45 | } 46 | } 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /stylesheets/repl-output-html.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputHTML($theme) { 5 | 6 | .repl-html-fold { 7 | flex: 1; 8 | 9 | .fa-html5 { 10 | color: if($theme == $dark-theme, $dark-app-active-html-logo-color, $lt-app-active-html-logo-color);; 11 | padding-left: 5px; 12 | cursor: pointer; 13 | } 14 | 15 | .fa-html5.nohtml { 16 | color: if($theme == $dark-theme, $dark-app-inactive-html-logo-color, $lt-app-inactive-html-logo-color);; 17 | } 18 | 19 | 20 | iframe.sandbox-view { 21 | display: block; 22 | border: none; 23 | width: 100%; 24 | background-color: initial; 25 | overflow: auto; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /stylesheets/repl-output-integer.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | 3 | @mixin replOutputInteger($theme) { 4 | 5 | .repl-integer { 6 | .toggle-number { 7 | cursor: pointer; 8 | border-bottom: 1px dotted grey; 9 | border-bottom-right-radius: 2px; 10 | border-bottom-left-radius: 2px; 11 | } 12 | .mode-group { 13 | opacity: 0.5; 14 | margin: 3px 5px; 15 | display: inline-flex; 16 | border: 1px solid if($theme == $dark-theme, $dark-app-integer-mode-color, $lt-app-integer-mode-color); 17 | border-radius: 3px; 18 | .mode { 19 | width: 1.2em; 20 | text-align: center; 21 | border-right: 1px solid if($theme == $dark-theme, $dark-app-integer-mode-color, $lt-app-integer-mode-color); 22 | cursor: pointer; 23 | font-size: 0.8em; 24 | 25 | &:last-child { 26 | border-right: none; 27 | } 28 | } 29 | .mode.selected { 30 | background-color: if($theme == $dark-theme, $dark-app-integer-mode-color, $lt-app-integer-mode-color); 31 | color: white; 32 | } 33 | .mode { 34 | position: relative; 35 | &:before { 36 | content: attr(data-token); 37 | position: absolute; 38 | top: -1.4em; 39 | font-size: 0.7em; 40 | color: if($theme == $dark-theme, $dark-app-integer-mode-title-color, $lt-app-integer-mode-title-color); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /stylesheets/repl-output-object.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputObjectFold($theme) { 5 | .repl-entry-message-output-object-folds { 6 | flex: 1; 7 | .repl-entry-message-output-object { 8 | .fa-play { 9 | font-size: 0.8em; 10 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 11 | } 12 | .fa-hashtag { 13 | padding-left: 5px; 14 | font-size: 70%; 15 | cursor: pointer; 16 | color: if($theme == $dark-theme, $dark-app-entry-message-output-bind-color, $lt-app-entry-message-output-bind-color); 17 | } 18 | 19 | .fa-calendar { 20 | padding: 0px 5px; 21 | color: if($theme == $dark-theme, $dark-app-date-icon-color, $lt-app-date-icon-color); 22 | } 23 | 24 | .fa-download { 25 | padding: 0px 5px; 26 | position: relative; 27 | top: 1px; 28 | font-size: 0.8em; 29 | cursor: pointer; 30 | color: if($theme == $dark-theme, $dark-app-download-color, $lt-app-download-color); 31 | } 32 | 33 | .object-desc { 34 | padding-left: 0px; 35 | } 36 | 37 | .object-rec { 38 | display: flex; 39 | flex-direction: column; 40 | 41 | .object-entry { 42 | display: flex; 43 | padding-left: 10px; 44 | 45 | .object-key { 46 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-idx-color, $lt-app-entry-message-output-arr-idx-color); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stylesheets/repl-output-regex.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputRegex($theme) { 5 | 6 | .repl-regex-fold { 7 | flex: 1; 8 | .repl-regex { 9 | 10 | .fa-play { 11 | color: if($theme == $dark-theme, $dark-app-regex-fold-color, $lt-app-regex-fold-color); 12 | font-size: 0.8em; 13 | position: relative; 14 | top: -1px; 15 | left: 0px; 16 | padding-right: 2px; 17 | } 18 | .fa-hashtag { 19 | padding-left: 5px; 20 | font-size: 70%; 21 | cursor: pointer; 22 | color: if($theme == $dark-theme, $dark-app-entry-message-output-bind-color, $lt-app-entry-message-output-bind-color); 23 | } 24 | 25 | .repl-regex-play-ground[contentEditable=true]:empty:not(:focus):before { 26 | content:attr(placeholder); 27 | color: if($theme == $dark-theme, $dark-app-regex-playground-placeholder-color, $lt-app-regex-playground-placeholder-color); 28 | } 29 | 30 | .repl-regex-play-ground { 31 | @include editor(); 32 | color: if($theme == $dark-theme, $dark-app-regex-playground-color, $lt-app-regex-playground-color); 33 | margin-top: 5px; 34 | border: 2px solid if($theme == $dark-theme, $dark-app-regex-playground-border-color, $lt-app-regex-playground-border-color); 35 | border-radius: 5px; 36 | padding: 5px; 37 | 38 | .matched { 39 | background-color: if($theme == $dark-theme, $dark-app-pattern-matched-bg-color, $lt-app-pattern-matched-bg-color); 40 | } 41 | } 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /stylesheets/repl-output-string.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputString($theme) { 5 | 6 | .repl-string-fold { 7 | flex: 1; 8 | .repl-string { 9 | word-break: break-all; 10 | .fa-play { 11 | font-size: 0.8em; 12 | padding-right: 6px; 13 | color: if($theme == $dark-theme, $dark-app-entry-message-output-arr-fold-color, $lt-app-entry-message-output-arr-fold-color); 14 | } 15 | .fa-play.fa-rotate-90 { 16 | position: relative; 17 | top: 2px; 18 | padding-top: 5px; 19 | padding-right: 6px; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stylesheets/repl-output-url.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replOutputURL($theme) { 5 | 6 | .repl-output-url { 7 | 8 | .repl-url { 9 | color: $app-external-link-color; 10 | .fa-external-link { 11 | padding: 0px 5px; 12 | cursor: pointer; 13 | color: $app-external-icon-color; 14 | position: relative; 15 | top: 1px; 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /stylesheets/repl-output.scss: -------------------------------------------------------------------------------- 1 | @import 'repl-output-array'; 2 | @import 'repl-output-object'; 3 | @import 'repl-output-integer'; 4 | @import 'repl-output-regex'; 5 | @import 'repl-output-string'; 6 | @import 'repl-output-color'; 7 | @import 'repl-output-html'; 8 | @import 'repl-output-url'; 9 | @import 'repl-output-crypto'; 10 | @import 'repl-output-buffer-explorer'; 11 | @import 'repl-output-chart-viewer'; 12 | @import 'repl-output-grid-viewer'; 13 | @import 'clojurescript/repl-output-cljs-var'; 14 | @import 'clojurescript/repl-output-cljs-val'; 15 | @import 'clojurescript/repl-output-cljs-meta'; 16 | @import 'clojurescript/repl-output-cljs-docs'; 17 | @import 'clojurescript/repl-output-cljs-source'; 18 | @import 'clojurescript/repl-output-cljs-wrapper'; 19 | 20 | @mixin replOutput($theme) { 21 | @include replOutputArrayFold($theme); 22 | @include replOutputObjectFold($theme); 23 | @include replOutputInteger($theme); 24 | @include replOutputRegex($theme); 25 | @include replOutputString($theme); 26 | @include replOutputColor($theme); 27 | @include replOutputHTML($theme); 28 | @include replOutputBufferExplorer($theme); 29 | @include replOutputURL($theme); 30 | @include replOutputCrypto($theme); 31 | @include replOutputChartViewer($theme); 32 | @include replOutputGridViewer($theme); 33 | @include replOutputCljsVar($theme); 34 | @include replOutputCljsVal($theme); 35 | @include replOutputCljsMeta($theme); 36 | @include replOutputCljsDocs($theme); 37 | @include replOutputCljsSource($theme); 38 | @include replOutputCljsWrapper($theme); 39 | } 40 | -------------------------------------------------------------------------------- /stylesheets/repl-preferences.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin preferences($theme) { 5 | .repl-preferences-panel { 6 | width: 100%; 7 | display: none; 8 | flex-direction: column; 9 | position: absolute; 10 | top: calc(100% - #{$app-font-size} - 6px); 11 | left: 0; 12 | height: 0px; 13 | z-index: 100; 14 | color: if($theme == $dark-theme, $dark-app-preference-color, $lt-app-preference-color ); 15 | background-color: if($theme == $dark-theme, $dark-app-preference-background-color, $lt-app-preference-background-color ); 16 | transition: all 0.5s cubic-bezier(0.55, 0.09, 0.68, 0.53); 17 | 18 | .repl-preferences-head { 19 | height: 40px; 20 | min-height: 40px; 21 | width: 100%; 22 | display:flex; 23 | background-color: if($theme == $dark-theme, $dark-app-preference-header-bg-color, $lt-app-preference-header-bg-color ); 24 | .title { 25 | margin: auto; 26 | font-weight: 900; 27 | font-size: 1.2em; 28 | } 29 | .close-preference { 30 | color: if($theme == $dark-theme, $dark-app-preference-close-color, $lt-app-preference-close-color ); 31 | cursor: pointer; 32 | display: flex; 33 | padding: 0px 10px; 34 | align-self: center; 35 | } 36 | } 37 | 38 | .repl-preferences-body { 39 | padding: 10px 20px; 40 | display: flex; 41 | flex-direction: column; 42 | 43 | .preference { 44 | flex: 1; 45 | display: flex; 46 | padding: 10px 0px; 47 | min-height: 20px; 48 | border-bottom: 1px solid if($theme == $dark-theme, $dark-app-preference-entry-border-color, $lt-app-preference-entry-border-color ); 49 | 50 | .preference-name { 51 | margin: auto 0; 52 | flex-basis: 30%; 53 | font-weight: 900; 54 | min-width: 300px; 55 | .lang-img { 56 | position: relative; 57 | &.ts-img { 58 | top: 3px; 59 | left: 0px; 60 | } 61 | &.js-img { 62 | top: 4px; 63 | left: 0px; 64 | } 65 | &.cljs-img { 66 | top: 5px; 67 | left: -5px; 68 | } 69 | } 70 | } 71 | .preference-value { 72 | padding: 0px 5px; 73 | flex: 1; 74 | .textbox { 75 | } 76 | 77 | .radio-group { 78 | padding-right: 10px; 79 | } 80 | 81 | .checkbox-group { 82 | padding-right: 6px; 83 | display: inherit; 84 | line-height: 1.75em; 85 | } 86 | 87 | .fa { 88 | padding-left: 5px; 89 | } 90 | 91 | .fa-arrow-up { 92 | color: if($theme == $dark-theme, $dark-app-move-up-color, $lt-app-move-up-color ); 93 | } 94 | 95 | .fa-arrow-down { 96 | color: if($theme == $dark-theme, $dark-app-move-down-color, $lt-app-move-down-color ); 97 | } 98 | } 99 | } 100 | 101 | .statusbar-placeholder { 102 | height: calc(#{$app-font-size} + 20px); 103 | min-height: calc(#{$app-font-size} + 20px); 104 | } 105 | 106 | fieldset { 107 | border: none; 108 | padding: 0px; 109 | } 110 | } 111 | } 112 | .repl-preferences-panel.open { 113 | top: 0px; 114 | height: calc(100% - #{$app-font-size} - 6px); 115 | display: flex; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /stylesheets/repl-prompt.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replPrompt($theme) { 5 | .repl-prompt { 6 | margin: 0; 7 | padding: 5px; 8 | background-color: if($theme == $dark-theme, $dark-app-prompt-background-color, $lt-app-prompt-background-color); 9 | display: flex; 10 | align-items: flex-start; 11 | min-height: 25px; 12 | 13 | .repl-active-icon { 14 | @include notSelectable(); 15 | color: if($theme == $dark-theme, $dark-app-prompt-active-icon-color, $lt-app-prompt-active-icon-color); 16 | @include arrowIcon(); 17 | } 18 | 19 | .repl-active-input { 20 | @include entryLayout(); 21 | @include editor(); 22 | } 23 | .repl-active-input.repl-active-input-running:before { 24 | content: "\f021"; 25 | float: right; 26 | margin-right: 8px; 27 | font: normal normal normal 14px/1 FontAwesome; 28 | color: if($theme == $dark-theme, $dark-app-prompt-spin-color, $lt-app-prompt-spin-color); 29 | animation: fa-spin 2s infinite linear; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /stylesheets/repl-source-file.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @mixin replSourceFile($theme) { 5 | 6 | .repl-source-access { 7 | 8 | .repl-source-file { 9 | color: $app-external-link-color; 10 | .fa-external-link { 11 | padding: 0px 5px; 12 | cursor: pointer; 13 | color: $app-external-icon-color; 14 | position: relative; 15 | top: 1px; 16 | } 17 | } 18 | .repl-no-source-file { 19 | .name { 20 | color: $app-text-error-color; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stylesheets/repl-status-bar.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | 4 | @keyframes spin { 5 | 0% { 6 | -webkit-transform: rotate(-30deg); 7 | transform: rotate(-30deg); 8 | } 9 | 10 | 100% { 11 | -webkit-transform: rotate(30deg); 12 | transform: rotate(30deg); 13 | } 14 | } 15 | 16 | @mixin statusBar($theme) { 17 | .repl-status-bar { 18 | @include notSelectable(); 19 | z-index: 101; 20 | position: fixed; 21 | bottom: 0; 22 | left: 0; 23 | padding: 3px 10px; 24 | background-color: if($theme == $dark-theme, $dark-app-status-bar-background-color, $lt-app-status-bar-background-color ); 25 | width: 100%; 26 | display: flex; 27 | align-items: center; 28 | 29 | .repl-status-bar-preference { 30 | padding-right: 10px; 31 | font-size: 1.3em; 32 | i.fa-cog { 33 | cursor: pointer; 34 | &:hover { 35 | animation: fa-spin 2s 1 linear; 36 | } 37 | } 38 | } 39 | 40 | .run-help { 41 | padding: 0px 25px; 42 | cursor: default; 43 | color: if($theme == $dark-theme, $dark-app-status-bar-run-help-color, $lt-app-status-bar-run-help-color); 44 | .run-command { 45 | color: if($theme == $dark-theme, $dark-app-status-bar-run-color, $lt-app-status-bar-run-color); 46 | font-size: 0.9em; 47 | font-weight: 900; 48 | font-style: normal; 49 | } 50 | .run { 51 | font-weight: 900; 52 | } 53 | } 54 | 55 | .placeholder { 56 | flex: 1; 57 | } 58 | 59 | .repl-status-bar-commands { 60 | color: if($theme == $dark-theme, $dark-app-status-bar-command-color, $lt-app-status-bar-command-color); 61 | padding: 0 5px; 62 | } 63 | 64 | .repl-status-bar-errors { 65 | color: if($theme == $dark-theme, $dark-app-status-bar-error-color, $lt-app-status-bar-error-color); 66 | padding: 0 5px; 67 | } 68 | 69 | .repl-status-bar-handles { 70 | color: if($theme == $dark-theme, $dark-app-status-bar-handle-color, $lt-app-status-bar-handle-color); 71 | padding: 0 5px; 72 | } 73 | 74 | .repl-status-bar-count, 75 | .repl-status-bar-message { 76 | pointer-events: none; 77 | color: if($theme == $dark-theme, $dark-app-status-bar-message-color, $lt-app-status-bar-message-color); 78 | padding: 0 5px; 79 | } 80 | .repl-status-bar-img { 81 | display: flex; 82 | align-self: center; 83 | } 84 | 85 | .console-notification { 86 | font-size: 0.8em; 87 | color: if($theme == $dark-theme, $dark-app-status-bar-console-notification-color, $lt-app-status-bar-console-notification-color); 88 | -webkit-animation:spin 0.5s linear infinite; 89 | animation:spin 0.5s linear infinite; 90 | -webkit-animation-direction: alternate; 91 | animation-direction: alternate; 92 | } 93 | 94 | .repl-status-cursor-position { 95 | color: if($theme == $dark-theme, $dark-app-status-bar-cursor-pos-color, $lt-app-status-bar-cursor-pos-color); 96 | cursor: default; 97 | } 98 | 99 | .console-release-notification { 100 | cursor: pointer; 101 | color: if($theme == $dark-theme, $dark-app-status-bar-console-release-notification-color, $lt-app-status-bar-console-release-notification-color); 102 | padding: 0 5px; 103 | } 104 | 105 | .repl-status-bar-console { 106 | color: if($theme == $dark-theme, $dark-app-status-bar-console-color, $lt-app-status-bar-console-color); 107 | padding-right: 15px; 108 | cursor: pointer; 109 | 110 | .fa-stack { 111 | .text-danger { 112 | color: if($theme == $dark-theme, $dark-app-entry-status-error-color, $lt-app-entry-status-error-color); 113 | } 114 | 115 | .fa-terminal { 116 | font-weight: 900; 117 | } 118 | } 119 | } 120 | 121 | .repl-status-bar-mode { 122 | color: if($theme == $dark-theme, $dark-app-status-bar-mode-color, $lt-app-status-bar-mode-color); 123 | padding: 0 5px; 124 | 125 | .fa { 126 | padding: 0 3px; 127 | } 128 | } 129 | 130 | .repl-status-bar-lang { 131 | color: if($theme == $dark-theme, $dark-app-status-bar-lang-color, $lt-app-status-bar-lang-color); 132 | padding: 0 5px; 133 | 134 | .fa, .icon-javascript { 135 | padding: 0 3px; 136 | font-size: 1.1em; 137 | } 138 | .icon-javascript { 139 | position: relative; 140 | top: 1px; 141 | } 142 | } 143 | 144 | } 145 | 146 | .repl-status-bar { 147 | height: calc(#{$app-font-size} + 6px); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /stylesheets/repl-suggestions.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | 3 | @mixin suggestions($theme) { 4 | .repl-prompt-suggestion-wrapper { 5 | position: absolute; 6 | background-color: if($theme == $dark-theme, $dark-app-suggestion-background-color, $lt-app-suggestion-background-color); 7 | z-index: 100; 8 | .repl-prompt-suggestion-list { 9 | font-size: 0.9em; 10 | [data-selected='true'] { 11 | background-color: lighten(if($theme == $dark-theme, $dark-app-background-color, $lt-app-background-color), 10%); 12 | } 13 | margin: 0; 14 | // dont change this 15 | max-height: 200px; 16 | 17 | // dont change this 18 | max-width: 300px; 19 | overflow: auto; 20 | border: 2px solid if($theme == $dark-theme, $dark-app-suggestion-border-color, $lt-app-suggestion-border-color); 21 | border-radius: 5px; 22 | padding: 2px 0; 23 | 24 | .repl-prompt-suggestion { 25 | min-height: calc(#{$app-font-size} + 8px); 26 | max-height: calc(#{$app-font-size} + 8px); 27 | display: flex; 28 | padding: 4px 2px; 29 | border-bottom: 1px solid if($theme == $dark-theme, $dark-app-suggestion-entry-border-color, $lt-app-suggestion-entry-border-color); 30 | cursor: pointer; 31 | 32 | .repl-prompt-suggestion-type { 33 | padding: 2px; 34 | font-weight: 900; 35 | border-radius: 100%; 36 | width: $app-font-size; 37 | height: $app-font-size; 38 | min-height: calc(#{$app-font-size} + 8px); 39 | max-height: calc(#{$app-font-size} + 8px); 40 | text-align: center; 41 | display: inline-block; 42 | color: if($theme == $dark-theme, $dark-app-suggestion-type-color, $lt-app-suggestion-type-color); 43 | font-family: if($theme == $dark-theme, $dark-app-suggestion-type-font-family, $lt-app-suggestion-type-font-family); 44 | background-color: if($theme == $dark-theme, $dark-app-suggestion-type-background-color, $lt-app-suggestion-type-background-color); 45 | } 46 | 47 | .repl-prompt-suggestion-text { 48 | width: 100%; 49 | height: 100%; 50 | padding: 2px 8px; 51 | display: inline-block; 52 | text-overflow: ellipsis; 53 | overflow: hidden; 54 | flex: 1; 55 | 56 | .repl-prompt-suggestion-highlight { 57 | white-space: nowrap; 58 | color: if($theme == $dark-theme, $dark-app-suggestion-type-hightlight-color, $lt-app-suggestion-type-hightlight-color); 59 | } 60 | 61 | .repl-prompt-suggestion-expect { 62 | white-space: nowrap; 63 | color: if($theme == $dark-theme, $dark-app-suggestion-type-expect-color, $lt-app-suggestion-type-expect-color); 64 | } 65 | } 66 | 67 | &:last-child { 68 | border-bottom: none; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /stylesheets/repl.scss: -------------------------------------------------------------------------------- 1 | @import 'themes'; 2 | @import 'repl-common'; 3 | @import 'repl-container-left'; 4 | @import 'repl-container-right'; 5 | @import 'repl-status-bar'; 6 | @import 'repl-suggestions'; 7 | @import 'repl-preferences'; 8 | @import 'repl-editor'; 9 | 10 | html{ 11 | height: 100%; 12 | } 13 | 14 | @mixin repl($theme) { 15 | #node-repl-plus { 16 | font-size: 0.9em; 17 | margin: 5px 0px; 18 | @include selectable(); 19 | 20 | .repl-container { 21 | display: flex; 22 | @include containerLeft($theme); 23 | @include containerRight($theme); 24 | @include statusBar($theme); 25 | } 26 | } 27 | 28 | #node-repl-prompt-suggestions { 29 | @include suggestions($theme); 30 | } 31 | 32 | #node-repl-preferences { 33 | @include preferences($theme); 34 | } 35 | @include replEditor($theme); 36 | } 37 | 38 | body { 39 | min-height: 100%; 40 | max-height: 100%; 41 | margin: 0; 42 | font-size: $app-font-size; 43 | font-family: $app-font-family; 44 | -webkit-font-feature-settings: 'liga' 1, 'kern' 1; 45 | font-feature-settings: 'liga' 1, 'kern' 1; 46 | @include notSelectable(); 47 | 48 | &:before { 49 | content: attr(data-watermark-logo); 50 | position: fixed; 51 | top: 40vh; 52 | // 2 characters 53 | left: calc((100vw - 20vh * 2 / 2)/2); 54 | font-size: 20vh; 55 | font-family: 'Josefin Sans', sans-serif; 56 | pointer-events: none; 57 | z-index: 10; 58 | } 59 | &:after { 60 | content: attr(data-watermark-msg); 61 | position: fixed; 62 | top: 56vh; 63 | // 16 characters 64 | left: calc((100vw - 2vh * 16 / 2)/2); 65 | font-size: 2vh; 66 | pointer-events: none; 67 | z-index: 10; 68 | } 69 | } 70 | 71 | body.dark-theme { 72 | background-color: $dark-app-background-color; 73 | color: $dark-app-color; 74 | &:before, &:after { 75 | color: $dark-app-water-mark-color; 76 | } 77 | @include repl($dark-theme); 78 | } 79 | 80 | body.light-theme { 81 | background-color: $lt-app-background-color; 82 | color: $lt-app-color; 83 | &:before, &:after { 84 | color: $lt-app-water-mark-color; 85 | } 86 | @include repl($light-theme); 87 | } 88 | 89 | pre { 90 | @include editor(); 91 | } 92 | 93 | ::selection { 94 | background-color: $app-text-selection-background-color; 95 | color: $app-text-selection-color; 96 | } 97 | 98 | .hide { 99 | display: none !important; 100 | } 101 | -------------------------------------------------------------------------------- /stylesheets/themes.scss: -------------------------------------------------------------------------------- 1 | @import 'configs'; 2 | @import 'dark-theme'; 3 | @import 'light-theme'; 4 | --------------------------------------------------------------------------------