more customcss stuff

This commit is contained in:
Zack Rauen 2019-06-10 16:37:50 -04:00
parent 8ee16509a8
commit 8b907bf8fe
22 changed files with 329 additions and 564 deletions

View File

@ -34,7 +34,8 @@
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react/prop-types": 0,
"react/jsx-no-target-blank": "error"
"react/jsx-no-target-blank": "error",
"react/jsx-key": 0
},
"globals": {
"webpackJsonp": false,

View File

@ -85,6 +85,14 @@
margin-bottom: 10px;
}
.checkbox-item {
display: flex;
}
.checkbox-item .checkbox-label {
margin-right: 8px;
}
@ -606,7 +614,7 @@ color: #f6f6f7;
.bd-detached-css-editor #bd-customcss-attach-controls button {
margin: 0;
width: 100px;
background: #31332b;
background: #2F3129;
color: #FFF;
height: 26px;
font-weight: 600;
@ -616,7 +624,7 @@ color: #f6f6f7;
.standardSidebarView-3F1I7i #bd-customcss-attach-controls button:hover,
.bd-detached-css-editor #bd-customcss-attach-controls button:hover {
background: #3b3e44;
background: rgb(59,61,51);
}
.contentRegion-3nDuYy #bd-customcss-attach-controls,
@ -768,6 +776,17 @@ color: #f6f6f7;
#bd-customcss-attach-controls button {
margin: 0;
width: 100px;
border-radius: 0px;
border-left: 2px solid rgb(63, 65, 70);
}
#bd-customcss-attach-controls button:first-of-type {
border-radius: 3px 0px 0px 3px;
border-left: none;
}
#bd-customcss-attach-controls button:last-of-type {
border-radius: 0px 3px 3px 0px;
}
#bd-customcss-detach-container #bd-customcss-detach-controls-buttons button {
@ -779,8 +798,13 @@ color: #f6f6f7;
font-size: 19px;
}
#bd-customcss-attach-controls .small-notice {
font-size: 10px;
margin-left: 5px;
}
/* Ace Editor Settings */
body > div:not([class]):last-of-type {
#ace_settingsmenu_container {
background: rgba(0,0,0, 0.7)!important;
}
@ -816,7 +840,7 @@ body .ace_closeButton:active {
}
#bd-customcss-attach-controls .help-text .inline {
background: #31332B;
background: #2F3129;
padding: .2em;
margin: -.2em 0;
border-radius: 3px;

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
js/main.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,8 @@
"watch": "webpack --progress --colors --watch",
"build-prod": "webpack --progress --colors --mode production -o js/main.min.js --devtool none",
"test": "echo \"Error: no test specified\" && exit 1",
"minify": "gulp minify-css",
"minify": "npm run build-prod && npm run minify-css",
"minify-css": "gulp minify-css",
"watch-css": "gulp watch-css"
},
"repository": {

View File

@ -1,20 +1,66 @@
import Builtin from "../structs/builtin";
import {Settings} from "modules";
import {Settings, DataStore, React, Events} from "modules";
import CSSEditor from "../ui/customcss/editor";
const electron = require("electron");
export default new class CustomCSS extends Builtin {
get name() {return "Custom CSS";}
get category() {return "customcss";}
get id() {return "customcss";}
get startDetached() {return Settings.get(this.collection, this.category, "startDetached");}
get nativeOpen() {return Settings.get(this.collection, this.category, "nativeOpen");}
constructor() {
super();
this.css = "";
}
enabled() {
Settings.registerPanel(this.id, this.name, {
element: CSSEditor,
order: 2
order: 2,
element: () => React.createElement(CSSEditor, {
css: this.css,
save: this.saveCSS.bind(this),
update: this.insertCSS.bind(this),
openNative: this.openNative.bind(this)
}),
onClick: (thisObject) => {
if (this.nativeOpen) this.openNative();
else if (this.startDetached) this.openDetached();
else thisObject._reactInternalFiber.child.memoizedProps.children.props.onSetSection(this.name);
}
});
this.loadCSS();
this.insertCSS();
}
disabled() {
Settings.removePanel(this.id);
}
loadCSS() {
this.css = DataStore.loadCustomCSS();
}
insertCSS(newCss) {
if (typeof(newCss) === "undefined") newCss = this.css;
if ($("#customcss").length == 0) {
$("head").append("<style id=\"customcss\"></style>");
}
$("#customcss").text(newCss).detach().appendTo(document.head);
}
saveCSS(newCss) {
if (typeof(newCss) !== "undefined") this.css = newCss;
DataStore.saveCustomCSS(this.css);
}
openNative() {
electron.shell.openExternal(`file://${DataStore.customCSS}`);
}
openDetached() {
this.log("Should open detached");
}
};

View File

@ -21,12 +21,12 @@ export default new class WindowPrefs extends Builtin {
}
enabled() {
this.setWindowPreference("transparency", true);
this.setWindowPreference("transparent", true);
this.setWindowPreference("backgroundColor", null);
}
disabled() {
this.setWindowPreference("transparency", false);
this.setWindowPreference("transparent", false);
this.setWindowPreference("backgroundColor", "#2f3136");
}

View File

@ -196,14 +196,16 @@ export default [
id: "startDetached",
name: "Start Detached",
note: "Clicking the Custom CSS tab opens the editor in a separate window",
value: false
value: false,
disableWith: "nativeOpen"
},
{
type: "switch",
id: "nativeOpen",
name: "Open in Native Editor",
note: "Clicking the Custom CSS tab opens your custom css in your native editor",
value: false
value: false,
disableWith: "startDetached"
}
]
}

View File

@ -28,6 +28,7 @@ export default new class DataStore {
if (!fs.existsSync(this.baseFolder)) fs.mkdirSync(this.baseFolder);
if (!fs.existsSync(this.dataFolder)) fs.mkdirSync(this.dataFolder);
if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data.misc, null, 4));
if (!fs.existsSync(this.customCSS)) fs.writeFileSync(this.customCSS, "");
const dataFiles = fs.readdirSync(this.dataFolder).filter(f => !fs.statSync(path.resolve(this.dataFolder, f)).isDirectory() && f.endsWith(".json"));
for (const file of dataFiles) {
this.data[file.split(".")[0]] = __non_webpack_require__(path.resolve(this.dataFolder, file));
@ -42,6 +43,7 @@ export default new class DataStore {
// this.setBDData("settings", settings);
}
get customCSS() {return this._customCSS || (this._customCSS = path.resolve(this.dataFolder, "custom.css"));}
get baseFolder() {return this._baseFolder || (this._baseFolder = path.resolve(Config.dataPath, "data"));}
get dataFolder() {return this._dataFolder || (this._dataFolder = path.resolve(this.baseFolder, `${releaseChannel}`));}
get BDFile() {return this._BDFile || (this._BDFile = path.resolve(Config.dataPath, "data", `${releaseChannel}.json`));}
@ -82,6 +84,14 @@ export default new class DataStore {
fs.writeFileSync(path.resolve(this.dataFolder, `${key}.json`), JSON.stringify(value, null, 4));
}
loadCustomCSS() {
return fs.readFileSync(this.customCSS).toString();
}
saveCustomCSS(css) {
return fs.writeFileSync(this.customCSS, css);
}
getPluginData(pluginName, key) {
if (this.pluginData[pluginName] !== undefined) return this.pluginData[pluginName][key] || undefined;
if (!fs.existsSync(this.getPluginFile(pluginName))) return undefined;

View File

@ -5,7 +5,6 @@ import WebpackModules, {DiscordModules} from "./webpackmodules";
import {SettingsPanel as SettingsRenderer} from "ui";
import Utilities from "./utilities";
import {Toasts} from "ui";
export default new class SettingsManager {
@ -44,8 +43,8 @@ export default new class SettingsManager {
if (this.panels.find(p => p.id == id)) return Utilities.err("Settings", "Already have a panel with id " + id);
const {element, onClick, order = 1} = options;
const section = {id, order, label: name, section: name};
if (onClick) section.onClick = onClick;
else section.element = element instanceof DiscordModules.React.Component ? () => DiscordModules.React.createElement(element, {}) : typeof(element) == "function" ? element : () => element;
if (onClick) section.clickListener = onClick;
if (element) section.element = element instanceof DiscordModules.React.Component ? () => DiscordModules.React.createElement(element, {}) : typeof(element) == "function" ? element : () => element;
this.panels.push(section);
}
@ -84,22 +83,30 @@ export default new class SettingsManager {
}
});
}
if (setting.disableWith) {
const path = this.getPath(setting.disableWith.split("."), collection.id, category.id);
if (setting.hasOwnProperty("disabled")) continue;
Object.defineProperty(setting, "disabled", {
get: () => {
return this.state[path.collection][path.category][path.setting];
}
});
}
}
}
}
if (collection.enableWith) {
const path = this.getPath(collection.enableWith.split("."));
Object.defineProperty(collection, "disabled", {
get: () => {
return !this.state[path.collection][path.category][path.setting];
}
});
}
}
}
async patchSections() {
Utilities.monkeyPatch(WebpackModules.getByDisplayName("FluxContainer(GuildSettings)").prototype, "render", {after: (data) => {
data.thisObject._reactInternalFiber.return.return.return.return.return.return.memoizedProps.id = "guild-settings";
}});
const UserSettings = await this.getUserSettings();
Utilities.monkeyPatch(UserSettings.prototype, "render", {after: (data) => {
data.thisObject._reactInternalFiber.return.return.return.return.return.return.return.memoizedProps.id = "user-settings";
}});
Utilities.monkeyPatch(UserSettings.prototype, "generateSections", {after: (data) => {
let location = data.returnValue.findIndex(s => s.section.toLowerCase() == "linux") + 1;
const insert = (section) => {
@ -116,8 +123,10 @@ export default new class SettingsManager {
element: () => SettingsRenderer.buildSettingsPanel(collection.name, collection.settings, this.state[collection.id], this.onSettingChange.bind(this, collection.id), collection.button ? collection.button : null)
});
}
for (const panel of this.panels.sort((a,b) => a.order > b.order)) insert(panel);
insert({section: "BBD Test", label: "Test Tab", onClick: function() {Toasts.success("This can just be a click listener!", {forceShow: true});}});
for (const panel of this.panels.sort((a,b) => a.order > b.order)) {
if (panel.clickListener) panel.onClick = (event) => panel.clickListener(data.thisObject, event, data.returnValue);
insert(panel);
}
insert({section: "CUSTOM", element: () => SettingsRenderer.attribution});
}});
this.forceUpdate();
@ -166,7 +175,7 @@ export default new class SettingsManager {
Events.dispatch("setting-updated", collection, category, id, value);
const after = this.collections.length + this.panels.length;
this.saveSettings();
if (before != after) this.forceUpdate();
if (before != after) setTimeout(this.forceUpdate.bind(this), 50);
}
getSetting(collection, category, id) {

View File

@ -1,40 +0,0 @@
import {React} from "modules";
export default class Checkbox extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {
checked: this.props.checked || false
};
}
render() {
return React.createElement(
"li",
null,
React.createElement(
"div",
{className: "checkbox checkbox-3kaeSU da-checkbox checkbox-3EVISJ da-checkbox", onClick: this.onClick},
React.createElement(
"div",
{className: "checkbox-inner checkboxInner-3yjcPe da-checkboxInner"},
React.createElement("input", {className: "checkboxElement-1qV33p da-checkboxElement", checked: this.state.checked, onChange: () => {}, type: "checkbox"}),
React.createElement("span", null)
),
React.createElement(
"span",
null,
this.props.text
)
)
);
}
onClick() {
this.props.onChange(this.props.id, !this.state.checked);
this.setState({
checked: !this.state.checked
});
}
}

View File

@ -0,0 +1,27 @@
import {React} from "modules";
export default class Checkbox extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {checked: this.props.checked || false};
}
render() {
return <div className="checkbox-item">
<div className="checkbox-label label-JWQiNe da-label">{this.props.text}</div>
<div className="checkbox-wrapper checkbox-3kaeSU da-checkbox checkbox-3EVISJ da-checkbox" onClick={this.onClick}>
<div className="checkbox-inner checkboxInner-3yjcPe da-checkboxInner">
<input className="checkbox checkboxElement-1qV33p da-checkboxElement" checked={this.state.checked} type="checkbox" />
<span></span>
</div>
<span></span>
</div>
</div>;
}
onClick() {
this.props.onChange(!this.state.checked);
this.setState({checked: !this.state.checked});
}
}

View File

@ -1,172 +0,0 @@
import {SettingsCookie} from "data";
import {BDV2, DataStore, Core, DiscordModules} from "modules";
import Checkbox from "./checkbox";
export default class CssEditorDetached extends DiscordModules.React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.updateCss = this.updateCss.bind(this);
this.saveCss = this.saveCss.bind(this);
this.onChange = this.onChange.bind(this);
}
componentDidMount() {
$("#app-mount").addClass("bd-detached-editor");
BDV2.editorDetached = true;
// this.updateLineCount();
this.editor = ace.edit("bd-customcss-editor-detached");
this.editor.setTheme("ace/theme/monokai");
this.editor.session.setMode("ace/mode/css");
this.editor.setShowPrintMargin(false);
this.editor.setFontSize(14);
this.editor.on("change", () => {
if (!SettingsCookie["bda-css-0"]) return;
this.saveCss();
this.updateCss();
});
}
componentWillUnmount() {
$("#app-mount").removeClass("bd-detached-editor");
BDV2.editorDetached = false;
this.editor.destroy();
}
updateLineCount() {
const lineCount = this.refs.editor.value.split("\n").length;
if (lineCount == this.props.lines) return;
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
this.props.lines = lineCount;
}
get options() {
return {
lineNumbers: true,
mode: "css",
indentUnit: 4,
theme: "material",
scrollbarStyle: "simple"
};
}
get css() {
const _ccss = DataStore.getBDData("bdcustomcss");
let ccss = "";
if (_ccss && _ccss !== "") {
ccss = atob(_ccss);
}
return ccss;
}
get root() {
const _root = $("#bd-customcss-detach-container");
if (!_root.length) {
if (!this.injectRoot()) return null;
return this.detachedRoot;
}
return _root[0];
}
injectRoot() {
if (!$(".app, .app-2rEoOp").length) return false;
$("<div/>", {
id: "bd-customcss-detach-container"
}).insertAfter($(".app, .app-2rEoOp"));
return true;
}
render() {
const self = this;
return DiscordModules.React.createElement(
"div",
{className: "bd-detached-css-editor", id: "bd-customcss-detach-editor"},
DiscordModules.React.createElement(
"div",
{id: "bd-customcss-innerpane"},
DiscordModules.React.createElement("div", {className: "editor-wrapper"},
DiscordModules.React.createElement("div", {id: "bd-customcss-editor-detached", className: "editor", ref: "editor"}, self.css)
),
DiscordModules.React.createElement(
"div",
{id: "bd-customcss-attach-controls"},
DiscordModules.React.createElement(
"ul",
{className: "checkbox-group"},
DiscordModules.React.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: self.onChange, checked: SettingsCookie["bda-css-0"]})
),
DiscordModules.React.createElement(
"div",
{id: "bd-customcss-detach-controls-button"},
DiscordModules.React.createElement(
"button",
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("update");
}},
"Update"
),
DiscordModules.React.createElement(
"button",
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
self.onClick("save");
}},
"Save"
),
DiscordModules.React.createElement(
"button",
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("attach");
}},
"Attach"
),
DiscordModules.React.createElement(
"span",
{style: {fontSize: "10px", marginLeft: "5px"}},
"Unsaved changes are lost on attach"
)
)
)
)
);
}
onChange(id, checked) {
switch (id) {
case "live-update":
SettingsCookie["bda-css-0"] = checked;
Core.saveSettings();
break;
}
}
onClick(id) {
const self = this;
switch (id) {
case "attach":
if ($("#editor-detached").length) self.props.attach();
DiscordModules.ReactDOM.unmountComponentAtNode(self.root);
self.root.remove();
break;
case "update":
self.updateCss();
break;
case "save":
self.saveCss();
break;
}
}
updateCss() {
if ($("#customcss").length == 0) {
$("head").append("<style id=\"customcss\"></style>");
}
$("#customcss").text(this.editor.session.getValue()).detach().appendTo(document.head);
}
saveCss() {
DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue()));
}
}

View File

@ -1,222 +0,0 @@
import {BDV2, DataStore, React, ReactDOM, Settings} from "modules";
import Checkbox from "./checkbox";
import SettingsTitle from "../settings/title";
export default class CssEditor extends React.Component {
constructor(props) {
super(props);
this.props.lines = 0;
this.state = {
detached: this.props.detached || BDV2.editorDetached
};
// this.attach = this.attach.bind(this);
// this.detachedEditor = React.createElement(EditorDetached, {attach: this.attach});
this.onClick = this.onClick.bind(this);
this.updateCss = this.updateCss.bind(this);
this.saveCss = this.saveCss.bind(this);
this.detach = this.detach.bind(this);
}
componentDidMount() {
// this.updateLineCount();
this.editor = ace.edit("bd-customcss-editor");
this.editor.setTheme("ace/theme/monokai");
this.editor.session.setMode("ace/mode/css");
this.editor.setShowPrintMargin(false);
this.editor.setFontSize(14);
this.editor.on("change", () => {
if (!Settings.get("settings", "customcss", "liveUpdate")) return;
this.saveCss();
this.updateCss();
});
}
componentWillUnmount() {
this.editor.destroy();
}
componentDidUpdate(prevProps, prevState) {
if (prevState.detached && !this.state.detached) {
ReactDOM.unmountComponentAtNode(this.detachedRoot);
}
}
codeMirror() {
}
get options() {
return {
lineNumbers: true,
mode: "css",
indentUnit: 4,
theme: "material",
scrollbarStyle: "simple"
};
}
get css() {
const _ccss = DataStore.getBDData("bdcustomcss");
let ccss = "";
if (_ccss && _ccss !== "") {
ccss = atob(_ccss);
}
return ccss;
}
updateLineCount() {
const lineCount = this.refs.editor.value.split("\n").length;
if (lineCount == this.props.lines) return;
this.refs.lines.textContent = Array.from(new Array(lineCount), (_, i) => i + 1).join(".\n") + ".";
this.props.lines = lineCount;
}
render() {
const self = this;
return React.createElement(
"div",
{className: "contentColumn-2hrIYH contentColumnDefault-1VQkGM content-column default", style: {padding: "60px 40px 0px"}},
// detached && React.createElement(
// "div",
// {id: "editor-detached"},
// React.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
// React.createElement(
// "h3",
// null,
// "Editor Detached"
// ),
// React.createElement(
// "button",
// {className: "btn btn-primary", onClick: () => {
// self.attach();
// }},
// "Attach"
// )
// ),
/*!detached && */React.createElement(
"div",
null,
React.createElement(SettingsTitle, {text: "Custom CSS Editor"}),
React.createElement("div", {className: "editor-wrapper"},
React.createElement("div", {id: "bd-customcss-editor", className: "editor", ref: "editor"}, self.css)
),
React.createElement(
"div",
{id: "bd-customcss-attach-controls"},
React.createElement(
"ul",
{className: "checkbox-group"},
React.createElement(Checkbox, {id: "live-update", text: "Live Update", onChange: this.onChange, checked: Settings.get("settings", "customcss", "liveUpdate")})
),
React.createElement(
"div",
{id: "bd-customcss-detach-controls-button"},
React.createElement(
"button",
{style: {borderRadius: "3px 0 0 3px", borderRight: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("update");
}},
"Update"
),
React.createElement(
"button",
{style: {borderRadius: "0", borderLeft: "1px solid #2d2d2d", borderRight: "1px solid #2d2d2d"}, className: "btn btn-primary", onClick: () => {
self.onClick("save");
}},
"Save"
),
React.createElement(
"button",
{style: {borderRadius: "0 3px 3px 0", borderLeft: "1px solid #3f4146"}, className: "btn btn-primary", onClick: () => {
self.onClick("detach");
}},
"Detach"
),
React.createElement(
"span",
{style: {fontSize: "10px", marginLeft: "5px"}},
"Unsaved changes are lost on detach"
),
React.createElement("div", {className: "help-text"},
"Press ",
React.createElement("code", {className: "inline"}, "ctrl"),
"+",
React.createElement("span", {className: "inline"}, ","),
" with the editor focused to access the editor's settings."
)
)
)
)
);
}
onClick(arg) {
switch (arg) {
case "update":
this.updateCss();
break;
case "save":
this.saveCss();
break;
case "detach":
this.detach();
break;
}
}
onChange(id, checked) {
switch (id) {
case "live-update":
Settings.set("settings", "customcss", "liveUpdate", checked);
break;
}
}
updateCss() {
if ($("#customcss").length == 0) {
$("head").append("<style id=\"customcss\"></style>");
}
$("#customcss").text(this.editor.session.getValue()).detach().appendTo(document.head);
}
saveCss() {
DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue()));
}
detach() {
return console.log("DETACH");
// this.setState({
// detached: true
// });
// const droot = this.detachedRoot;
// if (!droot) {
// console.log("FAILED TO INJECT ROOT: .app");
// return;
// }
// ReactDOM.render(this.detachedEditor, droot);
}
// get detachedRoot() {
// const _root = $("#bd-customcss-detach-container");
// if (!_root.length) {
// if (!this.injectDetachedRoot()) return null;
// return this.detachedRoot;
// }
// return _root[0];
// }
// injectDetachedRoot() {
// if (!$(".app, .app-2rEoOp").length) return false;
// $("<div/>", {
// id: "bd-customcss-detach-container"
// }).insertAfter($(".app, .app-2rEoOp"));
// return true;
// }
// attach() {
// this.setState({
// detached: false
// });
// }
}

