Please download the latest version from BetterDiscord.net");
+ this.alert("Not Supported", "BetterDiscord v" + bdConfig.version + " (your version)" + " is not supported by the latest js (" + bbdVersion + ").
Please download the latest version from GitHub");
return;
}
@@ -255,10 +294,6 @@ Core.prototype.init = async function() {
settingsPanel = new V2_SettingsPanel();
settingsPanel.updateSettings();
- // Add check for backwards compatibility
- if (!bdpluginErrors) bdpluginErrors = [];
- if (!bdthemeErrors) bdthemeErrors = [];
-
Utils.log("Loading Plugins");
pluginModule = new PluginModule();
pluginModule.loadPlugins();
@@ -285,7 +320,15 @@ Core.prototype.init = async function() {
// Show loading errors
if (settingsCookie["fork-ps-1"]) {
Utils.log("Collecting Startup Errors");
- this.showStartupErrors();
+ this.showStartupErrors({plugins: bdpluginErrors, themes: bdthemeErrors});
+ }
+
+ if (!DataStore.getBDData("RNMAnnouncement")) {
+ DataStore.setBDData("RNMAnnouncement", true);
+ this.alert("Significant Changes", `
+ The lastest release of BBD has made a lot of improvements including being able to automatically load, unload, and reload plugins and themes.
+ If you had the RestartNoMore plugin, I suggest removing it (or turning off BBD's loader in settings) so things aren't being loaded multiple times.
+ `);
}
};
@@ -306,14 +349,14 @@ Core.prototype.injectExternals = function() {
};
Core.prototype.initSettings = function () {
- bdSettingsStorage.initialize();
- if (!bdSettingsStorage.get("settings")) {
+ DataStore.initialize();
+ if (!DataStore.getSettingGroup("settings")) {
settingsCookie = defaultCookie;
this.saveSettings();
}
else {
this.loadSettings();
- $("`);
- if (settingsCookie["fork-ps-2"]) mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been applied.`);
+ if (settingsCookie["fork-ps-2"] && !reload) mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been applied.`);
};
-ThemeModule.prototype.disableTheme = function (theme) {
+ThemeModule.prototype.disableTheme = function(theme, reload = false) {
themeCookie[theme] = false;
this.saveThemeData();
$(`#${Utils.escapeID(bdthemes[theme].name)}`).remove();
- if (settingsCookie["fork-ps-2"]) mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been removed.`);
+ if (settingsCookie["fork-ps-2"] && !reload) mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been disabled.`);
};
-ThemeModule.prototype.toggleTheme = function (theme) {
+ThemeModule.prototype.toggleTheme = function(theme) {
if (themeCookie[theme]) this.disableTheme(theme);
else this.enableTheme(theme);
};
-ThemeModule.prototype.loadThemeData = function () {
- let saved = bdSettingsStorage.get("themes");
+ThemeModule.prototype.loadTheme = function(filename) {
+ const error = ContentManager.loadContent(filename, "theme");
+ if (error && settingsCookie["fork-ps-2"]) {
+ Utils.err(`${filename} could not be loaded.`, error);
+ return BdApi.showToast(`${filename} could not be loaded. It may not have been loaded.`, {type: "error"});
+ }
+
+ const theme = Object.values(bdthemes).find(p => p.filename == filename);
+ if (settingsCookie["fork-ps-2"]) BdApi.showToast(`${theme.name} v${theme.version} was loaded.`, {type: "success"});
+};
+
+ThemeModule.prototype.unloadTheme = function(theme) {
+ if (themeCookie[theme]) this.disableTheme(theme, true);
+ const error = ContentManager.unloadContent(bdthemes[theme].filename, "theme");
+ delete bdthemes[theme];
+ if (error && settingsCookie["fork-ps-2"]) {
+ Utils.err(`${theme} could not be unloaded. It may have not been loaded yet.`, error);
+ return BdApi.showToast(`${theme} could not be unloaded. It may have not been loaded yet.`, {type: "error"});
+ }
+ if (settingsCookie["fork-ps-2"]) BdApi.showToast(`${theme} was unloaded.`, {type: "success"});
+};
+
+ThemeModule.prototype.reloadTheme = function(theme) {
+ const error = ContentManager.reloadContent(bdthemes[theme].filename, "theme");
+ if (themeCookie[theme]) this.disableTheme(theme, true), this.enableTheme(theme, true);
+ if (error && settingsCookie["fork-ps-2"]) {
+ Utils.err(`${theme} could not be reloaded.`, error);
+ return BdApi.showToast(`${theme} could not be reloaded.`, {type: "error"});
+ }
+ if (settingsCookie["fork-ps-2"]) BdApi.showToast(`${theme} v${bdthemes[theme].version} was reloaded.`, {type: "success"});
+};
+
+ThemeModule.prototype.updateThemeList = function() {
+ const results = ContentManager.loadNewContent("theme");
+ for (const filename of results.added) this.loadTheme(filename);
+ for (const name of results.removed) this.unloadTheme(name);
+};
+
+ThemeModule.prototype.loadThemeData = function() {
+ let saved = DataStore.getSettingGroup("themes");
if (saved) {
themeCookie = saved;
}
};
ThemeModule.prototype.saveThemeData = function () {
- bdSettingsStorage.set("themes", themeCookie);
+ DataStore.setSettingGroup("themes", themeCookie);
};
@@ -1491,8 +1826,10 @@ BdApi.getPlugin = function (name) {
return null;
};
+var betterDiscordIPC = require("electron").ipcRenderer;
//Get ipc for reason
BdApi.getIpc = function () {
+ Utils.warn("[Deprecation Notice] BetterDiscord's IPC has been deprecated and may be removed in future versions.");
return betterDiscordIPC;
};
@@ -1535,17 +1872,21 @@ BdApi.getInternalInstance = function(node) {
// Gets data
BdApi.loadData = function(pluginName, key) {
- return window.bdPluginStorage.get(pluginName, key);
+ return DataStore.getPluginData(pluginName, key);
};
+BdApi.getData = BdApi.loadData;
+
// Sets data
BdApi.saveData = function(pluginName, key, data) {
- return window.bdPluginStorage.set(pluginName, key, data);
+ return DataStore.setPluginData(pluginName, key, data);
};
+BdApi.setData = BdApi.saveData;
+
// Deletes data
BdApi.deleteData = function(pluginName, key) {
- return window.bdPluginStorage.delete(pluginName, key);
+ return DataStore.deletePluginData(pluginName, key);
};
// Patches other functions
@@ -1568,6 +1909,40 @@ BdApi.testJSON = function(data) {
return Utils.testJSON(data);
};
+BdApi.isPluginEnabled = function(name) {
+ return !!pluginCookie[name];
+};
+
+BdApi.isThemeEnabled = function(name) {
+ return !!themeCookie[name];
+};
+
+BdApi.isSettingEnabled = function(id) {
+ return !!settingsCookie[id];
+};
+
+// Gets data
+BdApi.getBDData = function(key) {
+ return DataStore.getBDData(key);
+};
+
+// Sets data
+BdApi.setBDData = function(key, data) {
+ return DataStore.setBDData(key, data);
+};
+
+
+/**
+ *
+ * @constructor
+ * @param {(HTMLElement|jQuery)} node - DOM node to monitor and show the tooltip on
+ * @param {string} tip - string to show in the tooltip
+ * @param {object} options - additional options for the tooltip
+ * @param {string} [options.style=black] - correlates to the discord styling
+ * @param {string} [options.side=top] - can be any of top, right, bottom, left
+ * @param {boolean} [options.preventFlip=false] - prevents moving the tooltip to the opposite side if it is too big or goes offscreen
+ * @param {boolean} [options.disabled=false] - whether the tooltip should be disabled from showing on hover
+ */
/* BetterDiscordApp DevMode JavaScript
* Version: 1.0
@@ -1986,6 +2361,10 @@ class V2 {
get NativeModule() {return BDV2.WebpackModules.findByUniqueProperties(["setBadge"]);}
+ get Tooltips() {return BDV2.WebpackModules.find(m => m.hide && m.show && !m.search && !m.submit && !m.search && !m.activateRagingDemon && !m.dismiss);}
+
+ get KeyGenerator() {return BDV2.WebpackModules.find(m => m.toString && /"binary"/.test(m.toString()));}
+
get reactComponent() {
return this.internal.react.Component;
}
@@ -2316,6 +2695,26 @@ class V2C_SideBar extends BDV2.reactComponent {
}
}
+class V2C_ReloadIcon extends BDV2.reactComponent {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return BDV2.react.createElement("svg", {
+ xmlns: "http://www.w3.org/2000/svg",
+ viewBox: "0 0 24 24",
+ fill: "#dcddde",
+ className: "bd-reload " + this.props.className,
+ onClick: this.props.onClick,
+ style: {width: this.props.size || "24px", height: this.props.size || "24px"}
+ },
+ BDV2.react.createElement("path", {d: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"}),
+ BDV2.react.createElement("path", {fill: "none", d: "M0 0h24v24H0z"})
+ );
+ }
+}
+
class V2C_XSvg extends BDV2.reactComponent {
constructor(props) {
super(props);
@@ -2475,7 +2874,7 @@ class V2C_CssEditorDetached extends BDV2.reactComponent {
}
get css() {
- let _ccss = window.bdStorage.get("bdcustomcss");
+ let _ccss = DataStore.getBDData("bdcustomcss");
let ccss = "";
if (_ccss && _ccss !== "") {
ccss = atob(_ccss);
@@ -2588,7 +2987,7 @@ class V2C_CssEditorDetached extends BDV2.reactComponent {
}
saveCss() {
- window.bdStorage.set("bdcustomcss", btoa(this.editor.session.getValue()));
+ DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue()));
}
}
@@ -2652,7 +3051,7 @@ class V2C_CssEditor extends BDV2.reactComponent {
}
get css() {
- let _ccss = window.bdStorage.get("bdcustomcss");
+ let _ccss = DataStore.getBDData("bdcustomcss");
let ccss = "";
if (_ccss && _ccss !== "") {
ccss = atob(_ccss);
@@ -2773,7 +3172,7 @@ class V2C_CssEditor extends BDV2.reactComponent {
}
saveCss() {
- window.bdStorage.set("bdcustomcss", btoa(this.editor.session.getValue()));
+ DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue()));
}
detach() {
@@ -2857,12 +3256,15 @@ class V2C_PluginCard extends BDV2.reactComponent {
self.setInitialState();
self.hasSettings = typeof self.props.plugin.getSettingsPanel === "function";
self.settingsPanel = "";
+
+ this.reload = this.reload.bind(this);
}
setInitialState() {
this.state = {
checked: pluginCookie[this.props.plugin.getName()],
- settings: false
+ settings: false,
+ reloads: 0
};
}
@@ -2892,6 +3294,13 @@ class V2C_PluginCard extends BDV2.reactComponent {
}, 300);
}
}
+
+ reload() {
+ const plugin = this.props.plugin.getName();
+ pluginModule.reloadPlugin(plugin);
+ this.props.plugin = bdplugins[plugin].plugin;
+ this.setState({reloads: this.state.reloads + 1});
+ }
render() {
let self = this;
@@ -2902,7 +3311,6 @@ class V2C_PluginCard extends BDV2.reactComponent {
let version = plugin.getVersion();
let website = bdplugins[name].website;
let source = bdplugins[name].source;
- //let { settingsPanel } = this;
if (this.state.settings) {
try { self.settingsPanel = plugin.getSettingsPanel(); }
@@ -2929,9 +3337,12 @@ class V2C_PluginCard extends BDV2.reactComponent {
" by ",
BDV2.react.createElement("span", {className: "bda-author"}, author)
),
- BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
- BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}),
- BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"})
+ BDV2.react.createElement("div", {className: "bda-controls"},
+ !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}),
+ BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
+ BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}),
+ BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"})
+ )
)
),
BDV2.react.createElement("div", {className: "bda-description-wrap scroller-wrap fade"},
@@ -2965,14 +3376,26 @@ class V2C_ThemeCard extends BDV2.reactComponent {
super(props);
this.setInitialState();
this.onChange = this.onChange.bind(this);
+ this.reload = this.reload.bind(this);
}
setInitialState() {
this.state = {
- checked: themeCookie[this.props.theme.name]
+ checked: themeCookie[this.props.theme.name],
+ reloads: 0
};
}
+ reload() {
+ const theme = this.props.theme.name;
+ const error = themeModule.reloadTheme(theme);
+ if (error) mainCore.showToast(`Could not reload ${bdthemes[theme].name}. Check console for details.`, {type: "error"});
+ else mainCore.showToast(`${bdthemes[theme].name} v${bdthemes[theme].version} has been reloaded.`, {type: "success"});
+ // this.setState(this.state);
+ this.props.theme = bdthemes[theme];
+ this.setState({reloads: this.state.reloads + 1});
+ }
+
render() {
let {theme} = this.props;
let name = theme.name;
@@ -2991,9 +3414,12 @@ class V2C_ThemeCard extends BDV2.reactComponent {
" by ",
BDV2.react.createElement("span", {className: "bda-author"}, author)
),
- BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
- BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}),
- BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"})
+ BDV2.react.createElement("div", {className: "bda-controls"},
+ !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload"}), {className: "bd-reload-card", onClick: this.reload}),
+ BDV2.react.createElement("label", {className: "ui-switch-wrapper ui-flex-child", style: {flex: "0 0 auto"}},
+ BDV2.react.createElement("input", {checked: this.state.checked, onChange: this.onChange, className: "ui-switch-checkbox", type: "checkbox"}),
+ BDV2.react.createElement("div", {className: this.state.checked ? "ui-switch checked" : "ui-switch"})
+ )
)
),
BDV2.react.createElement("div", {className: "bda-description-wrap scroller-wrap fade"},
@@ -3068,6 +3494,9 @@ class V2Components {
static get ContentColumn() {
return V2C_ContentColumn;
}
+ static get ReloadIcon() {
+ return V2C_ReloadIcon;
+ }
static get XSvg() {
return V2C_XSvg;
}
@@ -3080,6 +3509,68 @@ class V2Components {
static get ServerCard() {
return V2C_ServerCard;
}
+
+ static TooltipWrap(Component, options) {
+
+ const {style = "black", side = "top", text = ""} = options;
+ const id = BDV2.KeyGenerator();
+
+ return class extends BDV2.reactComponent {
+ constructor(props) {
+ super(props);
+ this.onMouseEnter = this.onMouseEnter.bind(this);
+ this.onMouseLeave = this.onMouseLeave.bind(this);
+ }
+
+ componentDidMount() {
+ this.node = BDV2.reactDom.findDOMNode(this);
+ this.node.addEventListener("mouseenter", this.onMouseEnter);
+ this.node.addEventListener("mouseleave", this.onMouseLeave);
+ }
+
+ componentWillUnmount() {
+ this.node.removeEventListener("mouseenter", this.onMouseEnter);
+ this.node.removeEventListener("mouseleave", this.onMouseLeave);
+ }
+
+ onMouseEnter() {
+ const {left, top, width, height} = this.node.getBoundingClientRect();
+ BDV2.Tooltips.show(id, {
+ position: side,
+ text: text,
+ color: style,
+ targetWidth: width,
+ targetHeight: height,
+ windowWidth: Utils.screenWidth,
+ windowHeight: Utils.screenHeight,
+ x: left,
+ y: top
+ });
+
+ const observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ const nodes = Array.from(mutation.removedNodes);
+ const directMatch = nodes.indexOf(this.node) > -1;
+ const parentMatch = nodes.some(parent => parent.contains(this.node));
+ if (directMatch || parentMatch) {
+ this.onMouseLeave();
+ observer.disconnect();
+ }
+ });
+ });
+
+ observer.observe(document.body, {subtree: true, childList: true});
+ }
+
+ onMouseLeave() {
+ BDV2.Tooltips.hide(id);
+ }
+
+ render() {
+ return BDV2.react.createElement(Component, this.props);
+ }
+ };
+ }
}
class V2_SettingsPanel_Sidebar {
@@ -3301,6 +3792,15 @@ class V2_SettingsPanel {
if (_c["fork-ps-4"]) classNormalizer.start();
else classNormalizer.stop();
+
+ if (_c["fork-ps-5"]) {
+ ContentManager.watchContent("plugin");
+ ContentManager.watchContent("theme");
+ }
+ else {
+ ContentManager.unwatchContent("plugin");
+ ContentManager.unwatchContent("theme");
+ }
if (_c["bda-gs-8"]) {
@@ -3352,22 +3852,30 @@ class V2_SettingsPanel {
}
get pluginsComponent() {
- let plugins = Object.keys(bdplugins).reduce((arr, key) => {
+ let plugins = Object.keys(bdplugins).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reduce((arr, key) => {
arr.push(BDV2.react.createElement(V2Components.PluginCard, {key: key, plugin: bdplugins[key].plugin}));return arr;
}, []);
let list = BDV2.react.createElement(V2Components.List, {key: "plugin-list", className: "bda-slist", children: plugins});
- let pfBtn = BDV2.react.createElement("button", {key: "folder-button", className: "bd-pfbtn", onClick: () => { betterDiscordIPC.send("asynchronous-message", {arg: "opendir", path: "plugindir"}); }}, "Open Plugin Folder");
- let contentColumn = BDV2.react.createElement(V2Components.ContentColumn, {key: "pcolumn", title: "Plugins", children: [pfBtn, list]});
+ let refreshIcon = !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload Plugin List"}), {className: "bd-reload-header", size: "18px", onClick: async () => {
+ pluginModule.updatePluginList();
+ this.sideBarOnClick("plugins");
+ }});
+ let pfBtn = BDV2.react.createElement("button", {key: "folder-button", className: "bd-pfbtn", onClick: () => { require("electron").shell.openItem(ContentManager.pluginsFolder); }}, "Open Plugin Folder");
+ let contentColumn = BDV2.react.createElement(V2Components.ContentColumn, {key: "pcolumn", title: "Plugins", children: [refreshIcon, pfBtn, list]});
return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [contentColumn, BDV2.react.createElement(V2Components.Tools, {key: "tools"})]});
}
get themesComponent() {
- let themes = Object.keys(bdthemes).reduce((arr, key) => {
+ let themes = Object.keys(bdthemes).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).reduce((arr, key) => {
arr.push(BDV2.react.createElement(V2Components.ThemeCard, {key: key, theme: bdthemes[key]}));return arr;
}, []);
let list = BDV2.react.createElement(V2Components.List, {key: "theme-list", className: "bda-slist", children: themes});
- let tfBtn = BDV2.react.createElement("button", {key: "folder-button", className: "bd-pfbtn", onClick: () => { betterDiscordIPC.send("asynchronous-message", {arg: "opendir", path: "themedir"}); }}, "Open Theme Folder");
- let contentColumn = BDV2.react.createElement(V2Components.ContentColumn, {key: "tcolumn", title: "Themes", children: [tfBtn, list]});
+ let refreshIcon = !settingsCookie["fork-ps-5"] && BDV2.react.createElement(V2Components.TooltipWrap(V2Components.ReloadIcon, {color: "black", side: "top", text: "Reload Theme List"}), {className: "bd-reload-header", size: "18px", onClick: async () => {
+ themeModule.updateThemeList();
+ this.sideBarOnClick("themes");
+ }});
+ let tfBtn = BDV2.react.createElement("button", {key: "folder-button", className: "bd-pfbtn", onClick: () => { require("electron").shell.openItem(ContentManager.themesFolder); }}, "Open Theme Folder");
+ let contentColumn = BDV2.react.createElement(V2Components.ContentColumn, {key: "tcolumn", title: "Themes", children: [refreshIcon, tfBtn, list]});
return BDV2.react.createElement(V2Components.Scroller, {contentColumn: true, fade: true, dark: true, children: [contentColumn, BDV2.react.createElement(V2Components.Tools, {key: "tools"})]});
}
diff --git a/js/main.min.js b/js/main.min.js
index 8b8768c2..5be0924a 100644
--- a/js/main.min.js
+++ b/js/main.min.js
@@ -1,17 +1,20 @@
-(function(){let t=window.require("fs"),a=window.require("process"),o=a.platform,d=("win32"===o?a.env.APPDATA:"darwin"===o?a.env.HOME+"/Library/Preferences":process.env.HOME+"/.config")+"/BetterDiscord/",l="localStorage.json",u={};if(t.existsSync(`${d}${l}`))try{u=JSON.parse(t.readFileSync(`${d}${l}`))}catch(b){console.log(b)}else if(t.existsSync(l))try{u=JSON.parse(t.readFileSync(l))}catch(b){console.log(b)}var h=u;h.setItem=function(b,f){h[b]=f,this.save()},h.getItem=function(b){return h[b]||null},h.save=function(){t.writeFileSync(`${d}${l}`,JSON.stringify(this),null,4)};var g=new Proxy(h,{set:function(b,f,y){h[f]=y,h.save()},get:function(b,f){return h[f]||null}});window.localStorage=g})(),(()=>{let t=document.createElement("div");t.className="bd-loaderv2",t.title="BetterDiscord is loading...",document.body.appendChild(t)})();var betterDiscordIPC=require("electron").ipcRenderer;window.bdStorage={},window.bdStorage.get=function(t){return betterDiscordIPC.sendSync("synchronous-message",{arg:"storage",cmd:"get","var":t})},window.bdStorage.set=function(t,a){betterDiscordIPC.sendSync("synchronous-message",{arg:"storage",cmd:"set","var":t,data:a})},window.bdPluginStorage={},window.bdPluginStorage.get=function(t,a){return betterDiscordIPC.sendSync("synchronous-message",{arg:"pluginstorage",cmd:"get",pn:t,"var":a})},window.bdPluginStorage.set=function(t,a,o){return"undefined"==typeof o?Utils.warn("Trying to set undefined value in plugin "+t):void betterDiscordIPC.sendSync("synchronous-message",{arg:"pluginstorage",cmd:"set",pn:t,"var":a,data:o})},window.bdPluginStorage.delete=function(t,a){betterDiscordIPC.sendSync("synchronous-message",{arg:"pluginstorage",cmd:"delete",pn:t,"var":a})};var bdSettings={},bdSettingsStorage={},releaseChannel=DiscordNative.globals.releaseChannel;bdSettingsStorage.initialize=function(){let t=require("fs"),a={stable:{},canary:{},ptb:{}};if(t.existsSync(bdConfig.dataPath+"/bdsettings.json"))try{a=JSON.parse(t.readFileSync(bdConfig.dataPath+"/bdsettings.json")),a.hasOwnProperty("settings")&&(a={[releaseChannel]:a})}catch(o){a={stable:{},canary:{},ptb:{}}}bdSettings=a?a:{stable:{},canary:{},ptb:{}}},bdSettingsStorage.get=function(t){return bdSettings[releaseChannel][t]?bdSettings[releaseChannel][t]:null},bdSettingsStorage.set=function(t,a){let o=require("fs");bdSettings[releaseChannel][t]=a;try{return o.writeFileSync(bdConfig.dataPath+"/bdsettings.json",JSON.stringify(bdSettings,null,4)),!0}catch(d){return Utils.err(d),!1}};var settingsPanel,emoteModule,quickEmoteMenu,voiceMode,pluginModule,themeModule,dMode,publicServersModule,minSupportedVersion="0.2.81",bbdVersion="0.1.2",mainCore,settings={"Save logs locally":{id:"bda-gs-0",info:"Saves chat logs locally",implemented:!1,hidden:!1,cat:"core"},"Public Servers":{id:"bda-gs-1",info:"Display public servers button",implemented:!0,hidden:!1,cat:"core"},"Minimal Mode":{id:"bda-gs-2",info:"Hide elements and reduce the size of elements.",implemented:!0,hidden:!1,cat:"core"},"Voice Mode":{id:"bda-gs-4",info:"Only show voice chat",implemented:!0,hidden:!1,cat:"core"},"Hide Channels":{id:"bda-gs-3",info:"Hide channels in minimal mode",implemented:!0,hidden:!1,cat:"core"},"Dark Mode":{id:"bda-gs-5",info:"Make certain elements dark by default(wip)",implemented:!0,hidden:!1,cat:"core"},"Override Default Emotes":{id:"bda-es-5",info:"Override default emotes",implemented:!1,hidden:!1,cat:"core"},"Voice Disconnect":{id:"bda-dc-0",info:"Disconnect from voice server when closing Discord",implemented:!0,hidden:!1,cat:"core"},"Custom css live update":{id:"bda-css-0",info:"",implemented:!0,hidden:!0,cat:"core"},"Custom css auto udpate":{id:"bda-css-1",info:"",implemented:!0,hidden:!0,cat:"core"},"24 Hour Timestamps":{id:"bda-gs-6",info:"Replace 12hr timestamps with proper ones",implemented:!0,hidden:!1,cat:"core"},"Coloured Text":{id:"bda-gs-7",info:"Make text colour the same as role colour",implemented:!0,hidden:!1,cat:"core"},"BetterDiscord Blue":{id:"bda-gs-b",info:"Replace Discord blue with BD Blue",implemented:!0,hidden:!1,cat:"core"},"Developer Mode":{id:"bda-gs-8",info:"Developer Mode",implemented:!0,hidden:!1,cat:"core"},"Startup Error Modal":{id:"fork-ps-1",info:"Show a modal with plugin/theme errors on startup",implemented:!0,hidden:!1,cat:"fork"},"Show Toasts":{id:"fork-ps-2",info:"Shows a small notification for starting and stopping plugins & themes",implemented:!0,hidden:!1,cat:"fork"},"Scroll To Settings":{id:"fork-ps-3",info:"Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)",implemented:!0,hidden:!1,cat:"fork"},"Animate On Hover":{id:"fork-es-2",info:"Only animate the emote modifiers on hover",implemented:!0,hidden:!1,cat:"fork"},"Copy Selector":{id:"fork-dm-1",info:"Adds a \"Copy Selector\" option to context menus when developer mode is active",implemented:!0,hidden:!1,cat:"fork"},"Download Emotes":{id:"fork-es-3",info:"Download emotes when the cache is expired",implemented:!0,hidden:!1,cat:"fork"},"Normalize Classes":{id:"fork-ps-4",info:"Adds stable classes to elements to help themes. (e.g. adds .da-channels to .channels-Ie2l6A)",implemented:!0,hidden:!1,cat:"fork"},"Twitch Emotes":{id:"bda-es-7",info:"Show Twitch emotes",implemented:!0,hidden:!1,cat:"emote"},"FrankerFaceZ Emotes":{id:"bda-es-1",info:"Show FrankerFaceZ Emotes",implemented:!0,hidden:!1,cat:"emote"},"BetterTTV Emotes":{id:"bda-es-2",info:"Show BetterTTV Emotes",implemented:!0,hidden:!1,cat:"emote"},"Emote Menu":{id:"bda-es-0",info:"Show Twitch/Favourite emotes in emote menu",implemented:!0,hidden:!1,cat:"emote"},"Emoji Menu":{id:"bda-es-9",info:"Show Discord emoji menu",implemented:!0,hidden:!1,cat:"emote"},"Emote Autocomplete":{id:"bda-es-3",info:"Autocomplete emote commands",implemented:!1,hidden:!1,cat:"emote"},"Emote Auto Capitalization":{id:"bda-es-4",info:"Autocapitalize emote commands",implemented:!0,hidden:!1,cat:"emote"},"Show Names":{id:"bda-es-6",info:"Show emote names on hover",implemented:!0,hidden:!1,cat:"emote"},"Show emote modifiers":{id:"bda-es-8",info:"Enable emote mods (flip, spin, pulse, spin2, spin3, 1spin, 2spin, 3spin, tr, bl, br, shake, shake2, shake3, flap)",implemented:!0,hidden:!1,cat:"emote"}},defaultCookie={"bda-gs-0":!1,"bda-gs-1":!0,"bda-gs-2":!1,"bda-gs-3":!1,"bda-gs-4":!1,"bda-gs-5":!0,"bda-gs-6":!1,"bda-gs-7":!1,"bda-gs-8":!1,"bda-es-0":!0,"bda-es-1":!0,"bda-es-2":!0,"bda-es-3":!1,"bda-es-4":!1,"bda-es-5":!0,"bda-es-6":!0,"bda-es-7":!0,"bda-gs-b":!1,"bda-es-8":!0,"bda-jd":!0,"bda-dc-0":!1,"bda-css-0":!1,"bda-css-1":!1,"bda-es-9":!0,"fork-dm-1":!1,"fork-ps-1":!0,"fork-ps-2":!0,"fork-ps-3":!0,"fork-ps-4":!0,"fork-es-2":!1,"fork-es-3":!0},settingsCookie={},bdpluginErrors,bdthemeErrors,bdConfig=null;function Core(t){window.bdConfig=t}var classNormalizer;Core.prototype.init=async function(){return bdConfig.version Please download the latest version from BetterDiscord.net"):void(bdConfig.updater.LatestVersion>bdConfig.version&&this.alert("Update Available",`
+(function(){let o=window.require("fs"),l=window.require("process"),d=l.platform,u=("win32"===d?l.env.APPDATA:"darwin"===d?l.env.HOME+"/Library/Preferences":process.env.HOME+"/.config")+"/BetterDiscord/",h="localStorage.json",g={};if(o.existsSync(`${u}${h}`))try{g=JSON.parse(o.readFileSync(`${u}${h}`))}catch(C){console.log(C)}else if(o.existsSync(h))try{g=JSON.parse(o.readFileSync(h))}catch(C){console.log(C)}var y=g;y.setItem=function(C,S){y[C]=S,this.save()},y.getItem=function(C){return y[C]||null},y.save=function(){o.writeFileSync(`${u}${h}`,JSON.stringify(this),null,4)};var E=new Proxy(y,{set:function(C,S,x){y[S]=x,y.save()},get:function(C,S){return y[S]||null}});window.localStorage=E})(),(()=>{let o=document.createElement("div");o.className="bd-loaderv2",o.title="BetterDiscord is loading...",document.body.appendChild(o)})();var DataStore=(()=>{const o=require("fs"),l=require("path"),d=DiscordNative.globals.releaseChannel;return new class{constructor(){this.data={settings:{stable:{},canary:{},ptb:{}}},this.pluginData={}}initialize(){if(o.existsSync(this.BDFile)?this.data=require(this.BDFile):o.writeFileSync(this.BDFile,JSON.stringify(this.data,null,4)),!!o.existsSync(this.settingsFile)){let h=require(this.settingsFile);o.unlinkSync(this.settingsFile),h=h.hasOwnProperty("settings")?Object.assign({stable:{},canary:{},ptb:{}},{[d]:h}):Object.assign({stable:{},canary:{},ptb:{}},h),this.setBDData("settings",h)}}get BDFile(){return this._BDFile||(this._BDFile=l.resolve(bdConfig.dataPath,"bdstorage.json"))}get settingsFile(){return this._settingsFile||(this._settingsFile=l.resolve(bdConfig.dataPath,"bdsettings.json"))}getPluginFile(h){return l.resolve(ContentManager.pluginsFolder,h+".config.json")}getSettingGroup(h){return this.data.settings[d][h]||null}setSettingGroup(h,g){this.data.settings[d][h]=g,o.writeFileSync(this.BDFile,JSON.stringify(this.data,null,4))}getBDData(h){return this.data[h]||""}setBDData(h,g){this.data[h]=g,o.writeFileSync(this.BDFile,JSON.stringify(this.data,null,4))}getPluginData(h,g){return void 0===this.pluginData[h]?o.existsSync(this.getPluginFile(h))?(this.pluginData[h]=JSON.parse(o.readFileSync(this.getPluginFile(h))),this.pluginData[h][g]||void 0):void 0:this.pluginData[h][g]||void 0}setPluginData(h,g,y){void 0===y||(this.pluginData[h]===void 0&&(this.pluginData[h]={}),this.pluginData[h][g]=y,o.writeFileSync(this.getPluginFile(h),JSON.stringify(this.pluginData[h],null,4)))}deletePluginData(h,g){this.pluginData[h]===void 0&&(this.pluginData[h]={}),delete this.pluginData[h][g],o.writeFileSync(this.getPluginFile(h),JSON.stringify(this.pluginData[h],null,4))}}})();window.bdStorage=class{static get(l){return Utils.warn("[Deprecation Notice] Please use BdApi.getBDData(). bdStorage may be removed in future versions."),DataStore.getBDData(l)}static set(l,d){Utils.warn("[Deprecation Notice] Please use BdApi.setBDData(). bdStorage may be removed in future versions."),DataStore.setBDData(l,d)}},window.bdPluginStorage=class{static get(l,d){return Utils.warn(`[Deprecation Notice] Please use BdApi.loadData() or BdApi.getData(). bdPluginStorage may be removed in future versions.`),DataStore.getPluginData(l,d)||null}static set(l,d,u){return Utils.warn("[Deprecation Notice] Please use BdApi.saveData() or BdApi.setData(). bdPluginStorage may be removed in future versions."),"undefined"==typeof value?Utils.warn("Trying to set undefined value in plugin "+l):void DataStore.setPluginData(l,d,u)}static delete(l,d){Utils.warn("[Deprecation Notice] Please use BdApi.deleteData(). bdPluginStorage may be removed in future versions."),DataStore.deletePluginData(l,d)}};var settingsPanel,emoteModule,quickEmoteMenu,voiceMode,pluginModule,themeModule,dMode,publicServersModule,minSupportedVersion="0.3.0",bbdVersion="0.2.2",mainCore,settings={"Save logs locally":{id:"bda-gs-0",info:"Saves chat logs locally",implemented:!1,hidden:!1,cat:"core"},"Public Servers":{id:"bda-gs-1",info:"Display public servers button",implemented:!0,hidden:!1,cat:"core"},"Minimal Mode":{id:"bda-gs-2",info:"Hide elements and reduce the size of elements.",implemented:!0,hidden:!1,cat:"core"},"Voice Mode":{id:"bda-gs-4",info:"Only show voice chat",implemented:!0,hidden:!1,cat:"core"},"Hide Channels":{id:"bda-gs-3",info:"Hide channels in minimal mode",implemented:!0,hidden:!1,cat:"core"},"Dark Mode":{id:"bda-gs-5",info:"Make certain elements dark by default(wip)",implemented:!0,hidden:!1,cat:"core"},"Override Default Emotes":{id:"bda-es-5",info:"Override default emotes",implemented:!1,hidden:!1,cat:"core"},"Voice Disconnect":{id:"bda-dc-0",info:"Disconnect from voice server when closing Discord",implemented:!0,hidden:!1,cat:"core"},"Custom css live update":{id:"bda-css-0",info:"",implemented:!0,hidden:!0,cat:"core"},"Custom css auto udpate":{id:"bda-css-1",info:"",implemented:!0,hidden:!0,cat:"core"},"24 Hour Timestamps":{id:"bda-gs-6",info:"Replace 12hr timestamps with proper ones",implemented:!0,hidden:!1,cat:"core"},"Coloured Text":{id:"bda-gs-7",info:"Make text colour the same as role colour",implemented:!0,hidden:!1,cat:"core"},"BetterDiscord Blue":{id:"bda-gs-b",info:"Replace Discord blue with BD Blue",implemented:!0,hidden:!1,cat:"core"},"Developer Mode":{id:"bda-gs-8",info:"Developer Mode",implemented:!0,hidden:!1,cat:"core"},"Startup Error Modal":{id:"fork-ps-1",info:"Show a modal with plugin/theme errors on startup",implemented:!0,hidden:!1,cat:"fork"},"Show Toasts":{id:"fork-ps-2",info:"Shows a small notification for important information",implemented:!0,hidden:!1,cat:"fork"},"Scroll To Settings":{id:"fork-ps-3",info:"Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)",implemented:!0,hidden:!1,cat:"fork"},"Animate On Hover":{id:"fork-es-2",info:"Only animate the emote modifiers on hover",implemented:!0,hidden:!1,cat:"fork"},"Copy Selector":{id:"fork-dm-1",info:"Adds a \"Copy Selector\" option to context menus when developer mode is active",implemented:!0,hidden:!1,cat:"fork"},"Download Emotes":{id:"fork-es-3",info:"Download emotes when the cache is expired",implemented:!0,hidden:!1,cat:"fork"},"Normalize Classes":{id:"fork-ps-4",info:"Adds stable classes to elements to help themes. (e.g. adds .da-channels to .channels-Ie2l6A)",implemented:!0,hidden:!1,cat:"fork"},"Automatic Loading":{id:"fork-ps-5",info:"Automatically loads, reloads, and unloads plugins and themes",implemented:!0,hidden:!1,cat:"fork"},"Twitch Emotes":{id:"bda-es-7",info:"Show Twitch emotes",implemented:!0,hidden:!1,cat:"emote"},"FrankerFaceZ Emotes":{id:"bda-es-1",info:"Show FrankerFaceZ Emotes",implemented:!0,hidden:!1,cat:"emote"},"BetterTTV Emotes":{id:"bda-es-2",info:"Show BetterTTV Emotes",implemented:!0,hidden:!1,cat:"emote"},"Emote Menu":{id:"bda-es-0",info:"Show Twitch/Favourite emotes in emote menu",implemented:!0,hidden:!1,cat:"emote"},"Emoji Menu":{id:"bda-es-9",info:"Show Discord emoji menu",implemented:!0,hidden:!1,cat:"emote"},"Emote Autocomplete":{id:"bda-es-3",info:"Autocomplete emote commands",implemented:!1,hidden:!1,cat:"emote"},"Emote Auto Capitalization":{id:"bda-es-4",info:"Autocapitalize emote commands",implemented:!0,hidden:!1,cat:"emote"},"Show Names":{id:"bda-es-6",info:"Show emote names on hover",implemented:!0,hidden:!1,cat:"emote"},"Show emote modifiers":{id:"bda-es-8",info:"Enable emote mods (flip, spin, pulse, spin2, spin3, 1spin, 2spin, 3spin, tr, bl, br, shake, shake2, shake3, flap)",implemented:!0,hidden:!1,cat:"emote"}},defaultCookie={"bda-gs-0":!1,"bda-gs-1":!0,"bda-gs-2":!1,"bda-gs-3":!1,"bda-gs-4":!1,"bda-gs-5":!0,"bda-gs-6":!1,"bda-gs-7":!1,"bda-gs-8":!1,"bda-es-0":!0,"bda-es-1":!0,"bda-es-2":!0,"bda-es-3":!1,"bda-es-4":!1,"bda-es-5":!0,"bda-es-6":!0,"bda-es-7":!0,"bda-gs-b":!1,"bda-es-8":!0,"bda-dc-0":!1,"bda-css-0":!1,"bda-css-1":!1,"bda-es-9":!0,"fork-dm-1":!1,"fork-ps-1":!0,"fork-ps-2":!0,"fork-ps-3":!0,"fork-ps-4":!0,"fork-ps-5":!0,"fork-es-2":!1,"fork-es-3":!0},settingsCookie={},bdpluginErrors,bdthemeErrors,bdConfig=null;function Core(o){window.bdConfig=o}var classNormalizer;Core.prototype.init=async function(){return bdConfig.version Please download the latest version from GitHub"):void(bdConfig.updater.LatestVersion>bdConfig.version&&this.alert("Update Available",`
An update for BandagedBD is available (${bdConfig.updater.LatestVersion})! Please Reinstall!