Merge branch 'master' into dom-attributes
This commit is contained in:
commit
ef19f4723e
|
@ -30,6 +30,12 @@
|
|||
"hint": "Disconnect from voice server when Discord closes",
|
||||
"value": false,
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"id": "menu-keybind",
|
||||
"type": "keybind",
|
||||
"text": "Menu keybind",
|
||||
"value": "mod+b"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -45,6 +51,13 @@
|
|||
"hint": "BetterDiscord developer mode",
|
||||
"value": false,
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"id": "ignore-content-manager-errors",
|
||||
"type": "bool",
|
||||
"text": "Ignore content manager errors",
|
||||
"hint": "Only when starting Discord. It gets annoying when you're reloading Discord often and have plugins that are meant to fail.",
|
||||
"value": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import BdCss from './styles/index.scss';
|
|||
import { Events, CssEditor, Globals, ExtModuleManager, PluginManager, ThemeManager, ModuleManager, WebpackModules, Settings, Database } from 'modules';
|
||||
import { ClientLogger as Logger, ClientIPC } from 'common';
|
||||
import { EmoteModule } from 'builtin';
|
||||
const ignoreExternal = true;
|
||||
const ignoreExternal = false;
|
||||
|
||||
class BetterDiscord {
|
||||
|
||||
|
@ -32,6 +32,7 @@ class BetterDiscord {
|
|||
window.emotes = EmoteModule;
|
||||
window.dom = DOM;
|
||||
EmoteModule.observe();
|
||||
|
||||
DOM.injectStyle(BdCss, 'bdmain');
|
||||
Events.on('global-ready', this.globalReady.bind(this));
|
||||
}
|
||||
|
@ -41,25 +42,25 @@ class BetterDiscord {
|
|||
await Database.init();
|
||||
await Settings.loadSettings();
|
||||
await ModuleManager.initModules();
|
||||
Modals.showContentManagerErrors();
|
||||
if (!ignoreExternal) {
|
||||
await ExtModuleManager.loadAllModules(true);
|
||||
await PluginManager.loadAllPlugins(true);
|
||||
await ThemeManager.loadAllThemes(true);
|
||||
}
|
||||
Modals.showContentManagerErrors();
|
||||
if (!Settings.get('core', 'advanced', 'ignore-content-manager-errors'))
|
||||
Modals.showContentManagerErrors();
|
||||
Events.emit('ready');
|
||||
Events.emit('discord-ready');
|
||||
} catch (err) {
|
||||
console.log('FAILED TO LOAD!', err);
|
||||
Logger.err('main', ['FAILED TO LOAD!', err]);
|
||||
}
|
||||
}
|
||||
|
||||
globalReady() {
|
||||
BdUI.initUiEvents();
|
||||
this.vueInstance = BdUI.injectUi();
|
||||
(async () => {
|
||||
this.init();
|
||||
})();
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ import Database from './database';
|
|||
export default class Content {
|
||||
|
||||
constructor(internals) {
|
||||
Utils.deepfreeze(internals.info);
|
||||
Object.freeze(internals.paths);
|
||||
|
||||
this.__internals = internals;
|
||||
|
||||
this.settings.on('setting-updated', event => this.events.emit('setting-updated', event));
|
||||
|
@ -170,3 +173,5 @@ export default class Content {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
Object.freeze(Content.prototype);
|
||||
|
|
|
@ -10,15 +10,16 @@
|
|||
|
||||
import Content from './content';
|
||||
import Globals from './globals';
|
||||
import Database from './database';
|
||||
import { Utils, FileUtils, ClientLogger as Logger } from 'common';
|
||||
import path from 'path';
|
||||
import { Events } from 'modules';
|
||||
import { SettingsSet, ErrorEvent } from 'structs';
|
||||
import { Modals } from 'ui';
|
||||
import Database from './database';
|
||||
import path from 'path';
|
||||
import Combokeys from 'combokeys';
|
||||
|
||||
/**
|
||||
* Base class for external content managing
|
||||
* Base class for managing external content
|
||||
*/
|
||||
export default class {
|
||||
|
||||
|
@ -209,10 +210,12 @@ export default class {
|
|||
userConfig.config.setSaved();
|
||||
|
||||
for (let setting of userConfig.config.findSettings(() => true)) {
|
||||
// This will load custom settings
|
||||
// Setting the content's path on only the live config (and not the default config) ensures that custom settings will not be loaded on the default settings
|
||||
setting.setContentPath(contentPath);
|
||||
}
|
||||
|
||||
Utils.deepfreeze(defaultConfig);
|
||||
Utils.deepfreeze(defaultConfig, object => object instanceof Combokeys);
|
||||
|
||||
const configs = {
|
||||
defaultConfig,
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* BetterDiscord Events Wrapper Module
|
||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
const eventemitters = new WeakMap();
|
||||
|
||||
export default class EventsWrapper {
|
||||
constructor(eventemitter) {
|
||||
eventemitters.set(this, eventemitter);
|
||||
}
|
||||
|
||||
get eventSubs() {
|
||||
return this._eventSubs || (this._eventSubs = []);
|
||||
}
|
||||
|
||||
subscribe(event, callback) {
|
||||
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||
this.eventSubs.push({
|
||||
event,
|
||||
callback
|
||||
});
|
||||
eventemitters.get(this).on(event, callback);
|
||||
}
|
||||
|
||||
unsubscribe(event, callback) {
|
||||
for (let index of this.eventSubs) {
|
||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) return;
|
||||
eventemitters.get(this).off(event, this.eventSubs[index].callback);
|
||||
this.eventSubs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
for (let event of this.eventSubs) {
|
||||
eventemitters.get(this).off(event.event, event.callback);
|
||||
}
|
||||
this.eventSubs.splice(0, this.eventSubs.length);
|
||||
}
|
||||
}
|
|
@ -13,3 +13,4 @@ export { default as SocketProxy } from './socketproxy';
|
|||
export { default as EventHook } from './eventhook';
|
||||
export { default as Permissions } from './permissionmanager';
|
||||
export { default as Database } from './database';
|
||||
export { default as EventsWrapper } from './eventswrapper';
|
||||
|
|
|
@ -11,27 +11,27 @@
|
|||
const PermissionMap = {
|
||||
IDENTIFY: {
|
||||
HEADER: 'Access your account information',
|
||||
BODY: 'Allows :NAME: to read your account information(excluding user token)'
|
||||
BODY: 'Allows :NAME: to read your account information (excluding user token).'
|
||||
},
|
||||
READ_MESSAGES: {
|
||||
HEADER: 'Read all messages',
|
||||
BODY: 'Allows :NAME: to read all messages accessible through your Discord account'
|
||||
BODY: 'Allows :NAME: to read all messages accessible through your Discord account.'
|
||||
},
|
||||
SEND_MESSAGES: {
|
||||
HEADER: 'Send messages',
|
||||
BODY: 'Allows :NAME: to send messages on your behalf'
|
||||
BODY: 'Allows :NAME: to send messages on your behalf.'
|
||||
},
|
||||
DELETE_MESSAGES: {
|
||||
HEADER: 'Delete messages',
|
||||
BODY: 'Allows :NAME: to delete messages on your behalf'
|
||||
BODY: 'Allows :NAME: to delete messages on your behalf.'
|
||||
},
|
||||
EDIT_MESSAGES: {
|
||||
HEADER: 'Edit messages',
|
||||
BODY: 'Allows :NAME: to edit messages on your behalf'
|
||||
BODY: 'Allows :NAME: to edit messages on your behalf.'
|
||||
},
|
||||
JOIN_SERVERS: {
|
||||
HEADER: 'Join servers for you',
|
||||
BODY: 'Allows :NAME: to join servers on your behalf'
|
||||
BODY: 'Allows :NAME: to join servers on your behalf.'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,50 +14,20 @@ import ExtModuleManager from './extmodulemanager';
|
|||
import PluginManager from './pluginmanager';
|
||||
import ThemeManager from './thememanager';
|
||||
import Events from './events';
|
||||
import EventsWrapper from './eventswrapper';
|
||||
import WebpackModules from './webpackmodules';
|
||||
import { SettingsSet, SettingsCategory, Setting, SettingsScheme } from 'structs';
|
||||
import { Modals, DOM } from 'ui';
|
||||
import { BdMenuItems, Modals, DOM } from 'ui';
|
||||
import SettingsModal from '../ui/components/bd/modals/SettingsModal.vue';
|
||||
|
||||
class EventsWrapper {
|
||||
constructor(eventemitter) {
|
||||
this.__eventemitter = eventemitter;
|
||||
}
|
||||
|
||||
get eventSubs() {
|
||||
return this._eventSubs || (this._eventSubs = []);
|
||||
}
|
||||
|
||||
subscribe(event, callback) {
|
||||
if (this.eventSubs.find(e => e.event === event && e.callback === callback)) return;
|
||||
this.eventSubs.push({
|
||||
event,
|
||||
callback
|
||||
});
|
||||
this.__eventemitter.on(event, callback);
|
||||
}
|
||||
|
||||
unsubscribe(event, callback) {
|
||||
for (let index of this.eventSubs) {
|
||||
if (this.eventSubs[index].event !== event || (callback && this.eventSubs[index].callback === callback)) return;
|
||||
this.__eventemitter.off(event, this.eventSubs[index].callback);
|
||||
this.eventSubs.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribeAll() {
|
||||
for (let event of this.eventSubs) {
|
||||
this.__eventemitter.off(event.event, event.callback);
|
||||
}
|
||||
this._eventSubs = [];
|
||||
}
|
||||
}
|
||||
|
||||
export default class PluginApi {
|
||||
|
||||
constructor(pluginInfo) {
|
||||
this.pluginInfo = pluginInfo;
|
||||
this.Events = new EventsWrapper(Events);
|
||||
this._menuItems = undefined;
|
||||
this._injectedStyles = undefined;
|
||||
this._modalStack = undefined;
|
||||
}
|
||||
|
||||
get plugin() {
|
||||
|
@ -153,6 +123,54 @@ export default class PluginApi {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* BdMenu
|
||||
*/
|
||||
|
||||
get BdMenu() {
|
||||
return {
|
||||
BdMenuItems: this.BdMenuItems
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* BdMenuItems
|
||||
*/
|
||||
|
||||
get menuItems() {
|
||||
return this._menuItems || (this._menuItems = []);
|
||||
}
|
||||
addMenuItem(item) {
|
||||
return BdMenuItems.add(item);
|
||||
}
|
||||
addMenuSettingsSet(category, set, text) {
|
||||
const item = BdMenuItems.addSettingsSet(category, set, text);
|
||||
return this.menuItems.push(item);
|
||||
}
|
||||
addMenuVueComponent(category, text, component) {
|
||||
const item = BdMenuItems.addVueComponent(category, text, component);
|
||||
return this.menuItems.push(item);
|
||||
}
|
||||
removeMenuItem(item) {
|
||||
BdMenuItems.remove(item);
|
||||
Utils.removeFromArray(this.menuItems, item);
|
||||
}
|
||||
removeAllMenuItems() {
|
||||
for (let item of this.menuItems)
|
||||
BdMenuItems.remove(item);
|
||||
}
|
||||
get BdMenuItems() {
|
||||
return Object.defineProperty({
|
||||
add: this.addMenuItem.bind(this),
|
||||
addSettingsSet: this.addMenuSettingsSet.bind(this),
|
||||
addVueComponent: this.addMenuVueComponent.bind(this),
|
||||
remove: this.removeMenuItem.bind(this),
|
||||
removeAll: this.removeAllMenuItems.bind(this)
|
||||
}, 'items', {
|
||||
get: () => this.menuItems
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* CssUtils
|
||||
*/
|
||||
|
@ -172,8 +190,8 @@ export default class PluginApi {
|
|||
injectStyle(id, css) {
|
||||
if (id && !css) css = id, id = undefined;
|
||||
this.deleteStyle(id);
|
||||
const styleid = `plugin-${this.getPlugin().id}-${id}`;
|
||||
this.injectedStyles.push(styleid);
|
||||
const styleid = `plugin-${this.plugin.id}-${id}`;
|
||||
this.injectedStyles.push(id);
|
||||
DOM.injectStyle(css, styleid);
|
||||
}
|
||||
async injectSass(id, scss, options) {
|
||||
|
@ -183,7 +201,7 @@ export default class PluginApi {
|
|||
this.injectStyle(id, css, options);
|
||||
}
|
||||
deleteStyle(id) {
|
||||
const styleid = `plugin-${this.getPlugin().id}-${id}`;
|
||||
const styleid = `plugin-${this.plugin.id}-${id}`;
|
||||
this.injectedStyles.splice(this.injectedStyles.indexOf(styleid), 1);
|
||||
DOM.deleteStyle(styleid);
|
||||
}
|
||||
|
@ -216,7 +234,6 @@ export default class PluginApi {
|
|||
}
|
||||
addModal(_modal, component) {
|
||||
const modal = Modals.add(_modal, component);
|
||||
modal.close = force => this.closeModal(modal, force);
|
||||
modal.on('close', () => {
|
||||
let index;
|
||||
while ((index = this.modalStack.findIndex(m => m === modal)) > -1)
|
||||
|
@ -245,16 +262,20 @@ export default class PluginApi {
|
|||
return this.addModal(Modals.settings(settingsset, headertext, options));
|
||||
}
|
||||
get Modals() {
|
||||
return Object.defineProperty(Object.defineProperty({
|
||||
return Object.defineProperties({
|
||||
add: this.addModal.bind(this),
|
||||
close: this.closeModal.bind(this),
|
||||
closeAll: this.closeAllModals.bind(this),
|
||||
closeLast: this.closeLastModal.bind(this),
|
||||
basic: this.basicModal.bind(this),
|
||||
settings: this.settingsModal.bind(this)
|
||||
}, 'stack', {
|
||||
get: () => this.modalStack
|
||||
}), 'baseComponent', {
|
||||
get: () => this.baseModalComponent
|
||||
}, {
|
||||
stack: {
|
||||
get: () => this.modalStack
|
||||
},
|
||||
baseComponent: {
|
||||
get: () => this.baseModalComponent
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -348,3 +369,8 @@ export default class PluginApi {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// Stop plugins from modifying the plugin API for all plugins
|
||||
// Plugins can still modify their own plugin API object
|
||||
Object.freeze(PluginApi);
|
||||
Object.freeze(PluginApi.prototype);
|
||||
|
|
|
@ -99,6 +99,9 @@ export default class extends ContentManager {
|
|||
}
|
||||
|
||||
const plugin = window.require(paths.mainPath)(Plugin, new PluginApi(info), Vendor, deps);
|
||||
if (!(plugin.prototype instanceof Plugin))
|
||||
throw {message: `Plugin ${info.name} did not return a class that extends Plugin.`};
|
||||
|
||||
const instance = new plugin({
|
||||
configs, info, main,
|
||||
paths: {
|
||||
|
|
|
@ -18,7 +18,23 @@ import path from 'path';
|
|||
|
||||
export default new class Settings {
|
||||
constructor() {
|
||||
this.settings = [];
|
||||
this.settings = defaultSettings.map(_set => {
|
||||
const set = new SettingsSet(_set);
|
||||
|
||||
set.on('setting-updated', event => {
|
||||
const { category, setting, value, old_value } = event;
|
||||
Logger.log('Settings', `${set.id}/${category.id}/${setting.id} was changed from ${old_value} to ${value}`);
|
||||
Events.emit('setting-updated', event);
|
||||
Events.emit(`setting-updated-${set.id}_${category.id}_${setting.id}`, event);
|
||||
});
|
||||
|
||||
set.on('settings-updated', async (event) => {
|
||||
await this.saveSettings();
|
||||
Events.emit('settings-updated', event);
|
||||
});
|
||||
|
||||
return set;
|
||||
});
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
|
@ -29,22 +45,12 @@ export default new class Settings {
|
|||
const user_config = await FileUtils.readJsonFromFile(settingsPath);
|
||||
const { settings, scss, css, css_editor_files, scss_error, css_editor_bounds } = user_config;
|
||||
|
||||
this.settings = defaultSettings.map(set => {
|
||||
const newSet = new SettingsSet(set);
|
||||
newSet.merge(settings.find(s => s.id === newSet.id));
|
||||
newSet.setSaved();
|
||||
newSet.on('setting-updated', event => {
|
||||
const { category, setting, value, old_value } = event;
|
||||
Logger.log('Settings', `${newSet.id}/${category.id}/${setting.id} was changed from ${old_value} to ${value}`);
|
||||
Events.emit('setting-updated', event);
|
||||
Events.emit(`setting-updated-${newSet.id}_${category.id}_${setting.id}`, event);
|
||||
});
|
||||
newSet.on('settings-updated', async (event) => {
|
||||
await this.saveSettings();
|
||||
Events.emit('settings-updated', event);
|
||||
});
|
||||
return newSet;
|
||||
});
|
||||
for (let set of this.settings) {
|
||||
const newSet = settings.find(s => s.id === set.id);
|
||||
if (!newSet) continue;
|
||||
set.merge(newSet);
|
||||
set.setSaved();
|
||||
}
|
||||
|
||||
CssEditor.setState(scss, css, css_editor_files, scss_error);
|
||||
CssEditor.editor_bounds = css_editor_bounds || {};
|
||||
|
|
|
@ -46,7 +46,8 @@ export default class Theme extends Content {
|
|||
/**
|
||||
* This is called when the theme is enabled.
|
||||
*/
|
||||
onstart() {
|
||||
async onstart() {
|
||||
if (!this.css) await this.recompile();
|
||||
DOM.injectTheme(this.css, this.id);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,8 +45,10 @@ export default class ThemeManager extends ContentManager {
|
|||
mainPath: paths.mainPath
|
||||
}
|
||||
});
|
||||
if (!instance.css) instance.recompile();
|
||||
else if (instance.enabled) instance.enable();
|
||||
if (instance.enabled) {
|
||||
instance.userConfig.enabled = false;
|
||||
instance.enable();
|
||||
}
|
||||
return instance;
|
||||
} catch (err) {
|
||||
throw err;
|
||||
|
|
|
@ -17,27 +17,29 @@ import DropdownSetting from './types/dropdown';
|
|||
import RadioSetting from './types/radio';
|
||||
import SliderSetting from './types/slider';
|
||||
import ColourSetting from './types/colour';
|
||||
import KeybindSetting from './types/keybind';
|
||||
import FileSetting from './types/file';
|
||||
import ArraySetting from './types/array';
|
||||
import CustomSetting from './types/custom';
|
||||
|
||||
export default class Setting {
|
||||
|
||||
constructor(args) {
|
||||
constructor(args, ...merge) {
|
||||
args = args.args || args;
|
||||
|
||||
if (args.type === 'color') args.type = 'colour';
|
||||
|
||||
if (args.type === 'bool') return new BoolSetting(args);
|
||||
else if (args.type === 'text') return new StringSetting(args);
|
||||
else if (args.type === 'number') return new NumberSetting(args);
|
||||
else if (args.type === 'dropdown') return new DropdownSetting(args);
|
||||
else if (args.type === 'radio') return new RadioSetting(args);
|
||||
else if (args.type === 'slider') return new SliderSetting(args);
|
||||
else if (args.type === 'colour') return new ColourSetting(args);
|
||||
else if (args.type === 'file') return new FileSetting(args);
|
||||
else if (args.type === 'array') return new ArraySetting(args);
|
||||
else if (args.type === 'custom') return new CustomSetting(args);
|
||||
if (args.type === 'bool') return new BoolSetting(args, ...merge);
|
||||
else if (args.type === 'text') return new StringSetting(args, ...merge);
|
||||
else if (args.type === 'number') return new NumberSetting(args, ...merge);
|
||||
else if (args.type === 'dropdown') return new DropdownSetting(args, ...merge);
|
||||
else if (args.type === 'radio') return new RadioSetting(args, ...merge);
|
||||
else if (args.type === 'slider') return new SliderSetting(args, ...merge);
|
||||
else if (args.type === 'colour') return new ColourSetting(args, ...merge);
|
||||
else if (args.type === 'keybind') return new KeybindSetting(args, ...merge);
|
||||
else if (args.type === 'file') return new FileSetting(args, ...merge);
|
||||
else if (args.type === 'array') return new ArraySetting(args, ...merge);
|
||||
else if (args.type === 'custom') return new CustomSetting(args, ...merge);
|
||||
else throw {message: `Setting type ${args.type} unknown`};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* BetterDiscord Keybind Setting Struct
|
||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import Setting from './basesetting';
|
||||
import Combokeys from 'combokeys';
|
||||
|
||||
export default class KeybindSetting extends Setting {
|
||||
|
||||
constructor(args, ...merge) {
|
||||
super(args, ...merge);
|
||||
|
||||
this.combokeys = new Combokeys(document);
|
||||
this.combokeys.bind(this.value, event => this.emit('keybind-activated', event));
|
||||
}
|
||||
|
||||
setValueHook() {
|
||||
this.combokeys.reset();
|
||||
this.combokeys.bind(this.value, event => this.emit('keybind-activated', event));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
@import './main.scss';
|
||||
@import './switches.scss';
|
||||
@import './text.scss';
|
||||
@import './files.scss';
|
||||
@import './dropdowns.scss';
|
||||
@import './radios.scss';
|
||||
@import './sliders.scss';
|
||||
@import './switches.scss';
|
||||
@import './colourpickers.scss';
|
||||
@import './keybinds.scss';
|
||||
@import './arrays.scss';
|
||||
@import './colourpicker.scss';
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
.bd-keybind {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
// width: 180px;
|
||||
margin-top: 10px;
|
||||
background-color: rgba(0,0,0,.1);
|
||||
border: 1px solid rgba(0,0,0,.3);
|
||||
transition: border .15s ease;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
min-height: 40px;
|
||||
|
||||
.bd-keybind-selected {
|
||||
flex: 1 1 auto;
|
||||
color: #f6f6f7;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.bd-keybind-unset {
|
||||
.bd-keybind-selected {
|
||||
color: hsla(240,6%,97%,.3);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.bd-button {
|
||||
border-radius: 2px;
|
||||
margin: -4px -4px -4px 10px;
|
||||
padding: 2px 20px;
|
||||
transition: background-color .2s ease-in-out, color .2s ease-in-out;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
flex: 0 0 auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.bd-active {
|
||||
border-color: $colerr;
|
||||
animation: bd-keybind-pulse 1s infinite;
|
||||
|
||||
.bd-button {
|
||||
color: $colerr;
|
||||
background-color: rgba($colerr, .3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bd-keybind-pulse {
|
||||
0% { box-shadow: 0 0 6px rgba(240,71,71,.3) }
|
||||
50% { box-shadow: 0 0 10px rgba(240,71,71,.6) }
|
||||
100% { box-shadow: 0 0 6px rgba(240,71,71,.3) }
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
.bd-setting-switch,
|
||||
.bd-form-textinput,
|
||||
.bd-form-textarea,
|
||||
.bd-form-fileinput,
|
||||
.bd-form-numberinput,
|
||||
.bd-form-dropdown,
|
||||
.bd-form-radio,
|
||||
.bd-form-numberinput,
|
||||
.bd-form-slider,
|
||||
.bd-form-colourpicker,
|
||||
.bd-setting-switch,
|
||||
.bd-form-keybind,
|
||||
.bd-form-fileinput,
|
||||
.bd-form-settingsarray {
|
||||
.bd-title {
|
||||
display: flex;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* BetterDiscord Menu Module
|
||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { Utils } from 'common';
|
||||
|
||||
let items = 0;
|
||||
|
||||
const BdMenuItems = new class {
|
||||
|
||||
constructor() {
|
||||
window.bdmenu = this;
|
||||
|
||||
this.items = [];
|
||||
|
||||
this.addSettingsSet('Internal', 'core', 'Core');
|
||||
this.addSettingsSet('Internal', 'ui', 'UI');
|
||||
this.addSettingsSet('Internal', 'emotes', 'Emotes');
|
||||
|
||||
this.add({category: 'Internal', contentid: 'css', text: 'CSS Editor'});
|
||||
this.add({category: 'External', contentid: 'plugins', text: 'Plugins'});
|
||||
this.add({category: 'External', contentid: 'themes', text: 'Themes'});
|
||||
}
|
||||
|
||||
add(item) {
|
||||
item.id = items++;
|
||||
item.contentid = item.contentid || (items++ + '');
|
||||
item.active = false;
|
||||
item.hidden = item.hidden || false;
|
||||
item._type = item._type || 'button';
|
||||
|
||||
this.items.push(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
addSettingsSet(category, set, text) {
|
||||
return this.add({
|
||||
category, set,
|
||||
text: text || set.text
|
||||
});
|
||||
}
|
||||
|
||||
addVueComponent(category, text, component) {
|
||||
return this.add({
|
||||
category, text, component
|
||||
});
|
||||
}
|
||||
|
||||
remove(item) {
|
||||
Utils.removeFromArray(this.items, item);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export { BdMenuItems };
|
|
@ -123,4 +123,5 @@ export default class {
|
|||
|
||||
return vueInstance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
<MiClose size="17"/>
|
||||
<span class="bd-x-text">ESC</span>
|
||||
</div>
|
||||
<SidebarItem v-for="item in sidebarItems" :item="item" :key="item.id" :onClick="itemOnClick" />
|
||||
<template v-for="(category, text) in sidebar">
|
||||
<SidebarItem :item="{text, type: 'header'}" />
|
||||
<SidebarItem v-for="item in category" :item="item" :key="item.id" :onClick="itemOnClick" />
|
||||
</template>
|
||||
</Sidebar>
|
||||
<div slot="sidebarfooter" class="bd-info">
|
||||
<span class="bd-vtext">v2.0.0a by Jiiks/JsSucks</span>
|
||||
|
@ -31,19 +34,21 @@
|
|||
</div>
|
||||
</div>
|
||||
<ContentColumn slot="content">
|
||||
<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">
|
||||
<SettingsPanel :settings="set" :schemes="set.schemes" />
|
||||
<div v-for="item in sidebarItems" v-if="activeContent(item.contentid) || animatingContent(item.contentid)" :class="{active: activeContent(item.contentid), animating: animatingContent(item.contentid)}">
|
||||
<template v-if="item.component">
|
||||
<component :is="item.component" :SettingsWrapper="SettingsWrapper" />
|
||||
</template>
|
||||
|
||||
<SettingsWrapper v-if="typeof item.set === 'string'" :headertext="Settings.getSet(item.set).headertext">
|
||||
<SettingsPanel :settings="Settings.getSet(item.set)" :schemes="Settings.getSet(item.set).schemes" />
|
||||
</SettingsWrapper>
|
||||
</div>
|
||||
<div v-if="activeContent('css') || animatingContent('css')" :class="{active: activeContent('css'), animating: animatingContent('css')}">
|
||||
<CssEditorView />
|
||||
</div>
|
||||
<div v-if="activeContent('plugins') || animatingContent('plugins')" :class="{active: activeContent('plugins'), animating: animatingContent('plugins')}">
|
||||
<PluginsView />
|
||||
</div>
|
||||
<div v-if="activeContent('themes') || animatingContent('themes')" :class="{active: activeContent('themes'), animating: animatingContent('themes')}">
|
||||
<ThemesView />
|
||||
<SettingsWrapper v-else-if="item.set" :headertext="item.set.headertext">
|
||||
<SettingsPanel :settings="item.set" :schemes="item.set.schemes" />
|
||||
</SettingsWrapper>
|
||||
|
||||
<CssEditorView v-if="item.contentid === 'css'" />
|
||||
<PluginsView v-if="item.contentid === 'plugins'" />
|
||||
<ThemesView v-if="item.contentid === 'themes'" />
|
||||
</div>
|
||||
</ContentColumn>
|
||||
</SidebarView>
|
||||
|
@ -53,32 +58,22 @@
|
|||
// Imports
|
||||
import { shell } from 'electron';
|
||||
import { Settings } from 'modules';
|
||||
import { BdMenuItems } from 'ui';
|
||||
import { SidebarView, Sidebar, SidebarItem, ContentColumn } from './sidebar';
|
||||
import { SettingsWrapper, SettingsPanel, CssEditorView, PluginsView, ThemesView } from './bd';
|
||||
import { SvgX, MiGithubCircle, MiWeb, MiClose, MiTwitterCircle } from './common';
|
||||
|
||||
// Constants
|
||||
const sidebarItems = [
|
||||
{ text: 'Internal', _type: 'header' },
|
||||
{ id: 0, contentid: "core", text: 'Core', active: false, _type: 'button' },
|
||||
{ id: 1, contentid: "ui", text: 'UI', active: false, _type: 'button' },
|
||||
{ id: 2, contentid: "emotes", text: 'Emotes', active: false, _type: 'button' },
|
||||
{ id: 3, contentid: "css", text: 'CSS Editor', active: false, _type: 'button' },
|
||||
{ text: 'External', _type: 'header' },
|
||||
{ id: 4, contentid: "plugins", text: 'Plugins', active: false, _type: 'button' },
|
||||
{ id: 5, contentid: "themes", text: 'Themes', active: false, _type: 'button' }
|
||||
];
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sidebarItems,
|
||||
BdMenuItems,
|
||||
activeIndex: -1,
|
||||
lastActiveIndex: -1,
|
||||
animating: false,
|
||||
first: true,
|
||||
Settings,
|
||||
timeout: null
|
||||
timeout: null,
|
||||
SettingsWrapper
|
||||
}
|
||||
},
|
||||
props: ['active', 'close'],
|
||||
|
@ -87,6 +82,20 @@
|
|||
SettingsWrapper, SettingsPanel, CssEditorView, PluginsView, ThemesView,
|
||||
MiGithubCircle, MiWeb, MiClose, MiTwitterCircle
|
||||
},
|
||||
computed: {
|
||||
sidebarItems() {
|
||||
return this.BdMenuItems.items;
|
||||
},
|
||||
sidebar() {
|
||||
const categories = {};
|
||||
for (let item of this.sidebarItems) {
|
||||
if (item.hidden) continue;
|
||||
const category = categories[item.category] || (categories[item.category] = []);
|
||||
category.push(item);
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
itemOnClick(id) {
|
||||
if (this.animating || id === this.activeIndex) return;
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
</template>
|
||||
<script>
|
||||
// Imports
|
||||
import { Events } from 'modules';
|
||||
import { Events, Settings } from 'modules';
|
||||
import { Modals } from 'ui';
|
||||
import BdSettings from './BdSettings.vue';
|
||||
|
||||
export default {
|
||||
|
@ -45,11 +46,9 @@
|
|||
hideSettings() { this.active = false },
|
||||
toggleSettings() { this.active = !this.active },
|
||||
keyupListener(e) {
|
||||
if (document.getElementsByClassName('bd-backdrop').length) return;
|
||||
if (this.$refs.settings.activeIndex !== -1 && e.which === 27) return this.$refs.settings.closeContent();
|
||||
if (this.active && e.which === 27) return this.hideSettings();
|
||||
if (!e.metaKey && !e.ctrlKey || e.key !== 'b') return;
|
||||
this.toggleSettings();
|
||||
if (Modals.stack.length || !this.active || e.which !== 27) return;
|
||||
if (this.$refs.settings.activeIndex !== -1) this.$refs.settings.closeContent();
|
||||
else this.hideSettings();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
},
|
||||
|
@ -69,6 +68,9 @@
|
|||
Events.on('update-check-end', e => this.updating = 1);
|
||||
Events.on('updates-available', e => this.updating = 2);
|
||||
window.addEventListener('keyup', this.keyupListener);
|
||||
|
||||
const menuKeybind = Settings.getSetting('core', 'default', 'menu-keybind');
|
||||
menuKeybind.on('keybind-activated', () => this.active = !this.active);
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('keyup', this.keyupListener);
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
// TODO Display error if plugin fails to start/stop
|
||||
try {
|
||||
await plugin.enabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
|
||||
this.$forceUpdate();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
@ -79,7 +78,6 @@
|
|||
async reloadPlugin(plugin) {
|
||||
try {
|
||||
await PluginManager.reloadPlugin(plugin);
|
||||
this.$forceUpdate();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
@ -88,7 +86,6 @@
|
|||
try {
|
||||
if (unload) await PluginManager.unloadPlugin(plugin);
|
||||
else await PluginManager.deletePlugin(plugin);
|
||||
this.$forceUpdate();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
// TODO Display error if theme fails to enable/disable
|
||||
try {
|
||||
await theme.enabled ? ThemeManager.disableTheme(theme) : ThemeManager.enableTheme(theme);
|
||||
this.$forceUpdate();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
@ -80,7 +79,6 @@
|
|||
try {
|
||||
if (reload) await ThemeManager.reloadTheme(theme);
|
||||
else await theme.recompile();
|
||||
this.$forceUpdate();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
@ -89,7 +87,6 @@
|
|||
try {
|
||||
if (unload) await ThemeManager.unloadTheme(theme);
|
||||
else await ThemeManager.deleteTheme(theme);
|
||||
this.$forceUpdate();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* BetterDiscord Setting Keybind Component
|
||||
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||
* All rights reserved.
|
||||
* https://betterdiscord.net
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
<template>
|
||||
<div class="bd-form-keybind">
|
||||
<div class="bd-form-keybind-details">
|
||||
<div class="bd-title">
|
||||
<h3>{{ setting.text }}</h3>
|
||||
</div>
|
||||
<div class="bd-hint">{{ setting.hint }}</div>
|
||||
</div>
|
||||
<div class="bd-keybind" :class="{'bd-active': active, 'bd-disabled': setting.disabled, 'bd-keybind-unset': !setting.value}">
|
||||
<div class="bd-keybind-selected">{{ selected || 'No Keybind Set' }}</div>
|
||||
<button class="bd-button" v-tooltip="`Click to record a new keybind sequence${setting.value ? ' (shift + click to delete the sequence)' : ''}`" @click="$event.shiftKey ? deleteKeybind() : toggleActive(); $event.target.blur()">{{ active ? 'Stop Recording' : setting.value ? 'Edit Keybind' : 'Record Keybind' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shell } from 'electron';
|
||||
import { ClientIPC } from 'common';
|
||||
import Combokeys from 'combokeys';
|
||||
import CombokeysRecord from 'combokeys/plugins/record';
|
||||
|
||||
const combokeys = new Combokeys(document);
|
||||
CombokeysRecord(combokeys);
|
||||
|
||||
const process = window.require('process');
|
||||
const modifierKey = process.platform === 'darwin' ? 'meta' : 'ctrl';
|
||||
|
||||
export default {
|
||||
props: ['setting'],
|
||||
data() {
|
||||
return {
|
||||
active: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selected() {
|
||||
return this.getDisplayString(this.setting.value);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
active(active) {
|
||||
if (active) combokeys.record(this.recorded);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleActive() {
|
||||
if (this.setting.disabled) return;
|
||||
this.active = !this.active;
|
||||
},
|
||||
deleteKeybind() {
|
||||
this.setting.value = '';
|
||||
},
|
||||
recorded(sequence) {
|
||||
if (!this.active) return;
|
||||
this.active = false;
|
||||
this.recordingValue = undefined;
|
||||
this.setting.value = sequence.join(' ');
|
||||
console.log('keypress', sequence);
|
||||
},
|
||||
getDisplayString(value) {
|
||||
if (!value) return;
|
||||
return value.split(' ').map(pattern => {
|
||||
return pattern.toUpperCase().replace(/\+/g, ' + ').replace(/mod/gi, modifierKey).replace(/meta/gi, 'Cmd').replace(/ctrl/gi, 'Ctrl').replace(/alt/gi, 'Alt').replace(/shift/gi, 'Shift');
|
||||
}).join(', ');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -10,17 +10,18 @@
|
|||
|
||||
<template>
|
||||
<div class="bd-form-item" :class="{'bd-form-item-changed': setting.changed, 'bd-disabled': disabled, 'bd-form-item-noheader': !setting.text, 'bd-form-item-fullwidth': setting.fullwidth}">
|
||||
<BoolSetting v-if="setting.type === 'bool'" :setting="setting" :change="change"/>
|
||||
<DropdownSetting v-if="setting.type === 'dropdown'" :setting="setting" :change="change"/>
|
||||
<NumberSetting v-if="setting.type === 'number'" :setting="setting" :change="change"/>
|
||||
<RadioSetting v-if="setting.type === 'radio'" :setting="setting" :change="change"/>
|
||||
<StringSetting v-if="setting.type === 'text' && !setting.multiline" :setting="setting" :change="change"/>
|
||||
<MultilineTextSetting v-if="setting.type === 'text' && setting.multiline" :setting="setting" :change="change"/>
|
||||
<SliderSetting v-if="setting.type === 'slider'" :setting="setting" :change="change"/>
|
||||
<FileSetting v-if="setting.type === 'file'" :setting="setting" :change="change"/>
|
||||
<BoolSetting v-if="setting.type === 'bool'" :setting="setting" :change="change" />
|
||||
<DropdownSetting v-if="setting.type === 'dropdown'" :setting="setting" :change="change" />
|
||||
<NumberSetting v-if="setting.type === 'number'" :setting="setting" :change="change" />
|
||||
<RadioSetting v-if="setting.type === 'radio'" :setting="setting" :change="change" />
|
||||
<StringSetting v-if="setting.type === 'text' && !setting.multiline" :setting="setting" :change="change" />
|
||||
<MultilineTextSetting v-if="setting.type === 'text' && setting.multiline" :setting="setting" />
|
||||
<SliderSetting v-if="setting.type === 'slider'" :setting="setting" :change="change" />
|
||||
<ColourSetting v-if="setting.type === 'colour'" :setting="setting" :change="change" />
|
||||
<KeybindSetting v-if="setting.type === 'keybind'" :setting="setting" />
|
||||
<FileSetting v-if="setting.type === 'file'" :setting="setting" :change="change" />
|
||||
<ArraySetting v-if="setting.type === 'array'" :setting="setting" :change="change" />
|
||||
<CustomSetting v-if="setting.type === 'custom'" :setting="setting" :change="change" />
|
||||
<ColourSetting v-if="setting.type === 'colour'" :setting="setting" :change="change"/>
|
||||
<div class="bd-form-divider"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -33,10 +34,11 @@
|
|||
import StringSetting from './String.vue';
|
||||
import MultilineTextSetting from './Multiline.vue';
|
||||
import SliderSetting from './Slider.vue';
|
||||
import ColourSetting from './Colour.vue';
|
||||
import KeybindSetting from './Keybind.vue';
|
||||
import FileSetting from './File.vue';
|
||||
import ArraySetting from './Array.vue';
|
||||
import CustomSetting from './Custom.vue';
|
||||
import ColourSetting from './Colour.vue';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
@ -50,10 +52,11 @@
|
|||
StringSetting,
|
||||
MultilineTextSetting,
|
||||
SliderSetting,
|
||||
ColourSetting,
|
||||
KeybindSetting,
|
||||
FileSetting,
|
||||
ArraySetting,
|
||||
CustomSetting,
|
||||
ColourSetting
|
||||
CustomSetting
|
||||
},
|
||||
computed: {
|
||||
changed() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export { default as DOM } from './dom';
|
||||
export { default as BdUI } from './bdui';
|
||||
export { default as VueInjector } from './vueinjector';
|
||||
export * from './bdmenu';
|
||||
export { default as Modals } from './modals';
|
||||
export { default as ProfileBadges } from './profilebadges';
|
||||
export { default as Reflection } from './reflection';
|
||||
|
|
|
@ -10,10 +10,15 @@
|
|||
|
||||
import { ClientLogger as Logger } from './logger';
|
||||
|
||||
const patchedFunctions = new WeakMap();
|
||||
|
||||
export class PatchedFunction {
|
||||
constructor(object, methodName, replaceOriginal = true) {
|
||||
if (object[methodName].__monkeyPatch)
|
||||
return object[methodName].__monkeyPatch;
|
||||
if (patchedFunctions.has(object[methodName])) {
|
||||
const patchedFunction = patchedFunctions.get(object[methodName]);
|
||||
if (replaceOriginal) patchedFunction.replaceOriginal();
|
||||
return patchedFunction;
|
||||
}
|
||||
|
||||
this.object = object;
|
||||
this.methodName = methodName;
|
||||
|
@ -25,7 +30,9 @@ export class PatchedFunction {
|
|||
this.replace = function(...args) {
|
||||
patchedFunction.call(this, arguments);
|
||||
};
|
||||
this.replace.__monkeyPatch = this;
|
||||
|
||||
patchedFunctions.set(object[methodName], this);
|
||||
patchedFunctions.set(this.replace, this);
|
||||
|
||||
if (replaceOriginal)
|
||||
this.replaceOriginal();
|
||||
|
|
|
@ -146,12 +146,14 @@ export class Utils {
|
|||
return value;
|
||||
}
|
||||
|
||||
static deepfreeze(object) {
|
||||
static deepfreeze(object, exclude) {
|
||||
if (exclude && exclude(object)) return;
|
||||
|
||||
if (typeof object === 'object' && object !== null) {
|
||||
const properties = Object.getOwnPropertyNames(object);
|
||||
|
||||
for (let property of properties) {
|
||||
this.deepfreeze(object[property]);
|
||||
this.deepfreeze(object[property], exclude);
|
||||
}
|
||||
|
||||
Object.freeze(object);
|
||||
|
@ -159,6 +161,13 @@ export class Utils {
|
|||
|
||||
return object;
|
||||
}
|
||||
|
||||
static removeFromArray(array, item) {
|
||||
let index;
|
||||
while ((index = array.indexOf(item)) > -1)
|
||||
array.splice(index, 1);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileUtils {
|
||||
|
|
|
@ -1323,6 +1323,15 @@
|
|||
"integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
|
||||
"dev": true
|
||||
},
|
||||
"binary-search-tree": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz",
|
||||
"integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"underscore": "1.4.4"
|
||||
}
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
|
||||
|
@ -2023,6 +2032,12 @@
|
|||
"delayed-stream": "1.0.0"
|
||||
}
|
||||
},
|
||||
"combokeys": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/combokeys/-/combokeys-3.0.0.tgz",
|
||||
"integrity": "sha1-lVxZo5Wa9A0mhGq2/DxoJEjnVy4=",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.14.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
|
||||
|
@ -5652,6 +5667,12 @@
|
|||
"integrity": "sha1-YSKJv7PCIOGGpYEYYY1b6MG6sCE=",
|
||||
"dev": true
|
||||
},
|
||||
"immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
|
||||
"dev": true
|
||||
},
|
||||
"imurmurhash": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||
|
@ -6293,6 +6314,15 @@
|
|||
"type-check": "0.3.2"
|
||||
}
|
||||
},
|
||||
"lie": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
|
||||
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"immediate": "3.0.6"
|
||||
}
|
||||
},
|
||||
"liftoff": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
|
||||
|
@ -6347,6 +6377,15 @@
|
|||
"json5": "0.5.1"
|
||||
}
|
||||
},
|
||||
"localforage": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.6.0.tgz",
|
||||
"integrity": "sha1-iwBZvus4dcSBJChsp/2/I9UrjJc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lie": "3.1.1"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
|
||||
|
@ -6979,6 +7018,27 @@
|
|||
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
|
||||
"dev": true
|
||||
},
|
||||
"nedb": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz",
|
||||
"integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "0.2.10",
|
||||
"binary-search-tree": "0.2.5",
|
||||
"localforage": "1.6.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"underscore": "1.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
|
||||
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"neo-async": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.0.tgz",
|
||||
|
@ -10569,6 +10629,12 @@
|
|||
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
|
||||
"dev": true
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz",
|
||||
"integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=",
|
||||
"dev": true
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"codemirror": "^5.23.0",
|
||||
"combokeys": "^3.0.0",
|
||||
"css-loader": "^0.28.9",
|
||||
"electron": "^1.6.15",
|
||||
"electron-rebuild": "^1.7.3",
|
||||
|
|
|
@ -77,6 +77,13 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "keybind-1",
|
||||
"type": "keybind",
|
||||
"value": "mod+.",
|
||||
"text": "Test Keybind Setting 1",
|
||||
"hint": "Test Keybind Setting Hint 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module.exports = (Plugin, { Logger, Settings }) => class extends Plugin {
|
||||
module.exports = (Plugin, { Logger, Settings, Modals, BdMenu: { BdMenuItems }, Api }) => class extends Plugin {
|
||||
async onstart() {
|
||||
this.keybindEvent = this.keybindEvent.bind(this);
|
||||
|
||||
// Some array event examples
|
||||
const arraySetting = this.settings.getSetting('default', 'array-1');
|
||||
Logger.log('Array setting', arraySetting);
|
||||
|
@ -7,8 +9,15 @@ module.exports = (Plugin, { Logger, Settings }) => class extends Plugin {
|
|||
arraySetting.on('item-updated', event => Logger.log('Item', event.item, 'of the array setting was updated', event));
|
||||
arraySetting.on('item-removed', event => Logger.log('Item', event.item, 'removed from the array setting'));
|
||||
|
||||
// Create a new settings set and show it in a modal
|
||||
const set = Settings.createSet({});
|
||||
// Keybind setting examples
|
||||
const keybindSetting = this.settings.getSetting('default', 'keybind-1');
|
||||
Logger.log('Keybind setting', keybindSetting);
|
||||
keybindSetting.on('keybind-activated', this.keybindEvent);
|
||||
|
||||
// Create a new settings set and add it to the menu
|
||||
const set = Settings.createSet({
|
||||
text: this.name
|
||||
});
|
||||
const category = await set.addCategory({ id: 'default' });
|
||||
|
||||
const setting = await category.addSetting({
|
||||
|
@ -33,6 +42,37 @@ module.exports = (Plugin, { Logger, Settings }) => class extends Plugin {
|
|||
set.setSaved();
|
||||
})
|
||||
|
||||
set.showModal('Custom settings panel');
|
||||
const setting2 = await category.addSetting({
|
||||
id: 'setting-2',
|
||||
type: 'text',
|
||||
text: 'Enter some text',
|
||||
fullwidth: true
|
||||
});
|
||||
|
||||
setting2.on('setting-updated', event => Logger.log('Setting 2 was changed to', event.value));
|
||||
|
||||
this.menuItem = BdMenuItems.addSettingsSet('Plugins', set, 'Plugin 4');
|
||||
|
||||
this.menuItem2 = BdMenuItems.addVueComponent('Plugins', 'Also Plugin 4', {
|
||||
template: `<component :is="SettingsWrapper" :headertext="plugin.name + ' custom menu panel'">
|
||||
<p style="margin-top: 0; color: #f6f6f7;">Test</p>
|
||||
</component>`,
|
||||
props: ['SettingsWrapper'],
|
||||
data() { return {
|
||||
Api, plugin: Api.plugin
|
||||
}; }
|
||||
});
|
||||
}
|
||||
|
||||
onstop() {
|
||||
const keybindSetting = this.settings.getSetting('default', 'keybind-1');
|
||||
keybindSetting.off('keybind-activated', this.keybindEvent);
|
||||
|
||||
BdMenuItems.removeAll();
|
||||
}
|
||||
|
||||
keybindEvent(event) {
|
||||
Logger.log('Keybind pressed', event);
|
||||
Modals.basic('Example Plugin 4', 'Test keybind activated.');
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue