CSS editor improvements
This commit is contained in:
parent
976aecd8f2
commit
91275f4332
|
@ -76,6 +76,32 @@
|
||||||
"headertext": "Emote Settings",
|
"headertext": "Emote Settings",
|
||||||
"settings": []
|
"settings": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "css",
|
||||||
|
"text": "CSS Editor",
|
||||||
|
"hidden": true,
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"category": "default",
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"id": "live-update",
|
||||||
|
"type": "bool",
|
||||||
|
"text": "Live update",
|
||||||
|
"hint": "Automatically recompile custom CSS when typing in the custom CSS editor.",
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "watch-files",
|
||||||
|
"type": "bool",
|
||||||
|
"text": "Watch included files",
|
||||||
|
"hint": "Automatically recompile theme and custom CSS when a file it imports is changed.",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "security",
|
"id": "security",
|
||||||
"text": "Security",
|
"text": "Security",
|
||||||
|
|
|
@ -195,7 +195,7 @@ export default class {
|
||||||
userConfig.enabled = readUserConfig.enabled || false;
|
userConfig.enabled = readUserConfig.enabled || false;
|
||||||
userConfig.config.merge({ settings: readUserConfig.config });
|
userConfig.config.merge({ settings: readUserConfig.config });
|
||||||
userConfig.config.setSaved();
|
userConfig.config.setSaved();
|
||||||
userConfig.data = readUserConfig.data || undefined;
|
userConfig.data = readUserConfig.data || {};
|
||||||
} catch (err) { /*We don't care if this fails it either means that user config doesn't exist or there's something wrong with it so we revert to default config*/
|
} catch (err) { /*We don't care if this fails it either means that user config doesn't exist or there's something wrong with it so we revert to default config*/
|
||||||
console.info(`Failed reading config for ${this.contentType} ${readConfig.info.name} in ${dirName}`);
|
console.info(`Failed reading config for ${this.contentType} ${readConfig.info.name} in ${dirName}`);
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
@ -8,20 +8,33 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ClientIPC } from 'common';
|
import { FileUtils, ClientLogger as Logger, ClientIPC } from 'common';
|
||||||
import Settings from './settings';
|
import Settings from './settings';
|
||||||
import { DOM } from 'ui';
|
import { DOM } from 'ui';
|
||||||
import filewatcher from 'filewatcher';
|
import filewatcher from 'filewatcher';
|
||||||
|
import path from 'path';
|
||||||
|
import electron from 'electron';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom css editor communications
|
* Custom css editor communications
|
||||||
*/
|
*/
|
||||||
export default class {
|
export default new class {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._scss = '';
|
||||||
|
this._css = '';
|
||||||
|
this._error = undefined;
|
||||||
|
this.editor_bounds = undefined;
|
||||||
|
this._files = undefined;
|
||||||
|
this._filewatcher = undefined;
|
||||||
|
this._watchfiles = undefined;
|
||||||
|
this.compiling = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init css editor
|
* Init css editor
|
||||||
*/
|
*/
|
||||||
static init() {
|
init() {
|
||||||
ClientIPC.on('bd-get-scss', () => this.sendToEditor('set-scss', { scss: this.scss }));
|
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-update-scss', (e, scss) => this.updateScss(scss));
|
||||||
ClientIPC.on('bd-save-csseditor-bounds', (e, bounds) => this.saveEditorBounds(bounds));
|
ClientIPC.on('bd-save-csseditor-bounds', (e, bounds) => this.saveEditorBounds(bounds));
|
||||||
|
@ -31,17 +44,25 @@ export default class {
|
||||||
await this.save();
|
await this.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filewatcher = filewatcher();
|
this.liveupdate = Settings.getSetting('css', 'default', 'live-update');
|
||||||
this.filewatcher.on('change', (file, stat) => {
|
this.liveupdate.on('setting-updated', event => {
|
||||||
// Recompile SCSS
|
this.sendToEditor('set-liveupdate', event.value);
|
||||||
this.updateScss(this.scss);
|
});
|
||||||
|
|
||||||
|
ClientIPC.on('bd-get-liveupdate', () => this.sendToEditor('set-liveupdate', this.liveupdate.value));
|
||||||
|
ClientIPC.on('bd-set-liveupdate', (e, value) => this.liveupdate.value = value);
|
||||||
|
|
||||||
|
this.watchfilessetting = Settings.getSetting('css', 'default', 'watch-files');
|
||||||
|
this.watchfilessetting.on('setting-updated', event => {
|
||||||
|
if (event.value) this.watchfiles = this.files;
|
||||||
|
else this.watchfiles = [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show css editor, flashes if already visible
|
* Show css editor, flashes if already visible
|
||||||
*/
|
*/
|
||||||
static async show() {
|
async show() {
|
||||||
await ClientIPC.send('openCssEditor', this.editor_bounds);
|
await ClientIPC.send('openCssEditor', this.editor_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,34 +71,35 @@ export default class {
|
||||||
* @param {String} scss scss to compile
|
* @param {String} scss scss to compile
|
||||||
* @param {bool} sendSource send to css editor instance
|
* @param {bool} sendSource send to css editor instance
|
||||||
*/
|
*/
|
||||||
static updateScss(scss, sendSource) {
|
async updateScss(scss, sendSource) {
|
||||||
if (sendSource)
|
if (sendSource)
|
||||||
this.sendToEditor('set-scss', { scss });
|
this.sendToEditor('set-scss', { scss });
|
||||||
|
|
||||||
if (!scss) {
|
if (!scss) {
|
||||||
this._scss = this.css = '';
|
this._scss = this.css = '';
|
||||||
this.sendToEditor('scss-error', null);
|
this.sendToEditor('scss-error', null);
|
||||||
return Promise.resolve();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
this.compile(scss).then(result => {
|
this.compiling = true;
|
||||||
this.css = result.css.toString();
|
const result = await this.compile(scss);
|
||||||
this._scss = scss;
|
this.css = result.css.toString();
|
||||||
this.files = result.stats.includedFiles;
|
this._scss = scss;
|
||||||
this.sendToEditor('scss-error', null);
|
this.files = result.stats.includedFiles;
|
||||||
resolve();
|
this.error = null;
|
||||||
}).catch(err => {
|
this.compiling = false;
|
||||||
this.sendToEditor('scss-error', err);
|
} catch (err) {
|
||||||
reject(err);
|
this.compiling = false;
|
||||||
});
|
this.error = err;
|
||||||
});
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save css to file
|
* Save css to file
|
||||||
*/
|
*/
|
||||||
static async save() {
|
async save() {
|
||||||
Settings.saveSettings();
|
Settings.saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +107,7 @@ export default class {
|
||||||
* Save current editor bounds
|
* Save current editor bounds
|
||||||
* @param {Rectangle} bounds editor bounds
|
* @param {Rectangle} bounds editor bounds
|
||||||
*/
|
*/
|
||||||
static saveEditorBounds(bounds) {
|
saveEditorBounds(bounds) {
|
||||||
this.editor_bounds = bounds;
|
this.editor_bounds = bounds;
|
||||||
Settings.saveSettings();
|
Settings.saveSettings();
|
||||||
}
|
}
|
||||||
|
@ -94,70 +116,192 @@ export default class {
|
||||||
* Send scss to core for compilation
|
* Send scss to core for compilation
|
||||||
* @param {String} scss scss string
|
* @param {String} scss scss string
|
||||||
*/
|
*/
|
||||||
static async compile(scss) {
|
async compile(scss) {
|
||||||
const result = await ClientIPC.send('bd-compileSass', { data: scss });
|
return await ClientIPC.send('bd-compileSass', {
|
||||||
console.log('Custom CSS SCSS compiler result:', result, '- CSS:', result.css.toString());
|
data: scss,
|
||||||
return result;
|
path: await this.fileExists() ? this.filePath : undefined
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send css to open editor
|
* Recompile the current SCSS
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
async recompile() {
|
||||||
|
return await this.updateScss(this.scss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data to open editor
|
||||||
* @param {any} channel
|
* @param {any} channel
|
||||||
* @param {any} data
|
* @param {any} data
|
||||||
*/
|
*/
|
||||||
static async sendToEditor(channel, data) {
|
async sendToEditor(channel, data) {
|
||||||
return await ClientIPC.send('sendToCssEditor', { channel, data });
|
return await ClientIPC.send('sendToCssEditor', { channel, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an SCSS file in a system editor
|
||||||
|
*/
|
||||||
|
async openSystemEditor() {
|
||||||
|
try {
|
||||||
|
await FileUtils.fileExists(this.filePath);
|
||||||
|
} catch (err) {
|
||||||
|
// File doesn't exist
|
||||||
|
// Create it
|
||||||
|
await FileUtils.writeFile(this.filePath, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.log('CSS Editor', `Opening file ${this.filePath} in the user's default editor.`);
|
||||||
|
|
||||||
|
// For some reason this doesn't work
|
||||||
|
// if (!electron.shell.openItem(this.filePath))
|
||||||
|
if (!electron.shell.openExternal('file://' + this.filePath))
|
||||||
|
throw {message: 'Failed to open system editor.'};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set current state
|
||||||
|
* @param {String} scss Current uncompiled SCSS
|
||||||
|
* @param {String} css Current compiled CSS
|
||||||
|
* @param {String} files Files imported in the SCSS
|
||||||
|
* @param {String} err Current compiler error
|
||||||
|
*/
|
||||||
|
setState(scss, css, files, err) {
|
||||||
|
this._scss = scss;
|
||||||
|
this.sendToEditor('set-scss', { scss });
|
||||||
|
this.css = css;
|
||||||
|
this.files = files;
|
||||||
|
this.error = err;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current uncompiled scss
|
* Current uncompiled scss
|
||||||
*/
|
*/
|
||||||
static get scss() {
|
get scss() {
|
||||||
return this._scss || '';
|
return this._scss || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set current scss
|
* Set current scss
|
||||||
*/
|
*/
|
||||||
static set scss(scss) {
|
set scss(scss) {
|
||||||
this.updateScss(scss, true);
|
this.updateScss(scss, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current compiled css
|
||||||
|
*/
|
||||||
|
get css() {
|
||||||
|
return this._css || '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject compiled css to head
|
* Inject compiled css to head
|
||||||
* {DOM}
|
* {DOM}
|
||||||
*/
|
*/
|
||||||
static set css(css) {
|
set css(css) {
|
||||||
|
this._css = css;
|
||||||
DOM.injectStyle(css, 'bd-customcss');
|
DOM.injectStyle(css, 'bd-customcss');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of files that are being watched for changes.
|
* Current error
|
||||||
* @returns {Array} Files being watched
|
|
||||||
*/
|
*/
|
||||||
static get files() {
|
get error() {
|
||||||
|
return this._error || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set current error
|
||||||
|
* {DOM}
|
||||||
|
*/
|
||||||
|
set error(err) {
|
||||||
|
this._error = err;
|
||||||
|
this.sendToEditor('scss-error', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of files that are imported in custom CSS.
|
||||||
|
* @return {Array} Files being watched
|
||||||
|
*/
|
||||||
|
get files() {
|
||||||
return this._files || (this._files = []);
|
return this._files || (this._files = []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all files that are imported in custom CSS.
|
||||||
|
* @param {Array} files Files to watch
|
||||||
|
*/
|
||||||
|
set files(files) {
|
||||||
|
this._files = files;
|
||||||
|
if (Settings.get('css', 'default', 'watch-files'))
|
||||||
|
this.watchfiles = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A filewatcher instance.
|
||||||
|
*/
|
||||||
|
get filewatcher() {
|
||||||
|
if (this._filewatcher) return this._filewatcher;
|
||||||
|
this._filewatcher = filewatcher();
|
||||||
|
this._filewatcher.on('change', (file, stat) => {
|
||||||
|
// Recompile SCSS
|
||||||
|
this.recompile();
|
||||||
|
});
|
||||||
|
return this._filewatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of files that are being watched for changes.
|
||||||
|
* @return {Array} Files being watched
|
||||||
|
*/
|
||||||
|
get watchfiles() {
|
||||||
|
return this._watchfiles || (this._watchfiles = []);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets all files to be watched.
|
* Sets all files to be watched.
|
||||||
* @param {Array} files Files to watch
|
* @param {Array} files Files to watch
|
||||||
*/
|
*/
|
||||||
static set files(files) {
|
set watchfiles(files) {
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (!this.files.includes(file)) {
|
if (!this.watchfiles.includes(file)) {
|
||||||
this.filewatcher.add(file);
|
this.filewatcher.add(file);
|
||||||
this.files.push(file);
|
this.watchfiles.push(file);
|
||||||
|
Logger.log('CSS Editor', `Watching file ${file} for changes`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let index in this.files) {
|
for (let index in this.watchfiles) {
|
||||||
const file = this.files[index];
|
let file = this.watchfiles[index];
|
||||||
if (!files.includes(file)) {
|
while (file && !files.find(f => f === file)) {
|
||||||
this.filewatcher.remove(file);
|
this.filewatcher.remove(file);
|
||||||
this.files.splice(index, 1);
|
this.watchfiles.splice(index, 1);
|
||||||
|
Logger.log('CSS Editor', `No longer watching file ${file} for changes`);
|
||||||
|
file = this.watchfiles[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path of the file the system editor should save to.
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
get filePath() {
|
||||||
|
return path.join(Settings.dataPath, 'user.scss');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the system editor's file exists.
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
async fileExists() {
|
||||||
|
try {
|
||||||
|
await FileUtils.fileExists(this.filePath);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ export default class ExtModule {
|
||||||
get dirName() { return this.paths.dirName }
|
get dirName() { return this.paths.dirName }
|
||||||
get enabled() { return true }
|
get enabled() { return true }
|
||||||
get config() { return this.userConfig.config || [] }
|
get config() { return this.userConfig.config || [] }
|
||||||
|
get data() { return this.userConfig.data || (this.userConfig.data = {}) }
|
||||||
get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new ExtModuleEvents(this)) }
|
get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new ExtModuleEvents(this)) }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ export default class Plugin {
|
||||||
get settings() { return this.userConfig.config }
|
get settings() { return this.userConfig.config }
|
||||||
get config() { return this.settings.settings }
|
get config() { return this.settings.settings }
|
||||||
get pluginConfig() { return this.config }
|
get pluginConfig() { return this.config }
|
||||||
get data() { return this.userConfig.data }
|
get data() { return this.userConfig.data || (this.userConfig.data = {}) }
|
||||||
get exports() { return this._exports ? this._exports : (this._exports = this.getExports()) }
|
get exports() { return this._exports ? this._exports : (this._exports = this.getExports()) }
|
||||||
get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new PluginEvents(this)) }
|
get events() { return this.EventEmitter ? this.EventEmitter : (this.EventEmitter = new PluginEvents(this)) }
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,9 @@ export default class PluginApi {
|
||||||
getConfigAsSCSS(settingsset) {
|
getConfigAsSCSS(settingsset) {
|
||||||
return ThemeManager.getConfigAsSCSS(settingsset ? settingsset : this.plugin.settings);
|
return ThemeManager.getConfigAsSCSS(settingsset ? settingsset : this.plugin.settings);
|
||||||
}
|
}
|
||||||
|
getConfigAsSCSSMap(settingsset) {
|
||||||
|
return ThemeManager.getConfigAsSCSSMap(settingsset ? settingsset : this.plugin.settings);
|
||||||
|
}
|
||||||
injectStyle(id, css) {
|
injectStyle(id, css) {
|
||||||
if (id && !css) css = id, id = undefined;
|
if (id && !css) css = id, id = undefined;
|
||||||
this.deleteStyle(id);
|
this.deleteStyle(id);
|
||||||
|
@ -132,6 +135,7 @@ export default class PluginApi {
|
||||||
return {
|
return {
|
||||||
compileSass: this.compileSass.bind(this),
|
compileSass: this.compileSass.bind(this),
|
||||||
getConfigAsSCSS: this.getConfigAsSCSS.bind(this),
|
getConfigAsSCSS: this.getConfigAsSCSS.bind(this),
|
||||||
|
getConfigAsSCSSMap: this.getConfigAsSCSSMap.bind(this),
|
||||||
injectStyle: this.injectStyle.bind(this),
|
injectStyle: this.injectStyle.bind(this),
|
||||||
injectSass: this.injectSass.bind(this),
|
injectSass: this.injectSass.bind(this),
|
||||||
deleteStyle: this.deleteStyle.bind(this),
|
deleteStyle: this.deleteStyle.bind(this),
|
||||||
|
|
|
@ -27,7 +27,7 @@ export default new class Settings {
|
||||||
|
|
||||||
const settingsPath = path.resolve(this.dataPath, 'user.settings.json');
|
const settingsPath = path.resolve(this.dataPath, 'user.settings.json');
|
||||||
const user_config = await FileUtils.readJsonFromFile(settingsPath);
|
const user_config = await FileUtils.readJsonFromFile(settingsPath);
|
||||||
const { settings, scss, css_editor_bounds, css_editor_files } = user_config;
|
const { settings, scss, css, css_editor_files, scss_error, css_editor_bounds } = user_config;
|
||||||
|
|
||||||
this.settings = defaultSettings.map(set => {
|
this.settings = defaultSettings.map(set => {
|
||||||
const newSet = new SettingsSet(set);
|
const newSet = new SettingsSet(set);
|
||||||
|
@ -46,9 +46,8 @@ export default new class Settings {
|
||||||
return newSet;
|
return newSet;
|
||||||
});
|
});
|
||||||
|
|
||||||
CssEditor.updateScss(scss, true);
|
CssEditor.setState(scss, css, css_editor_files, scss_error);
|
||||||
CssEditor.editor_bounds = css_editor_bounds || {};
|
CssEditor.editor_bounds = css_editor_bounds || {};
|
||||||
CssEditor.files = css_editor_files || [];
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// There was an error loading settings
|
// There was an error loading settings
|
||||||
// This probably means that the user doesn't have any settings yet
|
// This probably means that the user doesn't have any settings yet
|
||||||
|
@ -64,13 +63,15 @@ export default new class Settings {
|
||||||
await FileUtils.writeJsonToFile(settingsPath, {
|
await FileUtils.writeJsonToFile(settingsPath, {
|
||||||
settings: this.settings.map(set => set.strip()),
|
settings: this.settings.map(set => set.strip()),
|
||||||
scss: CssEditor.scss,
|
scss: CssEditor.scss,
|
||||||
|
css: CssEditor.css,
|
||||||
|
css_editor_files: CssEditor.files,
|
||||||
|
scss_error: CssEditor.error,
|
||||||
css_editor_bounds: {
|
css_editor_bounds: {
|
||||||
width: CssEditor.editor_bounds.width,
|
width: CssEditor.editor_bounds.width,
|
||||||
height: CssEditor.editor_bounds.height,
|
height: CssEditor.editor_bounds.height,
|
||||||
x: CssEditor.editor_bounds.x,
|
x: CssEditor.editor_bounds.x,
|
||||||
y: CssEditor.editor_bounds.y
|
y: CssEditor.editor_bounds.y
|
||||||
},
|
}
|
||||||
css_editor_files: CssEditor.files
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let set of this.getSettings) {
|
for (let set of this.getSettings) {
|
||||||
|
@ -90,6 +91,7 @@ export default new class Settings {
|
||||||
get core() { return this.getSet('core') }
|
get core() { return this.getSet('core') }
|
||||||
get ui() { return this.getSet('ui') }
|
get ui() { return this.getSet('ui') }
|
||||||
get emotes() { return this.getSet('emotes') }
|
get emotes() { return this.getSet('emotes') }
|
||||||
|
get css() { return this.getSet('css') }
|
||||||
get security() { return this.getSet('security') }
|
get security() { return this.getSet('security') }
|
||||||
|
|
||||||
getCategory(set_id, category_id) {
|
getCategory(set_id, category_id) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Settings from './settings';
|
||||||
import ThemeManager from './thememanager';
|
import ThemeManager from './thememanager';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { SettingUpdatedEvent, SettingsUpdatedEvent } from 'structs';
|
import { SettingUpdatedEvent, SettingsUpdatedEvent } from 'structs';
|
||||||
|
@ -47,13 +48,11 @@ export default class Theme {
|
||||||
this.settings.on('settings-updated', event => this.events.emit('settings-updated', event));
|
this.settings.on('settings-updated', event => this.events.emit('settings-updated', event));
|
||||||
this.settings.on('settings-updated', event => this.recompile());
|
this.settings.on('settings-updated', event => this.recompile());
|
||||||
|
|
||||||
this.filewatcher = filewatcher();
|
const watchfiles = Settings.getSetting('css', 'default', 'watch-files');
|
||||||
const files = this.files;
|
if (watchfiles.value) this.watchfiles = this.files;
|
||||||
this.data.files = [];
|
watchfiles.on('setting-updated', event => {
|
||||||
this.files = files;
|
if (event.value) this.watchfiles = this.files;
|
||||||
this.filewatcher.on('change', (file, stat) => {
|
else this.watchfiles = [];
|
||||||
// Recompile SCSS
|
|
||||||
this.recompile();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,34 +165,64 @@ export default class Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of files that are being watched for changes.
|
* An array of files that are imported in custom CSS.
|
||||||
* @returns {Array} Files being watched
|
* @return {Array} Files being watched
|
||||||
*/
|
*/
|
||||||
get files() {
|
get files() {
|
||||||
return this.data.files || (this.data.files = []);
|
return this.data.files || (this.data.files = []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets all files to be watched.
|
* Sets all files that are imported in custom CSS.
|
||||||
* @param {Array} files Files to watch
|
* @param {Array} files Files to watch
|
||||||
*/
|
*/
|
||||||
set files(files) {
|
set files(files) {
|
||||||
if (!files) files = [];
|
this.data.files = files;
|
||||||
|
if (Settings.get('css', 'default', 'watch-files'))
|
||||||
|
this.watchfiles = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A filewatcher instance.
|
||||||
|
*/
|
||||||
|
get filewatcher() {
|
||||||
|
if (this._filewatcher) return this._filewatcher;
|
||||||
|
this._filewatcher = filewatcher();
|
||||||
|
this._filewatcher.on('change', (file, stat) => {
|
||||||
|
// Recompile SCSS
|
||||||
|
this.recompile();
|
||||||
|
});
|
||||||
|
return this._filewatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of files that are being watched for changes.
|
||||||
|
* @return {Array} Files being watched
|
||||||
|
*/
|
||||||
|
get watchfiles() {
|
||||||
|
return this._watchfiles || (this._watchfiles = []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all files to be watched.
|
||||||
|
* @param {Array} files Files to watch
|
||||||
|
*/
|
||||||
|
set watchfiles(files) {
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (!this.files.includes(file)) {
|
if (!this.watchfiles.includes(file)) {
|
||||||
this.filewatcher.add(file);
|
this.filewatcher.add(file);
|
||||||
this.files.push(file);
|
this.watchfiles.push(file);
|
||||||
Logger.log(this.name, `Watching file ${file} for changes`);
|
Logger.log(this.name, `Watching file ${file} for changes`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let index in this.files) {
|
for (let index in this.watchfiles) {
|
||||||
const file = this.files[index];
|
let file = this.watchfiles[index];
|
||||||
if (!files.includes(file)) {
|
while (file && !files.find(f => f === file)) {
|
||||||
this.filewatcher.remove(file);
|
this.filewatcher.remove(file);
|
||||||
this.files.splice(index, 1);
|
this.watchfiles.splice(index, 1);
|
||||||
Logger.log(this.name, `No longer watching file ${file} for changes`);
|
Logger.log(this.name, `No longer watching file ${file} for changes`);
|
||||||
|
file = this.watchfiles[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,10 @@ export default class SettingsSet {
|
||||||
return this.args.headertext || `${this.text} Settings`;
|
return this.args.headertext || `${this.text} Settings`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get hidden() {
|
||||||
|
return this.args.hidden || false;
|
||||||
|
}
|
||||||
|
|
||||||
get categories() {
|
get categories() {
|
||||||
return this.args.settings || [];
|
return this.args.settings || [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,8 @@
|
||||||
|
|
||||||
.bd-hint {
|
.bd-hint {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
color: #72767d;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
margin-bottom: 0;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
border-bottom: 0px solid rgba(114, 118, 126, 0.1);
|
border-bottom: 0px solid rgba(114, 118, 126, 0.1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
.bd-err {
|
||||||
|
color: $colerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-p {
|
||||||
|
color: $coldimwhite;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-hint {
|
||||||
|
@extend .bd-p;
|
||||||
|
color: #72767d;
|
||||||
|
}
|
|
@ -10,3 +10,4 @@
|
||||||
@import './profilebadges.scss';
|
@import './profilebadges.scss';
|
||||||
|
|
||||||
@import './discordoverrides.scss';
|
@import './discordoverrides.scss';
|
||||||
|
@import './helpers.scss';
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ContentColumn slot="content">
|
<ContentColumn slot="content">
|
||||||
<div v-for="set in Settings.settings" v-if="activeContent(set.id) || animatingContent(set.id)" :class="{active: activeContent(set.id), animating: animatingContent(set.id)}">
|
<div v-for="set in Settings.settings" v-if="!set.hidden && activeContent(set.id) || animatingContent(set.id)" :class="{active: activeContent(set.id), animating: animatingContent(set.id)}">
|
||||||
<SettingsWrapper :headertext="set.headertext">
|
<SettingsWrapper :headertext="set.headertext">
|
||||||
<SettingsPanel :settings="set" :schemes="set.schemes" />
|
<SettingsPanel :settings="set" :schemes="set.schemes" />
|
||||||
</SettingsWrapper>
|
</SettingsWrapper>
|
||||||
|
|
|
@ -11,58 +11,97 @@
|
||||||
<template>
|
<template>
|
||||||
<SettingsWrapper headertext="CSS Editor">
|
<SettingsWrapper headertext="CSS Editor">
|
||||||
<div class="bd-css-editor">
|
<div class="bd-css-editor">
|
||||||
|
<div v-if="CssEditor.error" class="bd-form-item">
|
||||||
|
<h5 style="margin-bottom: 10px;">Compiler error</h5>
|
||||||
|
<div class="bd-err bd-pre-wrap"><div class="bd-pre">{{ CssEditor.error.formatted }}</div></div>
|
||||||
|
<div class="bd-form-divider"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="bd-form-item">
|
<div class="bd-form-item">
|
||||||
<h5>Custom Editor</h5>
|
<h5>Custom Editor</h5>
|
||||||
<div class="bd-form-warning">
|
<FormButton v-if="internalEditorIsInstalled" :onClick="openInternalEditor">Open</FormButton>
|
||||||
<div class="bd-text">Custom Editor is not installed!</div>
|
<template v-else>
|
||||||
<FormButton>Install</FormButton>
|
<div class="bd-form-warning">
|
||||||
</div>
|
<div class="bd-text">Custom Editor is not installed!</div>
|
||||||
<span style="color: #FFF; font-size: 12px; font-weight: 700;">*This is displayed if editor is not installed</span>
|
<FormButton>Install</FormButton>
|
||||||
<FormButton :onClick="openInternalEditor">Open</FormButton>
|
</div>
|
||||||
|
<span style="color: #fff; font-size: 12px; font-weight: 700;">* This is displayed if editor is not installed</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-form-divider"></div>
|
<div class="bd-form-divider"></div>
|
||||||
<Setting :setting="liveUpdateSetting" :change="enabled => liveUpdateSetting.value = enabled" />
|
|
||||||
<div class="bd-form-item">
|
<div class="bd-form-item">
|
||||||
<h5>System Editor</h5>
|
<h5>System Editor</h5>
|
||||||
<FormButton>
|
<FormButton :onClick="openSystemEditor">Open</FormButton>
|
||||||
Open
|
<p class="bd-hint">This will open {{ systemEditorPath }} in your system's default editor.</p>
|
||||||
</FormButton>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="bd-form-divider"></div>
|
<div class="bd-form-divider"></div>
|
||||||
<FormButton :onClick="() => {}">Enabled</FormButton>
|
|
||||||
<FormButton :disabled="true"><span>Disabled</span></FormButton>
|
<div class="bd-form-item">
|
||||||
<FormButton :loading="true" />
|
<h5 style="margin-bottom: 10px;">Settings</h5>
|
||||||
|
</div>
|
||||||
|
<SettingsPanel :settings="settingsset" />
|
||||||
|
|
||||||
|
<!-- <Setting :setting="liveUpdateSetting" />
|
||||||
|
<Setting :setting="watchFilesSetting" /> -->
|
||||||
|
|
||||||
|
<FormButton :onClick="recompile" :loading="compiling">Recompile</FormButton>
|
||||||
</div>
|
</div>
|
||||||
</SettingsWrapper>
|
</SettingsWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Imports
|
// Imports
|
||||||
import { CssEditor } from 'modules';
|
import { Settings, CssEditor } from 'modules';
|
||||||
import { SettingsWrapper } from './';
|
import { SettingsWrapper } from './';
|
||||||
import { FormButton } from '../common';
|
import { FormButton } from '../common';
|
||||||
|
import SettingsPanel from './SettingsPanel.vue';
|
||||||
import Setting from './setting/Setting.vue';
|
import Setting from './setting/Setting.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SettingsWrapper,
|
SettingsWrapper,
|
||||||
|
SettingsPanel,
|
||||||
Setting,
|
Setting,
|
||||||
FormButton
|
FormButton
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
liveUpdateSetting: {
|
CssEditor
|
||||||
id: "live-update",
|
};
|
||||||
type: "bool",
|
},
|
||||||
text: "Live Update",
|
computed: {
|
||||||
hint: "Automatically update client css when saved.",
|
error() {
|
||||||
value: true
|
return this.CssEditor.error;
|
||||||
}
|
},
|
||||||
|
compiling() {
|
||||||
|
return this.CssEditor.compiling;
|
||||||
|
},
|
||||||
|
systemEditorPath() {
|
||||||
|
return this.CssEditor.filePath;
|
||||||
|
},
|
||||||
|
liveUpdateSetting() {
|
||||||
|
return Settings.getSetting('css', 'default', 'live-update');
|
||||||
|
},
|
||||||
|
watchFilesSetting() {
|
||||||
|
return Settings.getSetting('css', 'default', 'watch-files');
|
||||||
|
},
|
||||||
|
settingsset() {
|
||||||
|
return Settings.css;
|
||||||
|
},
|
||||||
|
internalEditorIsInstalled() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openInternalEditor() {
|
openInternalEditor() {
|
||||||
CssEditor.show();
|
this.CssEditor.show();
|
||||||
|
},
|
||||||
|
openSystemEditor() {
|
||||||
|
this.CssEditor.openSystemEditor();
|
||||||
|
},
|
||||||
|
recompile() {
|
||||||
|
this.CssEditor.recompile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,33 +116,39 @@ export default class DOM {
|
||||||
return document.querySelector(e);
|
return document.querySelector(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getElements(e) {
|
||||||
|
return document.querySelectorAll(e);
|
||||||
|
}
|
||||||
|
|
||||||
static createElement(tag = 'div', className = null, id = null) {
|
static createElement(tag = 'div', className = null, id = null) {
|
||||||
return new BdNode(tag, className, id);
|
return new BdNode(tag, className, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static deleteStyle(id) {
|
static deleteStyle(id) {
|
||||||
const exists = this.getElement(`bd-styles > #${id}`);
|
const exists = Array.from(this.bdStyles.children).find(e => e.id === id);
|
||||||
if (exists) exists.remove();
|
if (exists) exists.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
static injectStyle(css, id) {
|
static injectStyle(css, id) {
|
||||||
this.deleteStyle(id);
|
const style = Array.from(this.bdStyles.children).find(e => e.id === id) || this.createElement('style', null, id).element;
|
||||||
this.bdStyles.append(this.createStyle(css, id));
|
style.textContent = css;
|
||||||
|
this.bdStyles.append(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getStyleCss(id) {
|
static getStyleCss(id) {
|
||||||
const exists = this.getElement(`bd-styles > #${id}`);
|
const exists = this.bdStyles.children.find(e => e.id === id);
|
||||||
return exists ? exists.textContent : '';
|
return exists ? exists.textContent : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
static deleteTheme(id) {
|
static deleteTheme(id) {
|
||||||
const exists = this.getElement(`bd-themes > #${id}`);
|
const exists = Array.from(this.bdThemes.children).find(e => e.id === id);
|
||||||
if (exists) exists.remove();
|
if (exists) exists.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
static injectTheme(css, id) {
|
static injectTheme(css, id) {
|
||||||
this.deleteTheme(id);
|
const style = Array.from(this.bdThemes.children).find(e => e.id === id) || this.createElement('style', null, id).element;
|
||||||
this.bdThemes.append(this.createStyle(css, id));
|
style.textContent = css;
|
||||||
|
this.bdThemes.append(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
static createStyle(css, id) {
|
static createStyle(css, id) {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<button @click="update">Update</button>
|
<button @click="update">Update</button>
|
||||||
<div class="flex-spacer"></div>
|
<div class="flex-spacer"></div>
|
||||||
<div id="chkboxLiveUpdate">
|
<div id="chkboxLiveUpdate">
|
||||||
<input type="checkbox" @click="toggleLiveUpdate" :checked="liveUpdate" /><span>Live Update</span>
|
<label><input type="checkbox" @click="toggleLiveUpdate" v-model="liveUpdate" /><span>Live Update</span></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -130,8 +130,7 @@
|
||||||
return this.$refs.mycm;
|
return this.$refs.mycm;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
created() {
|
||||||
this.codemirror.on('keyup', this.cmOnKeyUp);
|
|
||||||
BDIpc.on('set-scss', (_, data) => {
|
BDIpc.on('set-scss', (_, data) => {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
console.log(data.error);
|
console.log(data.error);
|
||||||
|
@ -141,14 +140,24 @@
|
||||||
this.setScss(data.scss);
|
this.setScss(data.scss);
|
||||||
});
|
});
|
||||||
|
|
||||||
BDIpc.sendToDiscord('get-scss');
|
|
||||||
|
|
||||||
BDIpc.on('scss-error', (_, err) => {
|
BDIpc.on('scss-error', (_, err) => {
|
||||||
this.error = err;
|
this.error = err;
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
if (err)
|
if (err)
|
||||||
console.error('SCSS parse error:', err);
|
console.error('SCSS parse error:', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
BDIpc.on('set-liveupdate', (e, liveUpdate) => this.liveUpdate = liveUpdate);
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.codemirror.on('keyup', this.cmOnKeyUp);
|
||||||
|
BDIpc.sendToDiscord('get-scss');
|
||||||
|
BDIpc.sendToDiscord('get-liveupdate');
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
liveUpdate(liveUpdate) {
|
||||||
|
BDIpc.sendToDiscord('set-liveupdate', liveUpdate);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
save() {
|
save() {
|
||||||
|
|
|
@ -38,11 +38,10 @@
|
||||||
width: 25px;
|
width: 25px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
/*background: #263238;*/
|
|
||||||
background: #36393f;
|
background: #36393f;
|
||||||
color: #bac9d2;
|
color: #bac9d2;
|
||||||
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
||||||
transition: background-color .2s ease;
|
transition: background-color .2s ease, color .2s ease;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
border: 0;
|
border: 0;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
background: #292b2f;
|
background: #292b2f;
|
||||||
border-top: 1px solid hsla(218,5%,47%,.3);
|
border-top: 1px solid hsla(218,5%,47%,.3);
|
||||||
color: #d84040;
|
color: #d84040;
|
||||||
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
font-family: monospace;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
background: #36393f;
|
background: #36393f;
|
||||||
color: #bac9d2;
|
color: #bac9d2;
|
||||||
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
||||||
transition: background-color .2s ease;
|
transition: background-color .2s ease, color .2s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
|
@ -47,6 +47,10 @@
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
margin: 0 6px 0 0;
|
margin: 0 6px 0 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -57,7 +61,6 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #bac9d2;
|
color: #bac9d2;
|
||||||
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue