diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index ed8cb87d5..d6502e985 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -10,6 +10,8 @@ const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx'); const SnippetBar = require('./snippetbar/snippetbar.jsx'); const MetadataEditor = require('./metadataEditor/metadataEditor.jsx'); +const EDITOR_THEME_KEY = 'HOMEBREWERY-EDITOR-THEME'; + const SNIPPETBAR_HEIGHT = 25; const DEFAULT_STYLE_TEXT = dedent` /*=======--- Example CSS styling ---=======*/ @@ -34,12 +36,14 @@ const Editor = createClass({ onMetaChange : ()=>{}, reportError : ()=>{}, - renderer : 'legacy' + editorTheme : 'default', + renderer : 'legacy' }; }, getInitialState : function() { return { - view : 'text' //'text', 'style', 'meta' + editorTheme : this.props.editorTheme, + view : 'text' //'text', 'style', 'meta' }; }, @@ -51,6 +55,13 @@ const Editor = createClass({ this.updateEditorSize(); this.highlightCustomMarkdown(); window.addEventListener('resize', this.updateEditorSize); + + const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); + if(editorTheme) { + this.setState({ + editorTheme : editorTheme + }); + } }, componentWillUnmount : function() { @@ -255,6 +266,13 @@ const Editor = createClass({ this.refs.codeEditor?.updateSize(); }, + updateEditorTheme : function(newTheme){ + window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); + this.setState({ + editorTheme : newTheme + }); + }, + //Called by CodeEditor after document switch, so Snippetbar can refresh UndoHistory rerenderParent : function (){ this.forceUpdate(); @@ -269,6 +287,7 @@ const Editor = createClass({ view={this.state.view} value={this.props.brew.text} onChange={this.props.onTextChange} + editorTheme={this.state.editorTheme} rerenderParent={this.rerenderParent} /> ; } @@ -281,6 +300,7 @@ const Editor = createClass({ value={this.props.brew.style ?? DEFAULT_STYLE_TEXT} onChange={this.props.onStyleChange} enableFolding={false} + editorTheme={this.state.editorTheme} rerenderParent={this.rerenderParent} /> ; } @@ -324,6 +344,8 @@ const Editor = createClass({ undo={this.undo} redo={this.redo} historySize={this.historySize()} + currentEditorTheme={this.state.editorTheme} + updateEditorTheme={this.updateEditorTheme} cursorPos={this.refs.codeEditor?.getCursorPosition() || {}} /> {this.renderEditor()} diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index 86e523a13..db88b5b0f 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -1,4 +1,4 @@ - +@import 'themes/codeMirror/customEditorStyles.less'; .editor{ position : relative; width : 100%; diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index fee1a5780..246d534a9 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -1,3 +1,4 @@ +/*eslint max-lines: ["warn", {"max": 250, "skipBlankLines": true, "skipComments": true}]*/ require('./snippetbar.less'); const React = require('react'); const createClass = require('create-react-class'); @@ -15,6 +16,8 @@ ThemeSnippets['V3_5eDMG'] = require('themes/V3/5eDMG/snippets.js'); ThemeSnippets['V3_Journal'] = require('themes/V3/Journal/snippets.js'); ThemeSnippets['V3_Blank'] = require('themes/V3/Blank/snippets.js'); +const EditorThemes = require('build/homebrew/codeMirror/editorThemes.json'); + const execute = function(val, props){ if(_.isFunction(val)) return val(props); return val; @@ -24,24 +27,26 @@ const Snippetbar = createClass({ displayName : 'SnippetBar', getDefaultProps : function() { return { - brew : {}, - view : 'text', - onViewChange : ()=>{}, - onInject : ()=>{}, - onToggle : ()=>{}, - showEditButtons : true, - renderer : 'legacy', - undo : ()=>{}, - redo : ()=>{}, - historySize : ()=>{}, - cursorPos : {} + brew : {}, + view : 'text', + onViewChange : ()=>{}, + onInject : ()=>{}, + onToggle : ()=>{}, + showEditButtons : true, + renderer : 'legacy', + undo : ()=>{}, + redo : ()=>{}, + historySize : ()=>{}, + updateEditorTheme : ()=>{}, + cursorPos : {} }; }, getInitialState : function() { return { - renderer : this.props.renderer, - snippets : [] + renderer : this.props.renderer, + themeSelector : false, + snippets : [] }; }, @@ -95,6 +100,31 @@ const Snippetbar = createClass({ this.props.onInject(injectedText); }, + toggleThemeSelector : function(){ + this.setState({ + themeSelector : !this.state.themeSelector + }); + }, + + changeTheme : function(e){ + if(e.target.value == this.props.currentEditorTheme) return; + this.props.updateEditorTheme(e.target.value); + + this.setState({ + showThemeSelector : false, + }); + }, + + renderThemeSelector : function(){ + return
+ +
; + }, + renderSnippetGroups : function(){ const snippets = this.state.snippets.filter((snippetGroup)=>snippetGroup.view === this.props.view); @@ -124,6 +154,12 @@ const Snippetbar = createClass({
+
+ +
+ {this.state.themeSelector && this.renderThemeSelector()} +
this.props.onViewChange('text')}> @@ -196,5 +232,4 @@ const SnippetGroup = createClass({
; }, - }); diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index cb24da105..ed2dcebce 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -46,6 +46,15 @@ color : black; } } + &.editorTheme{ + .tooltipLeft('Editor Themes'); + font-size : 0.75em; + color : black; + &.active{ + color : white; + background-color: black; + } + } &.divider { background: linear-gradient(#000, #000) no-repeat center/1px 100%; width: 5px; @@ -54,6 +63,15 @@ } } } + .themeSelector{ + position: absolute; + left: -65px; + top: 30px; + z-index: 999; + width: 170px; + background-color: black; + border-radius: 5px; + } } .snippetBarButton{ height : @menuHeight; diff --git a/scripts/buildHomebrew.js b/scripts/buildHomebrew.js index 3bf0065d0..e78759168 100644 --- a/scripts/buildHomebrew.js +++ b/scripts/buildHomebrew.js @@ -99,6 +99,24 @@ fs.emptyDirSync('./build'); await fs.copy('./themes/assets', './build/assets'); await fs.copy('./client/icons', './build/icons'); + //v==---------------------------MOVE CM EDITOR THEMES -----------------------------==v// + + editorThemeFiles = fs.readdirSync('./node_modules/codemirror/theme'); + + const editorThemeFile = './themes/codeMirror/editorThemes.json'; + if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile); + const stream = fs.createWriteStream(editorThemeFile, { flags: 'a' }); + stream.write('[\n"default"'); + + for (themeFile of editorThemeFiles) { + stream.write(`,\n"${themeFile.slice(0, -4)}"`); + } + stream.write('\n]\n'); + stream.end(); + + await fs.copy('./node_modules/codemirror/theme', './build/homebrew/cm-themes'); + await fs.copy('./themes/codeMirror', './build/homebrew/codeMirror'); + //v==----------------------------- BUNDLE PACKAGES --------------------------------==v// const bundles = await pack('./client/homebrew/homebrew.jsx', { diff --git a/shared/naturalcrit/codeEditor/codeEditor.jsx b/shared/naturalcrit/codeEditor/codeEditor.jsx index 2b5685e49..e0a2220b4 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.jsx +++ b/shared/naturalcrit/codeEditor/codeEditor.jsx @@ -49,7 +49,8 @@ const CodeEditor = createClass({ value : '', wrap : true, onChange : ()=>{}, - enableFolding : true + enableFolding : true, + editorTheme : 'default' }; }, @@ -91,6 +92,10 @@ const CodeEditor = createClass({ } else { this.codeMirror.setOption('foldOptions', false); } + + if(prevProps.editorTheme !== this.props.editorTheme){ + this.codeMirror.setOption('theme', this.props.editorTheme); + } }, buildEditor : function() { @@ -159,6 +164,7 @@ const CodeEditor = createClass({ autoCloseTags : true, styleActiveLine : true, showTrailingSpace : false, + theme : this.props.editorTheme // specialChars : / /, // specialCharPlaceholder : function(char) { // const el = document.createElement('span'); @@ -406,7 +412,10 @@ const CodeEditor = createClass({ //----------------------// render : function(){ - return
; + return <> + +
+ ; } }); diff --git a/themes/codeMirror/customEditorStyles.less b/themes/codeMirror/customEditorStyles.less new file mode 100644 index 000000000..3318e1305 --- /dev/null +++ b/themes/codeMirror/customEditorStyles.less @@ -0,0 +1,88 @@ +.editor .codeEditor .CodeMirror { + // Themes with dark backgrounds + &.cm-s-3024-night, + &.cm-s-abbott, + &.cm-s-abcdef, + &.cm-s-ambiance, + &.cm-s-ayu-dark, + &.cm-s-ayu-mirage, + &.cm-s-base16-dark, + &.cm-s-bespin, + &.cm-s-blackboard, + &.cm-s-cobalt, + &.cm-s-colorforth, + &.cm-s-darcula, + &.cm-s-dracula, + &.cm-s-duotone-dark, + &.cm-s-erlang-dark, + &.cm-s-gruvbox-dark, + &.cm-s-hopscotch, + &.cm-s-icecoder, + &.cm-s-isotope, + &.cm-s-lesser-dark, + &.cm-s-liquibyte, + &.cm-s-lucario, + &.cm-s-material, + &.cm-s-material-darker, + &.cm-s-material-ocean, + &.cm-s-material-palenight, + &.cm-s-mbo, + &.cm-s-midnight, + &.cm-s-monokai, + &.cm-s-moxer, + &.cm-s-night, + &.cm-s-nord, + &.cm-s-oceanic-next, + &.cm-s-panda-syntax, + &.cm-s-paraiso-dark, + &.cm-s-pastel-on-dark, + &.cm-s-railscasts, + &.cm-s-rubyblue, + &.cm-s-seti, + &.cm-s-shadowfox, + &.cm-s-the-matrix, + &.cm-s-tomorrow-night-bright, + &.cm-s-tomorrow-night-eighties, + &.cm-s-twilight, + &.cm-s-vibrant-ink, + &.cm-s-xq-dark, + &.cm-s-yonce, + &.cm-s-zenburn + { + .CodeMirror-code { + .block:not(.cm-comment) { + color: magenta; + } + .columnSplit { + color: black; + background-color: rgba(35,153,153,0.5); + } + .pageLine { + background-color: rgba(255,255,255,0.75); + & ~ pre.CodeMirror-line { + color: black; + } + } + } + } + // Themes with light backgrounds + &.cm-s-default, + &.cm-s-3024-day, + &.cm-s-ambiance-mobile, + &.cm-s-base16-light, + &.cm-s-duotone-light, + &.cm-s-eclipse, + &.cm-s-elegant, + &.cm-s-juejin, + &.cm-s-neat, + &.cm-s-neo, + &.cm-s-paraiso-lightm + &.cm-s-solarized, + &.cm-s-ssms, + &.cm-s-ttcn, + &.cm-s-xq-light, + &.cm-s-yeti { + // Future styling for themes with light backgrounds + --dummyVar: 'currently unused'; + } +} \ No newline at end of file diff --git a/themes/codeMirror/editorThemes.json b/themes/codeMirror/editorThemes.json new file mode 100644 index 000000000..a4dd74470 --- /dev/null +++ b/themes/codeMirror/editorThemes.json @@ -0,0 +1,68 @@ +[ +"default", +"3024-day", +"3024-night", +"abbott", +"abcdef", +"ambiance-mobile", +"ambiance", +"ayu-dark", +"ayu-mirage", +"base16-dark", +"base16-light", +"bespin", +"blackboard", +"cobalt", +"colorforth", +"darcula", +"dracula", +"duotone-dark", +"duotone-light", +"eclipse", +"elegant", +"erlang-dark", +"gruvbox-dark", +"hopscotch", +"icecoder", +"idea", +"isotope", +"juejin", +"lesser-dark", +"liquibyte", +"lucario", +"material-darker", +"material-ocean", +"material-palenight", +"material", +"mbo", +"mdn-like", +"midnight", +"monokai", +"moxer", +"neat", +"neo", +"night", +"nord", +"oceanic-next", +"panda-syntax", +"paraiso-dark", +"paraiso-light", +"pastel-on-dark", +"railscasts", +"rubyblue", +"seti", +"shadowfox", +"solarized", +"ssms", +"the-matrix", +"tomorrow-night-bright", +"tomorrow-night-eighties", +"ttcn", +"twilight", +"vibrant-ink", +"xq-dark", +"xq-light", +"yeti", +"yonce", +"zenburn" +]