View File

@ -0,0 +1,98 @@
import {React, Settings} from "modules";
import Checkbox from "./checkbox";
import SettingsTitle from "../settings/title";
export default class CssEditor extends React.Component {
constructor(props) {
super(props);
this.toggleLiveUpdate = this.toggleLiveUpdate.bind(this);
this.updateCss = this.updateCss.bind(this);
this.saveCss = this.saveCss.bind(this);
this.detach = this.detach.bind(this);
this.openNative = this.openNative.bind(this);
}
componentDidMount() {
this.editor = ace.edit("bd-customcss-editor");
// Add id to the ace menu container
const originalShow = this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec;
this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec = function() {
originalShow.apply(this, arguments);
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) continue;
const node = mutation.addedNodes[0];
if (node.parentElement !== document.body || !node.querySelector("#ace_settingsmenu")) continue;
node.id = "ace_settingsmenu_container";
observer.disconnect();
}
});
observer.observe(document.body, {childList: true});
};
this.editor.setTheme("ace/theme/monokai");
this.editor.session.setMode("ace/mode/css");
this.editor.setShowPrintMargin(false);
this.editor.setFontSize(14);
this.editor.on("change", () => {
if (!Settings.get("settings", "customcss", "liveUpdate")) return;
this.saveCss();
this.updateCss();
});
}
componentWillUnmount() {
this.editor.destroy();
}
render() {
return [
<SettingsTitle text="Custom CSS Editor" />,
<div className="editor-wrapper">
<div id="bd-customcss-editor" className="editor">{this.props.css}</div>
</div>,
<div id="bd-customcss-attach-controls">
<div className="checkbox-group">
<Checkbox text="Live Update" onChange={this.toggleLiveUpdate} checked={Settings.get("settings", "customcss", "liveUpdate")} />
</div>
<div id="bd-customcss-detach-controls-button">
<button className="btn btn-primary" onClick={this.updateCss}>Update</button>
<button className="btn btn-primary" onClick={this.saveCss}>Save</button>
<button className="btn btn-primary" onClick={this.openNative}>Open Natively</button>
<button className="btn btn-primary" onClick={this.detach}>Detach</button>
<span className="small-notice">Unsaved changes are lost on detach</span>
<div className="help-text">
Press <code className="inline">ctrl</code>+<code className="inline">,</code> with the editor focused to access the editor&apos;s settings.
</div>
</div>
</div>
];
}
toggleLiveUpdate(checked) {
Settings.set("settings", "customcss", "liveUpdate", checked);
}
updateCss() {
const newCss = this.editor.session.getValue();
if (this.props.update) this.props.update(newCss);
}
saveCss() {
const newCss = this.editor.session.getValue();
if (this.props.save) this.props.save(newCss);
}
detach() {
if (this.props.openDetached) this.props.openDetached();
}
openNative() {
if (this.props.openNative) this.props.openNative();
}
}

View File

@ -1,20 +0,0 @@
import {React} from "modules";
export default class BDLogo extends React.Component {
render() {
return React.createElement("svg",
{height: "100%", width: this.props.size || "16px", className: "bd-logo " + this.props.className, style: {fillRule: "evenodd", clipRule: "evenodd", strokeLinecap: "round", strokeLinejoin: "round"}, viewBox: "0 0 2000 2000"},
React.createElement("metadata", null),
React.createElement("defs", null,
React.createElement("filter", {id: "shadow1"}, React.createElement("feDropShadow", {"dx": "20", "dy": "0", "stdDeviation": "20", "flood-color": "rgba(0,0,0,0.35)"})),
React.createElement("filter", {id: "shadow2"}, React.createElement("feDropShadow", {"dx": "15", "dy": "0", "stdDeviation": "20", "flood-color": "rgba(255,255,255,0.15)"})),
React.createElement("filter", {id: "shadow3"}, React.createElement("feDropShadow", {"dx": "10", "dy": "0", "stdDeviation": "20", "flood-color": "rgba(0,0,0,0.35)"}))
),
React.createElement("g", null,
React.createElement("path", {style: {filter: "url(#shadow3)"}, d: "M1195.44+135.442L1195.44+135.442L997.6+136.442C1024.2+149.742+1170.34+163.542+1193.64+179.742C1264.34+228.842+1319.74+291.242+1358.24+365.042C1398.14+441.642+1419.74+530.642+1422.54+629.642L1422.54+630.842L1422.54+632.042C1422.54+773.142+1422.54+1228.14+1422.54+1369.14L1422.54+1370.34L1422.54+1371.54C1419.84+1470.54+1398.24+1559.54+1358.24+1636.14C1319.74+1709.94+1264.44+1772.34+1193.64+1821.44C1171.04+1837.14+1025.7+1850.54+1000+1863.54L1193.54+1864.54C1539.74+1866.44+1864.54+1693.34+1864.54+1296.64L1864.54+716.942C1866.44+312.442+1541.64+135.442+1195.44+135.442Z", fill: "#171717", opacity: "1"}),
React.createElement("path", {style: {filter: "url(#shadow2)"}, d: "M1695.54+631.442C1685.84+278.042+1409.34+135.442+1052.94+135.442L361.74+136.442L803.74+490.442L1060.74+490.442C1335.24+490.442+1335.24+835.342+1060.74+835.342L1060.74+1164.84C1150.22+1164.84+1210.53+1201.48+1241.68+1250.87C1306.07+1353+1245.76+1509.64+1060.74+1509.64L361.74+1863.54L1052.94+1864.54C1409.24+1864.54+1685.74+1721.94+1695.54+1368.54C1695.54+1205.94+1651.04+1084.44+1572.64+999.942C1651.04+915.542+1695.54+794.042+1695.54+631.442Z", fill: "#3E82E5", opacity: "1"}),
React.createElement("path", {style: {filter: "url(#shadow1)"}, d: "M1469.25+631.442C1459.55+278.042+1183.05+135.442+826.65+135.442L135.45+135.442L135.45+1004C135.45+1004+135.427+1255.21+355.626+1255.21C575.825+1255.21+575.848+1004+575.848+1004L577.45+490.442L834.45+490.442C1108.95+490.442+1108.95+835.342+834.45+835.342L664.65+835.342L664.65+1164.84L834.45+1164.84C923.932+1164.84+984.244+1201.48+1015.39+1250.87C1079.78+1353+1019.47+1509.64+834.45+1509.64L135.45+1509.64L135.45+1864.54L826.65+1864.54C1182.95+1864.54+1459.45+1721.94+1469.25+1368.54C1469.25+1205.94+1424.75+1084.44+1346.35+999.942C1424.75+915.542+1469.25+794.042+1469.25+631.442Z", fill: "#FFFFFF", opacity: "1"})
)
);
}
}

19
src/ui/icons/bdlogo.jsx Normal file
View File

@ -0,0 +1,19 @@
import {React} from "modules";
export default class BDLogo extends React.Component {
render() {
return <svg className={"bd-logo " + this.props.className} height="100%" width={this.props.size || "16px"} viewBox="0 0 2000 2000" style={{fillRule: "evenodd", clipRule: "evenodd", strokeLinecap: "round", strokeLinejoin: "round"}}>
<metadata />
<defs>
<filter id="shadow1"><feDropShadow dx="20" dy="0" stdDeviation="20" floodColor="rgba(0,0,0,0.35)" /></filter>
<filter id="shadow2"><feDropShadow dx="15" dy="0" stdDeviation="20" floodColor="rgba(255,255,255,0.15)" /></filter>
<filter id="shadow3"><feDropShadow dx="10" dy="0" stdDeviation="20" floodColor="rgba(0,0,0,0.35)" /></filter>
</defs>
<g>
<path style={{filter: "url(#shadow3)"}} fill="#171717" opacity="1" d="M1195.44+135.442L1195.44+135.442L997.6+136.442C1024.2+149.742+1170.34+163.542+1193.64+179.742C1264.34+228.842+1319.74+291.242+1358.24+365.042C1398.14+441.642+1419.74+530.642+1422.54+629.642L1422.54+630.842L1422.54+632.042C1422.54+773.142+1422.54+1228.14+1422.54+1369.14L1422.54+1370.34L1422.54+1371.54C1419.84+1470.54+1398.24+1559.54+1358.24+1636.14C1319.74+1709.94+1264.44+1772.34+1193.64+1821.44C1171.04+1837.14+1025.7+1850.54+1000+1863.54L1193.54+1864.54C1539.74+1866.44+1864.54+1693.34+1864.54+1296.64L1864.54+716.942C1866.44+312.442+1541.64+135.442+1195.44+135.442Z" />
<path style={{filter: "url(#shadow2)"}} fill="#3E82E5" opacity="1" d="M1695.54+631.442C1685.84+278.042+1409.34+135.442+1052.94+135.442L361.74+136.442L803.74+490.442L1060.74+490.442C1335.24+490.442+1335.24+835.342+1060.74+835.342L1060.74+1164.84C1150.22+1164.84+1210.53+1201.48+1241.68+1250.87C1306.07+1353+1245.76+1509.64+1060.74+1509.64L361.74+1863.54L1052.94+1864.54C1409.24+1864.54+1685.74+1721.94+1695.54+1368.54C1695.54+1205.94+1651.04+1084.44+1572.64+999.942C1651.04+915.542+1695.54+794.042+1695.54+631.442Z" />
<path style={{filter: "url(#shadow1)"}} fill="#FFFFFF" opacity="1" d="M1469.25+631.442C1459.55+278.042+1183.05+135.442+826.65+135.442L135.45+135.442L135.45+1004C135.45+1004+135.427+1255.21+355.626+1255.21C575.825+1255.21+575.848+1004+575.848+1004L577.45+490.442L834.45+490.442C1108.95+490.442+1108.95+835.342+834.45+835.342L664.65+835.342L664.65+1164.84L834.45+1164.84C923.932+1164.84+984.244+1201.48+1015.39+1250.87C1079.78+1353+1019.47+1509.64+834.45+1509.64L135.45+1509.64L135.45+1864.54L826.65+1864.54C1182.95+1864.54+1459.45+1721.94+1469.25+1368.54C1469.25+1205.94+1424.75+1084.44+1346.35+999.942C1424.75+915.542+1469.25+794.042+1469.25+631.442Z" />
</g>
</svg>;
}
}

