diff --git a/client/src/modules/csseditor.js b/client/src/modules/csseditor.js index b77c08f3..c6614417 100644 --- a/client/src/modules/csseditor.js +++ b/client/src/modules/csseditor.js @@ -9,18 +9,70 @@ */ import { ClientIPC } from 'common'; +import Settings from './settings'; import { DOM } from 'ui'; export default class { - static async show() { - const t = await ClientIPC.send('openCssEditor', {}); - ClientIPC.send('setCss', { css: DOM.getStyleCss('bd-customcss') }); + static init() { + ClientIPC.on('bd-get-scss', () => this.sendToEditor('set-scss', { scss: this.scss })); + ClientIPC.on('bd-update-scss', (e, scss) => this.updateScss(scss)); + ClientIPC.on('bd-save-csseditor-bounds', (e, bounds) => this.saveEditorBounds(bounds)); - ClientIPC.on('bd-update-css', this.updateCss); + ClientIPC.on('bd-save-scss', async (e, scss) => { + await this.updateScss(scss); + await this.save(); + }); } - static updateCss(e, css) { + static async show() { + await ClientIPC.send('openCssEditor', this.editor_bounds); + } + + static updateScss(scss, sendSource) { + if (sendSource) + this.sendToEditor('set-scss', { scss }); + + return new Promise((resolve, reject) => { + this.compile(scss).then(css => { + this.css = css; + this._scss = scss; + this.sendToEditor('scss-error', null); + resolve(); + }).catch(err => { + this.sendToEditor('scss-error', err); + reject(err); + }); + }); + } + + static async save() { + Settings.saveSettings(); + } + + static saveEditorBounds(bounds) { + this.editor_bounds = bounds; + Settings.saveSettings(); + } + + static async compile(scss) { + return await ClientIPC.send('bd-compileSass', { data: scss }); + } + + static async sendToEditor(channel, data) { + return await ClientIPC.send('sendToCssEditor', { channel, data }); + } + + static get scss() { + return this._scss || ''; + } + + static set scss(scss) { + this.updateScss(scss, true); + } + + static set css(css) { DOM.injectStyle(css, 'bd-customcss'); } + } diff --git a/client/src/modules/modulemanager.js b/client/src/modules/modulemanager.js index da62132a..cdf51dce 100644 --- a/client/src/modules/modulemanager.js +++ b/client/src/modules/modulemanager.js @@ -10,7 +10,7 @@ /*Module Manager initializes all modules when everything is ready*/ -import { Events, SocketProxy, EventHook } from 'modules'; +import { Events, SocketProxy, EventHook, CssEditor } from 'modules'; import { ProfileBadges } from 'ui'; export default class { @@ -19,7 +19,8 @@ export default class { return this._modules ? this._modules : (this._modules = [ new ProfileBadges(), new SocketProxy(), - new EventHook() + new EventHook(), + CssEditor ]); } diff --git a/client/src/modules/settings.js b/client/src/modules/settings.js index 63de4024..872ce3e4 100644 --- a/client/src/modules/settings.js +++ b/client/src/modules/settings.js @@ -9,7 +9,8 @@ */ import defaultSettings from '../data/user.settings.default'; -import { default as Globals } from './globals'; +import Globals from './globals'; +import CssEditor from './csseditor'; import { FileUtils, ClientLogger as Logger } from 'common'; import path from 'path'; @@ -20,7 +21,7 @@ export default class { const settingsPath = path.resolve(this.dataPath, 'user.settings.json'); const user_config = await FileUtils.readJsonFromFile(settingsPath); - const { settings } = user_config; + const { settings, scss, css_editor_bounds } = user_config; this.settings = defaultSettings; @@ -40,6 +41,9 @@ export default class { } } } + + CssEditor.updateScss(scss, true); + CssEditor.editor_bounds = css_editor_bounds; } catch (err) { // There was an error loading settings // This probably means that the user doesn't have any settings yet @@ -68,7 +72,14 @@ export default class { }; }) }; - }) + }), + scss: CssEditor.scss, + css_editor_bounds: { + width: CssEditor.editor_bounds.width, + height: CssEditor.editor_bounds.height, + x: CssEditor.editor_bounds.x, + y: CssEditor.editor_bounds.y + } }); } catch (err) { // There was an error loading settings diff --git a/client/src/modules/thememanager.js b/client/src/modules/thememanager.js index b8250590..1b9bf2c6 100644 --- a/client/src/modules/thememanager.js +++ b/client/src/modules/thememanager.js @@ -105,7 +105,7 @@ class Theme { let css = ''; if (this.info.type === 'sass') { css = await ClientIPC.send('bd-compileSass', { - scss: ThemeManager.getConfigAsSCSS(this.themeConfig), + data: ThemeManager.getConfigAsSCSS(this.themeConfig), path: this.paths.mainPath.replace(/\\/g, '/') }); console.log(css); diff --git a/core/src/main.js b/core/src/main.js index 10a77bc1..837a42b3 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -45,7 +45,8 @@ console.log(dummyArgs); class Comms { - constructor() { + constructor(bd) { + this.bd = bd; this.initListeners(); } @@ -54,8 +55,11 @@ class Comms { o.reply(Common.Config.config); }); - BDIpc.on('bd-openCssEditor', o => CSSEditor.openEditor(o)); - BDIpc.on('bd-setCss', o => CSSEditor.setCSS(o.args)); + BDIpc.on('bd-sendToDiscord', event => this.bd.windowUtils.send(event.args.channel, event.args.message)); + + BDIpc.on('bd-openCssEditor', o => this.bd.csseditor.openEditor(o)); + // BDIpc.on('bd-setScss', o => this.bd.csseditor.setSCSS(o.args.scss)); + BDIpc.on('bd-sendToCssEditor', o => this.bd.csseditor.send(o.args.channel, o.args.data)); BDIpc.on('bd-readFile', this.readFile); BDIpc.on('bd-readJson', o => this.readFile(o, true)); @@ -67,11 +71,13 @@ class Comms { }); BDIpc.on('bd-compileSass', o => { - const { scss, path } = o.args; - if (!scss && !path) return; - const data = scss ? `${scss} @import '${path}';` : `@import '${path}';`; + if (!o.args.data && !o.args.path) return; + if (o.args.path && o.args.data) { + o.args.data = `${o.args.data} @import '${o.args.path}';`; + o.args.path = undefined; + } - sass.render({ data }, (err, result) => { + sass.render(o.args, (err, result) => { if (err) { o.reply({ err }); return; @@ -100,10 +106,17 @@ class Comms { class BetterDiscord { constructor(args) { + if (BetterDiscord.loaded) { + // Creating two BetterDiscord objects??? + console.log('Creating two BetterDiscord objects???'); + return null; + } + BetterDiscord.loaded = true; + this.injectScripts = this.injectScripts.bind(this); this.ignite = this.ignite.bind(this); Common.Config = new Config(args || dummyArgs); - this.comms = new Comms(); + this.comms = new Comms(this); this.init(); } @@ -111,6 +124,8 @@ class BetterDiscord { const window = await this.waitForWindow(); this.windowUtils = new WindowUtils({ window }); + this.csseditor = new CSSEditor(this); + //Log some events for now //this.windowUtils.webContents.on('did-start-loading', e => this.windowUtils.executeJavascript(`console.info('did-start-loading');`)); //this.windowUtils.webContents.on('did-stop-loading', e => this.windowUtils.executeJavascript(`console.info('did-stop-loading');`)); @@ -128,8 +143,6 @@ class BetterDiscord { this.windowUtils.send('did-navigate-in-page', { event, url, isMainFrame }); }); - BDIpc.on('bd-sendToDiscord', event => this.windowUtils.send(event.args.channel, event.args.message)) - setTimeout(() => { if (__DEV) { this.injectScripts(); } }, 500); @@ -180,4 +193,4 @@ class BetterDiscord { module.exports = { BetterDiscord -} \ No newline at end of file +}; diff --git a/core/src/modules/csseditor.js b/core/src/modules/csseditor.js index 6f90f05f..9355ccd3 100644 --- a/core/src/modules/csseditor.js +++ b/core/src/modules/csseditor.js @@ -12,9 +12,15 @@ const path = require('path'); const { BrowserWindow } = require('electron'); const { Module } = require('./modulebase'); +const { WindowUtils } = require('./utils'); class CSSEditor extends Module { + constructor(bd) { + super(); + this.bd = bd; + } + openEditor(o) { if (this.editor) { if (this.editor.isFocused()) return; @@ -25,33 +31,45 @@ class CSSEditor extends Module { return; } - this.editor = new BrowserWindow(this.options); - this.editor.loadURL(`file://${this.editorPath}/index.html`); - this.editor.open = true; - this.editor.setSheetOffset(33); + const options = this.options; + for (let option in o.args) { + options[option] = o.args[option]; + } - this.editor.webContents.on('close', () => { + this.editor = new BrowserWindow(options); + this.editor.loadURL('about:blank'); + this.editor.setSheetOffset(33); + this.editorUtils = new WindowUtils({ window: this.editor }); + + this.editor.on('close', () => { + this.bd.windowUtils.send('bd-save-csseditor-bounds', this.editor.getBounds()); this.editor = null; }); this.editor.once('ready-to-show', () => { - this.editor.show() + this.editor.show(); }); this.editor.webContents.on('did-finish-load', () => { + this.editorUtils.injectScript(path.join(this.editorPath, 'csseditor.js')); o.reply(true); }); } - setCSS(css) { - this.editor.webContents.send("set-css", css); + setSCSS(scss) { + this.send('set-scss', scss); + } + + send(channel, data) { + if (!this.editor) return; + this.editor.webContents.send(channel, data); } set alwaysOnTop(state) { + if (!this.editor) return; this.editor.setAlwaysOnTop(state); } - //TODO user options from config get options() { return { width: 800, @@ -69,4 +87,4 @@ class CSSEditor extends Module { } -module.exports = { 'CSSEditor': new CSSEditor() }; +module.exports = { CSSEditor }; diff --git a/core/src/modules/utils.js b/core/src/modules/utils.js index b9b748bb..a525322b 100644 --- a/core/src/modules/utils.js +++ b/core/src/modules/utils.js @@ -133,8 +133,12 @@ class WindowUtils extends Module { injectScript(fpath, variable) { console.log(`Injecting: ${fpath}`); - if (variable) this.executeJavascript(`${variable} = require("${fpath}");`); - else this.executeJavascript(`require("${fpath}");`); + + const escaped_path = fpath.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + const escaped_variable = variable ? variable.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : null; + + if (variable) this.executeJavascript(`window["${escaped_variable}"] = require("${escaped_path}");`); + else this.executeJavascript(`require("${escaped_path}");`); } events(event, callback) { @@ -152,4 +156,4 @@ module.exports = { Utils, FileUtils, WindowUtils -} \ No newline at end of file +}; diff --git a/csseditor/src/BDIpc.js b/csseditor/src/BDIpc.js index 36d9a7e5..711a669b 100644 --- a/csseditor/src/BDIpc.js +++ b/csseditor/src/BDIpc.js @@ -1,6 +1,6 @@ const { ipcRenderer } = window.require('electron'); -class BDIpc { +export default class { static on(channel, cb) { ipcRenderer.on(channel, (event, args) => cb(event, args)); @@ -21,6 +21,8 @@ class BDIpc { }); } -} + static sendToDiscord(channel, message) { + this.send('bd-sendToDiscord', { channel, message }); + } -module.exports = { BDIpc }; \ No newline at end of file +} diff --git a/csseditor/src/Editor.vue b/csseditor/src/Editor.vue index 4d1d7e1c..d17f18b8 100644 --- a/csseditor/src/Editor.vue +++ b/csseditor/src/Editor.vue @@ -8,7 +8,7 @@
CSS Editor
- +
@@ -16,19 +16,16 @@
Loading Please Wait...
- +
+
{{ error.formatted }}
- Live Update + Live Update
@@ -45,11 +42,9 @@ import '../../node_modules/codemirror/addon/dialog/dialog.js'; import '../../node_modules/codemirror/addon/hint/show-hint.js'; + import BDIpc from './BDIpc'; + const { remote } = window.require('electron'); - const { BDIpc } = require('./BDIpc'); - function sendToDiscord(channel, message) { - BDIpc.send('bd-sendToDiscord', { channel, message }); - } const ExcludedIntelliSenseTriggerKeys = { '8': 'backspace', @@ -103,49 +98,7 @@ '222': 'quote' }; - /*Methods*/ - function save() { - const css = this.codemirror.getValue(); - sendToDiscord('save-css', css); - } - - function update() { - const css = this.codemirror.getValue(); - sendToDiscord('update-css', css); - } - - function toggleaot() { - this.alwaysOnTop = !this.alwaysOnTop; - remote.getCurrentWindow().setAlwaysOnTop(this.alwaysOnTop); - } - - function close() { - window.close(); - } - - function setCss(css) { - this.loading = false; - this.codemirror.setValue(css || ''); - } - - function cmOnChange(value) { - if(this.liveUpdate) sendToDiscord('update-css', value); - } - - function cmOnKeyUp(editor, event) { - if (event.ctrlKey) return; - if (ExcludedIntelliSenseTriggerKeys[event.keyCode]) return; - cmCommands.autocomplete(editor, null, { completeSingle: false }); - } - - function toggleLiveUpdate(e) { - this.liveUpdate = !this.liveUpdate; - } - - const methods = { save, update, toggleaot, close, setCss, cmOnChange, cmOnKeyUp, toggleLiveUpdate }; - export default { - methods, data() { return { loading: true, @@ -155,7 +108,7 @@ cmOptions: { indentUnit: 4, tabSize: 4, - mode: 'css', + mode: 'text/x-scss', lineNumbers: true, theme: 'material', scrollbarStyle: 'overlay', @@ -165,7 +118,8 @@ dialog: { 'position': 'bottom' } - } + }, + error: null } }, computed: { @@ -176,18 +130,57 @@ return this.$refs.mycm; } }, - mounted: function () { + mounted() { this.codemirror.on('keyup', this.cmOnKeyUp); - BDIpc.on('set-css', (_, data) => { + BDIpc.on('set-scss', (_, data) => { if (data.error) { console.log(data.error); return; } console.log(data); - this.setCss(data.css); + this.setScss(data.scss); }); - BDIpc.send('get-css'); + BDIpc.sendToDiscord('get-scss'); + + BDIpc.on('scss-error', (_, err) => { + this.error = err; + this.$forceUpdate(); + if (err) + console.error('SCSS parse error:', err); + }); + }, + methods: { + save() { + const scss = this.codemirror.getValue(); + BDIpc.sendToDiscord('save-scss', scss); + }, + update() { + const scss = this.codemirror.getValue(); + BDIpc.sendToDiscord('update-scss', scss); + }, + toggleaot() { + this.alwaysOnTop = !this.alwaysOnTop; + remote.getCurrentWindow().setAlwaysOnTop(this.alwaysOnTop); + }, + close() { + window.close(); + }, + setScss(scss) { + this.loading = false; + this.codemirror.setValue(scss || ''); + }, + cmOnChange(value) { + if(this.liveUpdate) BDIpc.sendToDiscord('update-scss', value); + }, + cmOnKeyUp(editor, event) { + if (event.ctrlKey) return; + if (ExcludedIntelliSenseTriggerKeys[event.keyCode]) return; + cmCommands.autocomplete(editor, null, { completeSingle: false }); + }, + toggleLiveUpdate(e) { + this.liveUpdate = !this.liveUpdate; + } } } diff --git a/csseditor/src/index.js b/csseditor/src/index.js index 496518e6..9146bd05 100644 --- a/csseditor/src/index.js +++ b/csseditor/src/index.js @@ -1,15 +1,21 @@ -const styles = require('./styles/index.scss'); +// const styles = require('./styles/index.scss'); import Vue from 'vue'; +import VueCodemirror from 'vue-codemirror'; + import Editor from './Editor.vue'; -import VueCodemirror from 'vue-codemirror' +import styles from './styles/index.scss'; Vue.use(VueCodemirror, {}); window.cmCommands = VueCodemirror.CodeMirror.commands; -new Vue({ - el: '#root', +const mount = document.createElement('div'); +mount.classList.add('container'); +document.body.appendChild(mount); + +const vue = new Vue({ + el: mount, components: { Editor }, template: '' }); diff --git a/csseditor/src/styles/tools.scss b/csseditor/src/styles/tools.scss index d2fc3c16..0c534bc6 100644 --- a/csseditor/src/styles/tools.scss +++ b/csseditor/src/styles/tools.scss @@ -1,3 +1,13 @@ +.parser-error { + padding: 4px 6px; + background: #292b2f; + border-top: 1px solid hsla(218,5%,47%,.3); + color: #d84040; + font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif; + white-space: pre-wrap; + font-size: 12px; +} + .tools { height: 36px; background: #292b2f; @@ -24,16 +34,18 @@ cursor: pointer; border: 0; margin-right: 4px; + flex: 0 0 auto; &:hover { background: #44474e; - color: #FFF; + color: #fff; } } #chkboxLiveUpdate { padding: 3px 10px; line-height: 22px; + flex: 0 0 auto; input[type="checkbox"] { margin: 0 6px 0 0; diff --git a/csseditor/webpack.config.js b/csseditor/webpack.config.js index affb1609..0e7ff99d 100644 --- a/csseditor/webpack.config.js +++ b/csseditor/webpack.config.js @@ -26,4 +26,4 @@ module.exports = { vue$: path.resolve('..', 'node_modules', 'vue', 'dist', 'vue.esm.js') } } -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 01f428c9..74631cef 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "build": "npm run build --prefix client && npm run build --prefix core && npm run build --prefix csseditor", "watch_client": "npm run watch --prefix client", "watch_core": "npm run watch --prefix core", + "watch_csseditor": "npm run watch --prefix csseditor", "lint": "eslint -f unix client/src core/src csseditor/src", "test": "npm run build && npm run lint", "build_node-sass": "node scripts/build-node-sass.js"