View File

@ -1,20 +0,0 @@
import {React} from "modules";
export default class XSvg extends React.Component {
constructor(props) {
super(props);
}
render() {
return React.createElement(
"svg",
{xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 12 12", style: {width: "18px", height: "18px"}},
React.createElement(
"g",
{className: "background", fill: "none", fillRule: "evenodd"},
React.createElement("path", {d: "M0 0h12v12H0"}),
React.createElement("path", {className: "fill", fill: "#dcddde", d: "M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6"})
)
);
}
}

12
src/ui/icons/close.jsx Normal file
View File

@ -0,0 +1,12 @@
import {React} from "modules";
export default class CloseButton extends React.Component {
render() {
return <svg viewBox="0 0 12 12" style={{width: "18px", height: "18px"}}>
<g className="background" fill="none" fillRule="evenodd">
<path d="M0 0h12v12H0" />
<path className="fill" fill="#dcddde" d="M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6" />
</g>
</svg>;
}
}

View File

@ -1,21 +0,0 @@
import {React} from "modules";
export default class ReloadIcon extends React.Component {
constructor(props) {
super(props);
}
render() {
return 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"}
},
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"}),
React.createElement("path", {fill: "none", d: "M0 0h24v24H0z"})
);
}
}

11
src/ui/icons/reload.jsx Normal file
View File

@ -0,0 +1,11 @@
import {React} from "modules";
export default class ReloadIcon extends React.Component {
render() {
const size = this.props.size || "24px";
return <svg className={"bd-reload " + this.props.className} onClick={this.props.onClick} fill="#dcddde" viewBox="0 0 24 24" style={{width: size, height: size}}>
<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" />
<path fill="none" d="M0 0h24v24H0z" />
</svg>;
}
}