├── README.md
├── HideMenuIcons.theme.css
├── HideNewTimestamps.theme.css
├── OldMessagesLoader.theme.css
├── XposeLoader.theme.css
├── UploadPlaceholder.plugin.js
├── IMEFix.plugin.js
├── XposeCompanion.plugin.js
├── OldMessages.theme.css
├── URLDecode.plugin.js
├── LICENSE
├── SendTimestamps.plugin.js
├── EditUploads.plugin.js
└── XposeRAW.theme.css
/README.md:
--------------------------------------------------------------------------------
1 | # BetterDiscord Addons
2 |
3 | A collection of various BetterDiscord themes/plugins.
4 |
--------------------------------------------------------------------------------
/HideMenuIcons.theme.css:
--------------------------------------------------------------------------------
1 | //META{"name":"HideMenuIcons","description":"Hides the new dropdown menu icons","author":"PseudoResonance","version":"1.0"}*//
2 |
3 | /* Hide menu icons */
4 | .da-hint {
5 | display:none;
6 | }
7 |
--------------------------------------------------------------------------------
/HideNewTimestamps.theme.css:
--------------------------------------------------------------------------------
1 | //META{"name":"HideNewTimestamps","description":"Hides the new timestamps to the left of messages","author":"PseudoResonance","version":"1.0"}*//
2 |
3 | /* Hide timestamps */
4 | .da-timestampVisibleOnHover {
5 | display:none;
6 | }
7 |
--------------------------------------------------------------------------------
/OldMessagesLoader.theme.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @name OldMessagesLoader
3 | * @author PseudoResonance
4 | * @version 1.1
5 | * @description Restores old messages style
6 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/OldMessages.theme.css
7 | */
8 |
9 | @import url("https://pseudoresonance.github.io/BetterDiscord-Theme/OldMessages.theme.css");
10 |
11 | :root, .theme-dark, .theme-light {
12 | --message-spacing:1.0625rem;
13 | --header-height:1.375rem;
14 | }
15 |
--------------------------------------------------------------------------------
/XposeLoader.theme.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @name XposeLoader
3 | * @author PseudoResonance
4 | * @version 1.1
5 | * @description Custom theme loader.
6 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/XposeRAW.theme.css
7 | */
8 |
9 | @import url("https://pseudoresonance.github.io/BetterDiscord-Theme/XposeRAW.theme.css");
10 |
11 | :root, .theme-dark, .theme-light {
12 | --background-screen-cover: 0, 0, 0 !important; /* Color of screen over background */
13 | --background-screen-opacity: 0.55 !important; /* Value from 0-1 - 0 being least opaque, 1 being most opaque */
14 | --background-url: url("https://i.imgur.com/MoKvTtu.jpg") !important; /* URL for background */
15 | }
16 |
--------------------------------------------------------------------------------
/UploadPlaceholder.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name UploadPlaceholder
3 | * @authorLink https://github.com/PseudoResonance
4 | * @donate https://bit.ly/3hAnec5
5 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/UploadPlaceholder.plugin.js
6 | */
7 |
8 | module.exports = (() =>
9 | {
10 | const config =
11 | {
12 | info:
13 | {
14 | name: "UploadPlaceholder",
15 | authors:
16 | [
17 | {
18 | name: "PseudoResonance",
19 | discord_id: "152927763605618689",
20 | github_username: "PseudoResonance"
21 | }
22 | ],
23 | version: "2.1.1",
24 | description: "Companion plugin for Xpose theme.",
25 | github: "https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/UploadPlaceholder.plugin.js",
26 | github_raw: "https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/UploadPlaceholder.plugin.js"
27 | },
28 | changelog: [
29 | {
30 | title: "Plugin renamed to XposeCompanion",
31 | type: "fixed",
32 | items: [
33 | "UploadPlaceholder renamed to XposeCompanion",
34 | "Please delete the UploadPlaceholder plugin if XposeCompanion is working!"
35 | ]
36 | }
37 | ]
38 | };
39 |
40 | return !global.ZeresPluginLibrary ? class
41 | {
42 | constructor() { this._config = config; }
43 |
44 | getName = () => config.info.name;
45 | getAuthor = () => config.info.description;
46 | getVersion = () => config.info.version;
47 |
48 | load()
49 | {
50 | BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
51 | confirmText: "Download Now",
52 | cancelText: "Cancel",
53 | onConfirm: () =>
54 | {
55 | require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) =>
56 | {
57 | if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
58 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
59 | });
60 | }
61 | });
62 | }
63 |
64 | start() { }
65 | stop() { }
66 | } : (([Plugin, Api]) => {
67 |
68 | const plugin = (Plugin, Api) =>
69 | {
70 | const { DiscordAPI, PluginUpdater, PluginUtilities } = Api;
71 |
72 | return class UploadPlaceholder extends Plugin
73 | {
74 | constructor()
75 | {
76 | super();
77 | }
78 |
79 | onStart()
80 | {
81 | require("request").get("https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/XposeCompanion.plugin.js", async (err, res, body) =>
82 | {
83 | if (err) return require("electron").shell.openExternal("https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/XposeCompanion.plugin.js");
84 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "XposeCompanion.plugin.js"), body, { flag: 'wx' }, r));
85 | });
86 | }
87 |
88 | }
89 |
90 | };
91 | return plugin(Plugin, Api);
92 | })(global.ZeresPluginLibrary.buildPlugin(config));
93 | })();
94 |
--------------------------------------------------------------------------------
/IMEFix.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name IMEFix
3 | * @authorLink https://github.com/PseudoResonance
4 | * @donate https://bit.ly/3hAnec5
5 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/IMEFix.plugin.js
6 | */
7 |
8 | module.exports = (() =>
9 | {
10 | const config =
11 | {
12 | info:
13 | {
14 | name: "IMEFix",
15 | authors:
16 | [
17 | {
18 | name: "PseudoResonance",
19 | discord_id: "152927763605618689",
20 | github_username: "PseudoResonance"
21 | }
22 | ],
23 | version: "1.0.0",
24 | description: "Fix IME input on Discord",
25 | github: "https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/IMEFix.plugin.js",
26 | github_raw: "https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/IMEFix.plugin.js"
27 | },
28 | changelog: [
29 | {
30 | title: "Initial Release",
31 | type: "added",
32 | items: [
33 | "Fixed IME input on Discord text inputs"
34 | ]
35 | }
36 | ]
37 | };
38 |
39 | return !global.ZeresPluginLibrary ? class
40 | {
41 | constructor() { this._config = config; }
42 |
43 | getName = () => config.info.name;
44 | getAuthor = () => config.info.description;
45 | getVersion = () => config.info.version;
46 |
47 | load()
48 | {
49 | BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
50 | confirmText: "Download Now",
51 | cancelText: "Cancel",
52 | onConfirm: () =>
53 | {
54 | require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) =>
55 | {
56 | if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
57 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
58 | });
59 | }
60 | });
61 | }
62 |
63 | start() { }
64 | stop() { }
65 | } : (([Plugin, Api]) => {
66 |
67 | const plugin = (Plugin, Api) =>
68 | {
69 | const { DiscordModules, Patcher } = Api;
70 |
71 | let composition = false;
72 |
73 | let domNode = null;
74 | let cancelPatch = null;
75 |
76 | return class IMEFix extends Plugin
77 | {
78 |
79 | constructor()
80 | {
81 | super();
82 | this.onStart = this.onStart.bind(this);
83 | this.onStop = this.onStop.bind(this);
84 | this.compositionStart = this.compositionStart.bind(this);
85 | this.compositionEnd = this.compositionEnd.bind(this);
86 | this.input = this.input.bind(this);
87 | }
88 |
89 | onStart()
90 | {
91 | this.cancelPatch = BdApi.monkeyPatch(BdApi.findModule(m => m.displayName === 'SlateChannelTextArea').prototype, 'componentDidMount',
92 | {
93 | after: _ =>
94 | {
95 | this.composition = false;
96 | this.domNode = DiscordModules.ReactDOM.findDOMNode(_.thisObject);
97 | this.domNode.addEventListener("compositionstart", this.compositionStart, true);
98 | this.domNode.addEventListener("compositionend", this.compositionEnd, true);
99 | this.domNode.addEventListener("input", this.input, true);
100 | }
101 | });
102 | }
103 |
104 | onStop()
105 | {
106 | this.cancelPatch();
107 | if (this.domNode != null)
108 | {
109 | this.domNode.removeEventListener("compositionstart", this.compositionStart, true);
110 | this.domNode.removeEventListener("compositionend", this.compositionEnd, true);
111 | this.domNode.removeEventListener("input", this.input, true);
112 | }
113 | }
114 |
115 | compositionStart(event)
116 | {
117 | this.composition = true;
118 | }
119 |
120 | compositionEnd(event)
121 | {
122 | this.composition = false;
123 | }
124 |
125 | input(event)
126 | {
127 | if (this.composition)
128 | {
129 | event.stopPropagation();
130 | }
131 | }
132 |
133 | }
134 |
135 | };
136 | return plugin(Plugin, Api);
137 | })(global.ZeresPluginLibrary.buildPlugin(config));
138 | })();
139 |
--------------------------------------------------------------------------------
/XposeCompanion.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name XposeCompanion
3 | * @authorLink https://github.com/PseudoResonance
4 | * @donate https://bit.ly/3hAnec5
5 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/XposeCompanion.plugin.js
6 | */
7 |
8 | module.exports = (() => {
9 | const config = {
10 | info: {
11 | name: "XposeCompanion",
12 | authors:
13 | [{
14 | name: "PseudoResonance",
15 | discord_id: "152927763605618689",
16 | github_username: "PseudoResonance"
17 | }
18 | ],
19 | version: "2.3.0",
20 | description: "Companion plugin for Xpose theme.",
21 | github: "https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/XposeCompanion.plugin.js",
22 | github_raw: "https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/XposeCompanion.plugin.js"
23 | },
24 | changelog: [{
25 | title: "Fixed Folder Backgrounds",
26 | type: "fixed",
27 | items: [
28 | "Fixed folder colors not showing initially"
29 | ]
30 | }
31 | ],
32 | defaultConfig: [{
33 | type: 'category',
34 | id: 'appearance',
35 | name: 'Appearance Settings',
36 | collapsible: true,
37 | shown: true,
38 | settings: [{
39 | name: 'Expanded server folder has color',
40 | id: 'guildFolderColor',
41 | type: 'switch',
42 | value: 'true'
43 | }
44 | ]
45 | }
46 | ]
47 | };
48 |
49 | return !global.ZeresPluginLibrary ? class {
50 | constructor() {
51 | this._config = config;
52 | }
53 |
54 | getName = () => config.info.name;
55 | getAuthor = () => config.info.description;
56 | getVersion = () => config.info.version;
57 |
58 | load() {
59 | BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
60 | confirmText: "Download Now",
61 | cancelText: "Cancel",
62 | onConfirm: () => {
63 | require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async(err, res, body) => {
64 | if (err)
65 | return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
66 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
67 | });
68 | }
69 | });
70 | }
71 |
72 | start() {}
73 | stop() {}
74 | }
75 | : (([Plugin, Api]) => {
76 |
77 | const plugin = (Plugin, Api) => {
78 | const {
79 | DiscordModules,
80 | DOMTools,
81 | PluginUtilities,
82 | Utilities,
83 | Patcher
84 | } = Api;
85 |
86 | let guildListObserver;
87 |
88 | return class XposeCompanion extends Plugin {
89 | constructor() {
90 | super();
91 | this.updateFolderBackgrounds = this.updateFolderBackgrounds.bind(this);
92 | this.handleUserSettingsChange = this.handleUserSettingsChange.bind(this);
93 | const pluginInst = this;
94 | guildListObserver = new MutationObserver(function (mutationsList, observer) {
95 | for (const mutation of mutationsList) {
96 | if (mutation.type === 'childList') {
97 | for (const node of mutation.addedNodes) {
98 | if (pluginInst.listStartsWith(node.classList, "wrapper-")) {
99 | const entry = DOMTools.query('[class^="expandedFolderBackground-"]', node);
100 | if (entry != null) {
101 | var icon = DOMTools.query('[class^="folderIconWrapper-"]', entry.nextSibling);
102 | var backgroundColor = icon.style.backgroundColor;
103 | if (backgroundColor == "") {
104 | backgroundColor = DOMTools.query('[class^="expandedFolderIconWrapper-"] > svg', icon).style.color;
105 | backgroundColor = backgroundColor.substring(0, backgroundColor.length - 1) + ", 0.4";
106 | }
107 | entry.style.backgroundColor = backgroundColor;
108 | }
109 | } else if (pluginInst.listStartsWith(node.classList, "expandedFolderBackground-")) {
110 | var icon = DOMTools.query('[class^="folderIconWrapper-"]', node.nextSibling);
111 | var backgroundColor = icon.style.backgroundColor;
112 | if (backgroundColor == "") {
113 | backgroundColor = DOMTools.query('[class^="expandedFolderIconWrapper-"] > svg', icon).style.color;
114 | backgroundColor = "rgba" + backgroundColor.substring(3, backgroundColor.length - 1) + ", 0.4)";
115 | }
116 | node.style.backgroundColor = backgroundColor;
117 | }
118 | }
119 | } else if (mutation.type === 'attributes') {
120 | if (pluginInst.listStartsWith(mutation.target.classList, "folderIconWrapper-")) {
121 | const backgroundColor = mutation.target.style.backgroundColor;
122 | if (backgroundColor != "")
123 | DOMTools.parents(mutation.target, '[class^="listItem-"]')[0].previousSibling.style.backgroundColor = backgroundColor;
124 | } else if (mutation.target.nodeName == 'svg' && pluginInst.listStartsWith(mutation.target.parentElement.classList, "expandedFolderIconWrapper-")) {
125 | var backgroundColor = mutation.target.style.color;
126 | backgroundColor = "rgba" + backgroundColor.substring(3, backgroundColor.length - 1) + ", 0.4)";
127 | DOMTools.parents(mutation.target, '[class^="listItem-"]')[0].previousSibling.style.backgroundColor = backgroundColor;
128 | }
129 | }
130 | }
131 | });
132 | }
133 |
134 | onStart() {
135 | this.updateFolderBackgrounds();
136 | DiscordModules.UserSettingsStore.addChangeListener(this.handleUserSettingsChange);
137 | }
138 |
139 | onStop() {
140 | Patcher.unpatchAll();
141 | guildListObserver.disconnect();
142 | DiscordModules.UserSettingsStore.removeChangeListener(this.handleUserSettingsChange);
143 | }
144 |
145 | updateFolderBackgrounds() {
146 | if (this.settings.appearance.guildFolderColor) {
147 | guildListObserver.observe(document.querySelector('[data-list-id="guildsnav"]'), {
148 | subtree: true,
149 | childList: true,
150 | attributeFilter: ["style"]
151 | });
152 | const folderBackgrounds = document.querySelectorAll('[class^="expandedFolderBackground-"]');
153 | for (const folderBackground of folderBackgrounds) {
154 | const parent = folderBackground.parentElement;
155 | const colorDecimal = Utilities.findInTree(BdApi.getInternalInstance(parent).return, node => {
156 | return node?.folderNode
157 | }, {
158 | walkable: ["props", "children", "child", "sibling", "memoizedProps"]
159 | }).folderNode.color;
160 | const backgroundColor = "rgba(" + ((colorDecimal >> 16) & 0xFF) + "," + ((colorDecimal >> 8) & 0xFF) + "," + (colorDecimal & 0xFF) + ", 0.4)";
161 | folderBackground.style.backgroundColor = backgroundColor;
162 | }
163 | } else {
164 | guildListObserver.disconnect();
165 | const folderBackgrounds = document.querySelectorAll('[class^="expandedFolderBackground-"]');
166 | for (const folderBackground of folderBackgrounds) {
167 | folderBackground.style.backgroundColor = null;
168 | }
169 | }
170 | }
171 |
172 | getSettingsPanel() {
173 | const panel = this.buildSettingsPanel();
174 | return panel.getElement();
175 | }
176 |
177 | saveSettings(category, setting, value) {
178 | this.settings[category][setting] = value;
179 | PluginUtilities.saveSettings(config.info.name, this.settings);
180 | if (category === 'appearance') {
181 | if (setting === 'guildFolderColor') {
182 | this.updateFolderBackgrounds();
183 | }
184 | }
185 | }
186 |
187 | async handleUserSettingsChange() {
188 | this.updateFolderBackgrounds();
189 | }
190 |
191 | listStartsWith(list, str) {
192 | if (list) {
193 | for (const value of list.entries()) {
194 | if (value[1].startsWith(str)) {
195 | return true;
196 | }
197 | }
198 | }
199 | return false;
200 | }
201 |
202 | }
203 |
204 | };
205 | return plugin(Plugin, Api);
206 | })(global.ZeresPluginLibrary.buildPlugin(config));
207 | })();
208 |
--------------------------------------------------------------------------------
/OldMessages.theme.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @name OldMessages
3 | * @author PseudoResonance
4 | * @version 1.3.2
5 | * @description Restores old messages style
6 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/OldMessages.theme.css
7 | */
8 |
9 | :root, .theme-dark, .theme-light {
10 | --message-spacing:1.0625rem;
11 | --header-height:1.375rem;
12 | --new-message-color:#f04747;
13 | --background-mentioned-hover:transparent;
14 | --background-message-hover:transparent;
15 | --oldMessagesVersion:"1.3.2";
16 | }
17 |
18 | /* Restore Reactions */
19 | [class*="reaction-"] {
20 | background:var(--background-modifier-accent);
21 | border-radius:.25rem;
22 | border:none;
23 | margin:0 .125rem .125rem 0;
24 | padding:0;
25 | -webkit-transition:background-color .1s ease;
26 | transition:background-color .1s ease;
27 | }
28 |
29 | [class*="reaction-"]:hover, [class*="reaction-"]:active {
30 | background-color:rgba(252, 245, 255, 0.05);
31 | }
32 |
33 | [class*="reaction-"][class*="reactionMe-"] {
34 | background-color:rgba(114,137,218,.3);
35 | }
36 |
37 | [class*="reaction-"] [class*="reactionInner-"] {
38 | padding:0 .375rem;
39 | }
40 |
41 | [class*="reaction-"] [class*="reactionCount-"]:not(#foo) {
42 | color:var(--text-muted);
43 | }
44 |
45 | [class*="reaction-"]:hover [class*="reactionCount-"]:not(#foo) {
46 | color:var(--interactive-hover);
47 | }
48 |
49 | [class*="reaction-"][class*="reactionMe-"] [class*="reactionCount-"]:not(#foo), [class*="reaction-"][class*="reactionMe-"]:hover [class*="reactionCount-"]:not(#foo) {
50 | color:#7289da;
51 | }
52 |
53 | /* Disable Message Hover */
54 | [class*="messagesWrapper-"] [class*="message-"]:not([class*="mentioned-"]):hover, [class*="messagesWrapper-"] [class*="message-"]:not([class*="mentioned-"]):active, [class*="messagesWrapper-"] [class*="message-"][class*="selected-"]:not([class*="mentioned-"]) {
55 | background-color:transparent !important;
56 | }
57 |
58 | [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"], [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"]:hover, [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"]:active, [class*="messagesWrapper-"] [class*="message-"][class*="selected-"][class*="mentioned-"] {
59 | background-color:transparent !important;
60 | //background-color:var(--background-mentioned) !important; /* Replace transparent background with background-mentioned if you want to keep the new mention highlighting */
61 | }
62 |
63 | /* Hide Yellow Bar to Left of New Mention Highlighting */
64 | [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"]::before {
65 | background-color:transparent !important;
66 | }
67 |
68 | /* Mentions - Comment these two sections out if you want to keep the new mention highlighting */
69 | [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"] > [class*="contents-"] > [class*="messageContent-"] {
70 | background-color:var(--background-mentioned) !important;
71 | margin-left:-8px;
72 | padding-left:4px;
73 | height:auto;
74 | border-left:4px solid #faa61a;
75 | border-radius:3px;
76 | }
77 |
78 | [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"] > [class*="contents-"] > [class*="messageContent-"]::before {
79 | content:"";
80 | display:block;
81 | position:absolute;
82 | left:61px;
83 | height:inherit;
84 | pointer-events:none;
85 | width:2px;
86 | }
87 |
88 | /* Disable Mention Background */
89 | [class*="mention"] {
90 | border-radius:0;
91 | transition:background-color 50ms ease-out,color 50ms ease-out;
92 | -webkit-transition:background-color 50ms ease-out,color 50ms ease-out;
93 | }
94 |
95 | [class*="mention"][class*="interactive"], [class*="mention"][class*="roleMention"] {
96 | background-color:rgba(114,137,218,.1);
97 | color:#7289da;
98 | }
99 |
100 | [class*="mention"][class*="interactive"]:hover, [class*="mention"][class*="roleMention"]:hover {
101 | background-color:rgba(114,137,218,.7);
102 | color:#fff;
103 | }
104 |
105 | [class*="messagesWrapper-"] [class*="message-"][class*="mentioned-"] [class*="mention"][class*="interactive"]:hover {
106 | color:#7289da;
107 | }
108 |
109 | /* Remove Spacer Between Messages and Text Input */
110 | [class*="messagesWrapper-"] [class*="scrollerSpacer-"] {
111 | height:25px;
112 | }
113 |
114 | [class*="messagesWrapper-"] ~ form [class*="channelTextArea-"] {
115 | padding-top:20px;
116 | margin-top:0px;
117 | }
118 |
119 | [class*="messagesWrapper-"] ~ form [class*="channelTextArea-"] {
120 | border-top:thin solid var(--background-modifier-accent);
121 | border-radius:0;
122 | }
123 |
124 | /* Resize/Move Message Buttons */
125 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="button-"] {
126 | height:16px;
127 | width:16px;
128 | min-height:16px;
129 | min-width:16px;
130 | color:rgba(185, 187, 190, 0.6);
131 | }
132 |
133 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="button-"]:hover, [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="button-"]:active, [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="button-"][class*="selected-"] {
134 | color:rgba(155, 157, 160, 0.6) !important;
135 | background-color:transparent !important;
136 | }
137 |
138 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] {
139 | top:-4px;
140 | padding-right:0.875rem;
141 | }
142 |
143 | [class*="messagesWrapper-"] [class*="groupStart-"]:not([class*="compact-"]) [class*="buttonContainer-"] {
144 | top:var(--header-height);
145 | }
146 |
147 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="button-"] {
148 | padding:0px;
149 | }
150 |
151 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] div {
152 | box-shadow:none !important;
153 | border:0px !important;
154 | }
155 |
156 | /* Disable Background on Message Buttons */
157 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="wrapper-"] {
158 | background-color:transparent !important;
159 | }
160 |
161 | /* Rotate Settings Button */
162 | [class*="messagesWrapper-"] [class*="buttonContainer-"] [class*="buttons-"] [class*="button-"]:last-child:not([class*="dangerous-"]) {
163 | transform:rotate(90deg);
164 | padding-right:0px;
165 | padding-left:0px;
166 | margin-right:0px;
167 | margin-left:0px;
168 | }
169 |
170 | /* Margin on Text to Accomodate Buttons */
171 | [class*="messagesWrapper-"] [class*="message-"] [class*="markup-"]:not([contenteditable="true"])::before {
172 | content:'';
173 | width:6.75rem;
174 | height:16px;
175 | display:inline-flex;
176 | float:right;
177 | }
178 |
179 | /* Make Text Area Wider */
180 | [class*="messagesWrapper-"] [class*="message-"] {
181 | padding-right:0.875rem !important;
182 | }
183 |
184 | /* Spacer Between Message Groups */
185 | [class*="messagesWrapper-"] [class*="groupStart-"]::after {
186 | content:'';
187 | border-top:thin solid var(--background-modifier-accent);
188 | top:calc(0px - var(--message-spacing));
189 | left:1rem;
190 | right:0.875rem;
191 | display:block;
192 | position:absolute;
193 | }
194 |
195 | [class*="messagesWrapper-"] [class*="groupStart-"]:not([class*="compact-"]) {
196 | margin-top:calc(var(--message-spacing) * 2);
197 | }
198 |
199 | [class*="messagesWrapper-"] [class*="divider-"] + [class*="groupStart-"]::after {
200 | display:none;
201 | }
202 |
203 | [class*="messagesWrapper-"] [class*="divider-"] + [class*="groupStart-"]:not([class*="compact-"]) {
204 | margin-top:calc(var(--message-spacing) * 0.5);
205 | }
206 |
207 | [class*="messagesWrapper-"] [class*="divider-"] + [class*="groupStart-"][class*="compact-"] {
208 | margin-top:0;
209 | }
210 |
211 | /* Spacing Between Messages Within a Group */
212 | [class*="messagesWrapper-"] [class*="message-"] {
213 | padding-top:0px;
214 | padding-bottom:0px;
215 | }
216 |
217 | /* Message Divider */
218 | [class*="messagesWrapper-"] [class*="divider-"] {
219 | width:100%;
220 | margin-left:0;
221 | border:none;
222 | height:calc(var(--message-spacing) * 1.5) !important;
223 | }
224 |
225 | [class*="messagesWrapper-"] [class*="divider-"] span {
226 | background-color:transparent;
227 | padding:0;
228 | display:flex;
229 | align-items:center;
230 | text-align:center;
231 | font-size:14px;
232 | line-height:22px;
233 | height:calc(var(--message-spacing) * 2) !important;
234 | left:1rem;
235 | right:0.875rem;
236 | position:absolute;
237 | }
238 |
239 | [class*="messagesWrapper-"] [class*="divider-"] span::before {
240 | margin-right:8px;
241 | }
242 |
243 | [class*="messagesWrapper-"] [class*="divider-"] span::after {
244 | margin-left:8px;
245 | }
246 |
247 | [class*="messagesWrapper-"] [class*="divider-"] span::before, [class*="messagesWrapper-"] [class*="divider-"] span::after {
248 | content:'';
249 | flex:1;
250 | }
251 |
252 | [class*="messagesWrapper-"] [class*="divider-"][class*="isUnread-"] span:nth-child(2) {
253 | display:none;
254 | }
255 |
256 | /* Spacing Correction */
257 | .group-spacing-0 [class*="divider-"][class*="hasContent-"] {
258 | margin-top:4px;
259 | margin-bottom:4px;
260 | }
261 |
262 | .group-spacing-4 [class*="divider-"][class*="hasContent-"] {
263 | margin-top:8px;
264 | margin-bottom:8px;
265 | }
266 |
267 | .group-spacing-8 [class*="divider-"][class*="hasContent-"] {
268 | margin-top:12px;
269 | margin-bottom:12px;
270 | }
271 |
272 | .group-spacing-16 [class*="divider-"][class*="hasContent-"] {
273 | margin-top:20px;
274 | margin-bottom:20px;
275 | }
276 |
277 | .group-spacing-24 [class*="divider-"][class*="hasContent-"] {
278 | margin-top:28px;
279 | margin-bottom:28px;
280 | }
281 |
282 | /* New Message Color */
283 | [class*="messagesWrapper-"] [class*="divider-"][class*="isUnread-"] span {
284 | color:var(--new-message-color);
285 | }
286 |
287 | [class*="messagesWrapper-"] [class*="divider-"][class*="isUnread-"] span::before, [class*="messagesWrapper-"] [class*="divider-"][class*="isUnread-"] span::after {
288 | border-bottom:thin solid var(--new-message-color);
289 | }
290 |
291 | [class*="messagesWrapper-"] [class*="divider-"][class*="isUnread-"] span svg {
292 | display:none;
293 | }
294 |
295 | /* Timestamp Divider Color */
296 | [class*="messagesWrapper-"] [class*="divider-"]:not([class*="isUnread-"]) span::before, [class*="messagesWrapper-"] [class*="divider-"]:not([class*="isUnread-"]) span::after {
297 | border-bottom:thin solid var(--background-modifier-accent);
298 | }
299 |
300 | /* Display version info */
301 | [class*="sidebar-"] [role="tabbar"] [class*="info-"]::after {
302 | content:"Old Messages Version: v" var(--oldMessagesVersion);
303 | color:var(--text-muted);
304 | font-size:12px;
305 | line-height:16px;
306 | }
307 |
308 | .bd-addon-list [id*="OldMessagesLoader"] .bd-addon-header .bd-title .bd-meta::after {
309 | content:"Theme Version: v" var(--oldMessagesVersion);
310 | padding-left:1em;
311 | }
312 |
--------------------------------------------------------------------------------
/URLDecode.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name URLDecode
3 | * @author PseudoResonance
4 | * @version 2.0.1
5 | * @description URL/embed decoder for non-ASCII text.
6 | * @authorLink https://github.com/PseudoResonance
7 | * @donate https://bit.ly/3hAnec5
8 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/URLDecode.plugin.js
9 | * @updateUrl https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/URLDecode.plugin.js
10 | */
11 |
12 | const config = {
13 | info: {
14 | name: "URLDecode",
15 | authors:
16 | [{
17 | name: "PseudoResonance",
18 | discord_id: "152927763605618689",
19 | github_username: "PseudoResonance"
20 | }
21 | ],
22 | version: "2.0.1",
23 | description: "URL/embed decoder for non-ASCII text.",
24 | github: "https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/URLDecode.plugin.js",
25 | github_raw: "https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/URLDecode.plugin.js"
26 | },
27 | changelog: [{
28 | title: "Fixed",
29 | type: "fixed",
30 | items: [
31 | "Updated to BetterDiscord 1.9.3"
32 | ]
33 | }
34 | ],
35 | defaultConfig: [{
36 | type: 'category',
37 | id: 'general',
38 | name: 'General Settings',
39 | collapsible: true,
40 | shown: true,
41 | settings: [{
42 | name: 'Decode chat URLs',
43 | id: 'decodeChat',
44 | type: 'switch',
45 | value: 'true'
46 | }, {
47 | name: 'Decode embed titles',
48 | id: 'decodeEmbed',
49 | type: 'switch',
50 | value: 'true'
51 | }
52 | ]
53 | }
54 | ]
55 | };
56 |
57 | class Dummy {
58 | constructor() {
59 | this._config = config;
60 | }
61 | start() {}
62 | stop() {}
63 | }
64 |
65 | if (!global.ZeresPluginLibrary) {
66 | BdApi.UI.showConfirmationModal("Library Missing", `The library plugin needed for ${config.name ?? config.info.name} is missing. Please click Download Now to install it.`, {
67 | confirmText: "Download Now",
68 | cancelText: "Cancel",
69 | onConfirm: () => {
70 | require("request").get("https://betterdiscord.app/gh-redirect?id=9", async(err, resp, body) => {
71 | if (err)
72 | return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
73 | if (resp.statusCode === 302) {
74 | require("request").get(resp.headers.location, async(error, response, content) => {
75 | if (error)
76 | return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
77 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), content, r));
78 | });
79 | } else {
80 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
81 | }
82 | });
83 | }
84 | });
85 | }
86 |
87 | module.exports = !global.ZeresPluginLibrary ? Dummy : (([Plugin, Api]) => {
88 | const plugin = (Plugin, Api) => {
89 | const {
90 | PluginUtilities,
91 | Patcher
92 | } = Api;
93 |
94 | class StripInvalidTrailingEncoding {
95 | /**
96 | * https://github.com/jridgewell/strip-invalid-trailing-encoding
97 | *
98 | * MIT License
99 | *
100 | * Copyright (c) 2017 Justin Ridgewell
101 | *
102 | * Permission is hereby granted, free of charge, to any person obtaining a copy
103 | * of this software and associated documentation files (the "Software"), to deal
104 | * in the Software without restriction, including without limitation the rights
105 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
106 | * copies of the Software, and to permit persons to whom the Software is
107 | * furnished to do so, subject to the following conditions:
108 | *
109 | * The above copyright notice and this permission notice shall be included in all
110 | * copies or substantial portions of the Software.
111 | *
112 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
113 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
114 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
115 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
116 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
117 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
118 | * SOFTWARE.
119 | */
120 |
121 | /**
122 | * Parses a (possibly) hex char into its int value.
123 | * If the char is not valid hex char, returns 16.
124 | *
125 | * @param {string} char
126 | * @return {number}
127 | */
128 | static toHex(char) {
129 | const i = char.charCodeAt(0);
130 | // 0 - 9
131 | if (i >= 48 && i <= 57) {
132 | return i - 48;
133 | }
134 |
135 | const a = (i | 0x20);
136 | // Normalize A-F into a-f.
137 | if (a >= 97 && a <= 102) {
138 | return a - 87;
139 | }
140 |
141 | // Invalid Hex
142 | return 16;
143 | }
144 |
145 | /**
146 | * Determines if a '%' character occurs in the last 3 characters of a string.
147 | * If none, returns 3.
148 | *
149 | * @param {string} string
150 | * @param {number} length
151 | * @return {boolean}
152 | */
153 | static hasPercent(string, length) {
154 | if (length > 0 && string[length - 1] === '%') {
155 | return 1;
156 | }
157 | if (length > 1 && string[length - 2] === '%') {
158 | return 2;
159 | }
160 | if (length > 2 && string[length - 3] === '%') {
161 | return 0;
162 | }
163 |
164 | return 3;
165 | }
166 |
167 | /**
168 | * Strips invalid Percent Encodings that occur at the end of a string.
169 | * This is highly optimized to trim only _broken_ sequences at the end.
170 | *
171 | * Note that this **IS NOT** a string sanitizer. It will not prevent native
172 | * decodeURIComponent from throwing errors. This is only to prevent "good"
173 | * strings that were invalidly truncated in the middle of a percent encoding
174 | * from throwing. Attackers can craft strings will not be "fixed" by stripping.
175 | *
176 | * @param {string} string
177 | * @param {number} length The length of the string.
178 | * @param {number} shift Position of the rightmost %.
179 | * @return {string, stripped} Stripped string and stripped portion
180 | */
181 | static _strip(string, length, shift) {
182 | let end = length - shift;
183 | let num = -shift;
184 | let high = '8';
185 | let low = '0';
186 | let continuation = false;
187 |
188 | for (let pos = length - 1; pos >= 0; pos--) {
189 | const char = string[pos];
190 | num++;
191 |
192 | if (char !== '%') {
193 | // If we have backtracked 3 characters and we don't find a "%", we know the
194 | // string did not end in an encoding.
195 | if (num % 3 === 0) {
196 | if (continuation) {
197 | // Someone put extra continuations.
198 | return {
199 | string: "",
200 | stripped: string
201 | };
202 | }
203 |
204 | break;
205 | }
206 |
207 | // Else, we need to keep backtracking.
208 | low = high;
209 | high = char;
210 | continue;
211 | }
212 |
213 | const h = this.toHex(high);
214 | const l = this.toHex(low);
215 | if (h === 16 || l === 16) {
216 | // Someone put non hex values.
217 | return {
218 | string: "",
219 | stripped: string
220 | };
221 | }
222 |
223 | // & %26
224 | // %26 00100110
225 | // α %CE%B1
226 | // %CE 11001110
227 | // %B1 10110001
228 | // ⚡ %E2%9A%A1
229 | // %E2 11100010
230 | // %9A 10011010
231 | // %A1 10100001
232 | // 𝝰 %F0%9D%9D%B0
233 | // %F0 11110000
234 | // %9D 10011101
235 | // %9D 10011101
236 | // %B0 10110000
237 | // Single encodings are guaranteed to have a leading "0" bit in the byte.
238 | // The first of a multi sequence always starts with "11" bits, while the
239 | // "continuation"s always start with "10" bits.
240 | // Spec: http://www.ecma-international.org/ecma-262/6.0/#table-43
241 | const isSingle = (h & 8) === 0;
242 | const isContinuationStart = (~h & 12) === 0;
243 |
244 | if (isSingle || isContinuationStart) {
245 | continuation = false;
246 |
247 | // If a single is full (has 3 chars), we don't need to truncate it.
248 | // If a continuation is full (chars depends on the offset of the leftmost
249 | // "0" bit), we don't need to truncate it.
250 | let escapes = 3;
251 | if (isContinuationStart) {
252 | if ((h & 2) === 0) {
253 | escapes = 6;
254 | } else if ((h & 1) === 0) {
255 | escapes = 9;
256 | } else if ((l & 8) === 0) {
257 | escapes = 12;
258 | } else if (num > 0 && num % 3 === 0) {
259 | // Someone put random hex values together.
260 | return {
261 | string: "",
262 | stripped: string
263 | };
264 | }
265 | }
266 |
267 | if (num > escapes) {
268 | // Someone put extra continuations.
269 | return {
270 | string: "",
271 | stripped: string
272 | };
273 | }
274 |
275 | if (num < escapes) {
276 | // We're at a broken sequence, truncate to here.
277 | end = pos;
278 | }
279 |
280 | break;
281 | } else {
282 | // A trailing % does not count as a continuation.
283 | if (pos < length - 1) {
284 | continuation = true;
285 | }
286 | }
287 |
288 | // Detect possible DOS attacks. Credible strings can never be worse than
289 | // the longest (4) escape sequence (3 chars) minus one (the trim).
290 | if (num > 4 * 3 - 1) {
291 | return {
292 | string: "",
293 | stripped: string
294 | };
295 | }
296 |
297 | // Intentionally set a bad hex value
298 | high = low = 'e';
299 | }
300 |
301 | if (end === length) {
302 | return {
303 | string,
304 | stripped: ""
305 | };
306 | }
307 |
308 | return {
309 | string: string.substr(0, end),
310 | stripped: string.substr(end, length)
311 | };
312 | }
313 |
314 | static strip(string) {
315 | const length = string.length;
316 | const shift = this.hasPercent(string, length);
317 |
318 | // If no % in the last 3 chars, then the string wasn't trimmed.
319 | if (shift === 3) {
320 | return {
321 | string,
322 | stripped: ""
323 | };
324 | }
325 |
326 | return this._strip(string, length, shift);
327 | }
328 | }
329 |
330 | return class URLDecode extends Plugin {
331 | constructor() {
332 | super();
333 | this.onStart = this.onStart.bind(this);
334 | this.getSettingsPanel = this.getSettingsPanel.bind(this);
335 | this.saveSettings = this.saveSettings.bind(this);
336 | }
337 |
338 | onStart() {
339 | const msgModule = BdApi.Webpack.getModule(BdApi.Webpack.Filters.byStrings("childrenRepliedMessage"), {
340 | defaultExport: false
341 | });
342 | Patcher.before(msgModule, "Z", (_, args) => {
343 | for (const item of args) {
344 | if (item.childrenMessageContent) {
345 | const msg = item.childrenMessageContent;
346 | if (this.settings.general.decodeChat) {
347 | if (msg.props && msg.props.content && Symbol.iterator in msg.props.content) {
348 | for (const elem of msg.props.content) {
349 | if (!(elem instanceof String || typeof elem === "string")) {
350 | if (elem.props && elem.props.href) {
351 | const newUrl = this.decodeText(elem.props.href);
352 | elem.props.title = newUrl;
353 | elem.props.children.forEach((element, index, arr) => {
354 | if (element instanceof String || typeof element === "string") {
355 | arr[index] = arr[index].replace(elem.props.href, newUrl);
356 | }
357 | });
358 | } else if (elem.props && elem.props.renderTextElement && !elem.props.renderTextElementURLDecode) {
359 | const func = elem.props.renderTextElement;
360 | elem.props.renderTextElement = (e, t) => {
361 | if (!(e instanceof String || typeof e === "string") && e.props && e.props.href) {
362 | const newUrl = this.decodeText(e.props.href);
363 | e.props.title = newUrl;
364 | e.props.children.forEach((element, index, arr) => {
365 | if (element instanceof String || typeof element === "string") {
366 | arr[index] = arr[index].replace(e.props.href, newUrl);
367 | }
368 | });
369 | }
370 | return func(e, t);
371 | };
372 | elem.props.renderTextElementURLDecode = true;
373 | }
374 | }
375 | }
376 | }
377 | }
378 | if (this.settings.general.decodeEmbed) {
379 | if (msg.props && msg.props.message && msg.props.message.embeds && Symbol.iterator in msg.props.message.embeds) {
380 | for (const embed of msg.props.message.embeds) {
381 | if (embed.url) {
382 | embed.rawTitle = this.decodeText(embed.rawTitle);
383 | embed.rawDescription = this.decodeText(embed.rawDescription);
384 | }
385 | }
386 | }
387 | }
388 | }
389 | return;
390 | }
391 | });
392 | }
393 |
394 | onStop() {
395 | Patcher.unpatchAll();
396 | }
397 |
398 | decodeText(text) {
399 | if (text) {
400 | text = text.replace(/\+/g, " ");
401 | try {
402 | return decodeURIComponent(text);
403 | } catch {
404 | try {
405 | let suffix = "";
406 | if (text.endsWith("...")) {
407 | text = text.slice(0, -3);
408 | suffix = "...";
409 | }
410 | const strippedData = StripInvalidTrailingEncoding.strip(text);
411 | if (strippedData.string) {
412 | return decodeURIComponent(strippedData.string) + suffix;
413 | }
414 | } catch {}
415 | }
416 | }
417 | return text;
418 | }
419 |
420 | getSettingsPanel() {
421 | const panel = this.buildSettingsPanel();
422 | return panel.getElement();
423 | }
424 |
425 | saveSettings(category, setting, value) {
426 | this.settings[category][setting] = value;
427 | PluginUtilities.saveSettings(config.info.name, this.settings);
428 | }
429 | };
430 | }
431 | return plugin(Plugin, Api);
432 | })(global.ZeresPluginLibrary.buildPlugin(config));
433 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/SendTimestamps.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name SendTimestamps
3 | * @version 2.2.2
4 | * @description Send timestamps in your messages easily by right clicking the text input.
5 | * @author Taimoor
6 | * @authorId 220161488516546561
7 | * @authorLink https://github.com/Taimoor-Tariq
8 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/SendTimestamps.plugin.js
9 | * @updateUrl https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/SendTimestamps.plugin.js
10 | */
11 | /*@cc_on
12 | @if (@_jscript)
13 |
14 | // Offer to self-install for clueless users that try to run this directly.
15 | var shell = WScript.CreateObject("WScript.Shell");
16 | var fs = new ActiveXObject("Scripting.FileSystemObject");
17 | var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
18 | var pathSelf = WScript.ScriptFullName;
19 | // Put the user at ease by addressing them in the first person
20 | shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
21 | if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
22 | shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
23 | } else if (!fs.FolderExists(pathPlugins)) {
24 | shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
25 | } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
26 | fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
27 | // Show the user where to put plugins in the future
28 | shell.Exec("explorer " + pathPlugins);
29 | shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
30 | }
31 | WScript.Quit();
32 |
33 | @else@*/
34 |
35 | /*
36 | Licensed under GPL version 2 by Taimoor
37 | https://github.com/Taimoor-Tariq/BetterDiscordStuff/blob/main/Plugins/SendTimestamps/SendTimestamps.plugin.js
38 |
39 | GNU GENERAL PUBLIC LICENSE
40 | Version 2, June 1991
41 |
42 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
43 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
44 | Everyone is permitted to copy and distribute verbatim copies
45 | of this license document, but changing it is not allowed.
46 |
47 | Preamble
48 |
49 | The licenses for most software are designed to take away your
50 | freedom to share and change it. By contrast, the GNU General Public
51 | License is intended to guarantee your freedom to share and change free
52 | software--to make sure the software is free for all its users. This
53 | General Public License applies to most of the Free Software
54 | Foundation's software and to any other program whose authors commit to
55 | using it. (Some other Free Software Foundation software is covered by
56 | the GNU Lesser General Public License instead.) You can apply it to
57 | your programs, too.
58 |
59 | When we speak of free software, we are referring to freedom, not
60 | price. Our General Public Licenses are designed to make sure that you
61 | have the freedom to distribute copies of free software (and charge for
62 | this service if you wish), that you receive source code or can get it
63 | if you want it, that you can change the software or use pieces of it
64 | in new free programs; and that you know you can do these things.
65 |
66 | To protect your rights, we need to make restrictions that forbid
67 | anyone to deny you these rights or to ask you to surrender the rights.
68 | These restrictions translate to certain responsibilities for you if you
69 | distribute copies of the software, or if you modify it.
70 |
71 | For example, if you distribute copies of such a program, whether
72 | gratis or for a fee, you must give the recipients all the rights that
73 | you have. You must make sure that they, too, receive or can get the
74 | source code. And you must show them these terms so they know their
75 | rights.
76 |
77 | We protect your rights with two steps: (1) copyright the software, and
78 | (2) offer you this license which gives you legal permission to copy,
79 | distribute and/or modify the software.
80 |
81 | Also, for each author's protection and ours, we want to make certain
82 | that everyone understands that there is no warranty for this free
83 | software. If the software is modified by someone else and passed on, we
84 | want its recipients to know that what they have is not the original, so
85 | that any problems introduced by others will not reflect on the original
86 | authors' reputations.
87 |
88 | Finally, any free program is threatened constantly by software
89 | patents. We wish to avoid the danger that redistributors of a free
90 | program will individually obtain patent licenses, in effect making the
91 | program proprietary. To prevent this, we have made it clear that any
92 | patent must be licensed for everyone's free use or not licensed at all.
93 |
94 | The precise terms and conditions for copying, distribution and
95 | modification follow.
96 |
97 | GNU GENERAL PUBLIC LICENSE
98 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
99 |
100 | 0. This License applies to any program or other work which contains
101 | a notice placed by the copyright holder saying it may be distributed
102 | under the terms of this General Public License. The "Program", below,
103 | refers to any such program or work, and a "work based on the Program"
104 | means either the Program or any derivative work under copyright law:
105 | that is to say, a work containing the Program or a portion of it,
106 | either verbatim or with modifications and/or translated into another
107 | language. (Hereinafter, translation is included without limitation in
108 | the term "modification".) Each licensee is addressed as "you".
109 |
110 | Activities other than copying, distribution and modification are not
111 | covered by this License; they are outside its scope. The act of
112 | running the Program is not restricted, and the output from the Program
113 | is covered only if its contents constitute a work based on the
114 | Program (independent of having been made by running the Program).
115 | Whether that is true depends on what the Program does.
116 |
117 | 1. You may copy and distribute verbatim copies of the Program's
118 | source code as you receive it, in any medium, provided that you
119 | conspicuously and appropriately publish on each copy an appropriate
120 | copyright notice and disclaimer of warranty; keep intact all the
121 | notices that refer to this License and to the absence of any warranty;
122 | and give any other recipients of the Program a copy of this License
123 | along with the Program.
124 |
125 | You may charge a fee for the physical act of transferring a copy, and
126 | you may at your option offer warranty protection in exchange for a fee.
127 |
128 | 2. You may modify your copy or copies of the Program or any portion
129 | of it, thus forming a work based on the Program, and copy and
130 | distribute such modifications or work under the terms of Section 1
131 | above, provided that you also meet all of these conditions:
132 |
133 | a) You must cause the modified files to carry prominent notices
134 | stating that you changed the files and the date of any change.
135 |
136 | b) You must cause any work that you distribute or publish, that in
137 | whole or in part contains or is derived from the Program or any
138 | part thereof, to be licensed as a whole at no charge to all third
139 | parties under the terms of this License.
140 |
141 | c) If the modified program normally reads commands interactively
142 | when run, you must cause it, when started running for such
143 | interactive use in the most ordinary way, to print or display an
144 | announcement including an appropriate copyright notice and a
145 | notice that there is no warranty (or else, saying that you provide
146 | a warranty) and that users may redistribute the program under
147 | these conditions, and telling the user how to view a copy of this
148 | License. (Exception: if the Program itself is interactive but
149 | does not normally print such an announcement, your work based on
150 | the Program is not required to print an announcement.)
151 |
152 | These requirements apply to the modified work as a whole. If
153 | identifiable sections of that work are not derived from the Program,
154 | and can be reasonably considered independent and separate works in
155 | themselves, then this License, and its terms, do not apply to those
156 | sections when you distribute them as separate works. But when you
157 | distribute the same sections as part of a whole which is a work based
158 | on the Program, the distribution of the whole must be on the terms of
159 | this License, whose permissions for other licensees extend to the
160 | entire whole, and thus to each and every part regardless of who wrote it.
161 |
162 | Thus, it is not the intent of this section to claim rights or contest
163 | your rights to work written entirely by you; rather, the intent is to
164 | exercise the right to control the distribution of derivative or
165 | collective works based on the Program.
166 |
167 | In addition, mere aggregation of another work not based on the Program
168 | with the Program (or with a work based on the Program) on a volume of
169 | a storage or distribution medium does not bring the other work under
170 | the scope of this License.
171 |
172 | 3. You may copy and distribute the Program (or a work based on it,
173 | under Section 2) in object code or executable form under the terms of
174 | Sections 1 and 2 above provided that you also do one of the following:
175 |
176 | a) Accompany it with the complete corresponding machine-readable
177 | source code, which must be distributed under the terms of Sections
178 | 1 and 2 above on a medium customarily used for software interchange; or,
179 |
180 | b) Accompany it with a written offer, valid for at least three
181 | years, to give any third party, for a charge no more than your
182 | cost of physically performing source distribution, a complete
183 | machine-readable copy of the corresponding source code, to be
184 | distributed under the terms of Sections 1 and 2 above on a medium
185 | customarily used for software interchange; or,
186 |
187 | c) Accompany it with the information you received as to the offer
188 | to distribute corresponding source code. (This alternative is
189 | allowed only for noncommercial distribution and only if you
190 | received the program in object code or executable form with such
191 | an offer, in accord with Subsection b above.)
192 |
193 | The source code for a work means the preferred form of the work for
194 | making modifications to it. For an executable work, complete source
195 | code means all the source code for all modules it contains, plus any
196 | associated interface definition files, plus the scripts used to
197 | control compilation and installation of the executable. However, as a
198 | special exception, the source code distributed need not include
199 | anything that is normally distributed (in either source or binary
200 | form) with the major components (compiler, kernel, and so on) of the
201 | operating system on which the executable runs, unless that component
202 | itself accompanies the executable.
203 |
204 | If distribution of executable or object code is made by offering
205 | access to copy from a designated place, then offering equivalent
206 | access to copy the source code from the same place counts as
207 | distribution of the source code, even though third parties are not
208 | compelled to copy the source along with the object code.
209 |
210 | 4. You may not copy, modify, sublicense, or distribute the Program
211 | except as expressly provided under this License. Any attempt
212 | otherwise to copy, modify, sublicense or distribute the Program is
213 | void, and will automatically terminate your rights under this License.
214 | However, parties who have received copies, or rights, from you under
215 | this License will not have their licenses terminated so long as such
216 | parties remain in full compliance.
217 |
218 | 5. You are not required to accept this License, since you have not
219 | signed it. However, nothing else grants you permission to modify or
220 | distribute the Program or its derivative works. These actions are
221 | prohibited by law if you do not accept this License. Therefore, by
222 | modifying or distributing the Program (or any work based on the
223 | Program), you indicate your acceptance of this License to do so, and
224 | all its terms and conditions for copying, distributing or modifying
225 | the Program or works based on it.
226 |
227 | 6. Each time you redistribute the Program (or any work based on the
228 | Program), the recipient automatically receives a license from the
229 | original licensor to copy, distribute or modify the Program subject to
230 | these terms and conditions. You may not impose any further
231 | restrictions on the recipients' exercise of the rights granted herein.
232 | You are not responsible for enforcing compliance by third parties to
233 | this License.
234 |
235 | 7. If, as a consequence of a court judgment or allegation of patent
236 | infringement or for any other reason (not limited to patent issues),
237 | conditions are imposed on you (whether by court order, agreement or
238 | otherwise) that contradict the conditions of this License, they do not
239 | excuse you from the conditions of this License. If you cannot
240 | distribute so as to satisfy simultaneously your obligations under this
241 | License and any other pertinent obligations, then as a consequence you
242 | may not distribute the Program at all. For example, if a patent
243 | license would not permit royalty-free redistribution of the Program by
244 | all those who receive copies directly or indirectly through you, then
245 | the only way you could satisfy both it and this License would be to
246 | refrain entirely from distribution of the Program.
247 |
248 | If any portion of this section is held invalid or unenforceable under
249 | any particular circumstance, the balance of the section is intended to
250 | apply and the section as a whole is intended to apply in other
251 | circumstances.
252 |
253 | It is not the purpose of this section to induce you to infringe any
254 | patents or other property right claims or to contest validity of any
255 | such claims; this section has the sole purpose of protecting the
256 | integrity of the free software distribution system, which is
257 | implemented by public license practices. Many people have made
258 | generous contributions to the wide range of software distributed
259 | through that system in reliance on consistent application of that
260 | system; it is up to the author/donor to decide if he or she is willing
261 | to distribute software through any other system and a licensee cannot
262 | impose that choice.
263 |
264 | This section is intended to make thoroughly clear what is believed to
265 | be a consequence of the rest of this License.
266 |
267 | 8. If the distribution and/or use of the Program is restricted in
268 | certain countries either by patents or by copyrighted interfaces, the
269 | original copyright holder who places the Program under this License
270 | may add an explicit geographical distribution limitation excluding
271 | those countries, so that distribution is permitted only in or among
272 | countries not thus excluded. In such case, this License incorporates
273 | the limitation as if written in the body of this License.
274 |
275 | 9. The Free Software Foundation may publish revised and/or new versions
276 | of the General Public License from time to time. Such new versions will
277 | be similar in spirit to the present version, but may differ in detail to
278 | address new problems or concerns.
279 |
280 | Each version is given a distinguishing version number. If the Program
281 | specifies a version number of this License which applies to it and "any
282 | later version", you have the option of following the terms and conditions
283 | either of that version or of any later version published by the Free
284 | Software Foundation. If the Program does not specify a version number of
285 | this License, you may choose any version ever published by the Free Software
286 | Foundation.
287 |
288 | 10. If you wish to incorporate parts of the Program into other free
289 | programs whose distribution conditions are different, write to the author
290 | to ask for permission. For software which is copyrighted by the Free
291 | Software Foundation, write to the Free Software Foundation; we sometimes
292 | make exceptions for this. Our decision will be guided by the two goals
293 | of preserving the free status of all derivatives of our free software and
294 | of promoting the sharing and reuse of software generally.
295 |
296 | NO WARRANTY
297 |
298 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
299 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
300 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
301 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
302 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
303 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
304 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
305 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
306 | REPAIR OR CORRECTION.
307 |
308 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
309 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
310 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
311 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
312 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
313 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
314 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
315 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
316 | POSSIBILITY OF SUCH DAMAGES.
317 |
318 | END OF TERMS AND CONDITIONS
319 |
320 | How to Apply These Terms to Your New Programs
321 |
322 | If you develop a new program, and you want it to be of the greatest
323 | possible use to the public, the best way to achieve this is to make it
324 | free software which everyone can redistribute and change under these terms.
325 |
326 | To do so, attach the following notices to the program. It is safest
327 | to attach them to the start of each source file to most effectively
328 | convey the exclusion of warranty; and each file should have at least
329 | the "copyright" line and a pointer to where the full notice is found.
330 |
331 |
332 | Copyright (C)
333 |
334 | This program is free software; you can redistribute it and/or modify
335 | it under the terms of the GNU General Public License as published by
336 | the Free Software Foundation; either version 2 of the License, or
337 | (at your option) any later version.
338 |
339 | This program is distributed in the hope that it will be useful,
340 | but WITHOUT ANY WARRANTY; without even the implied warranty of
341 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
342 | GNU General Public License for more details.
343 |
344 | You should have received a copy of the GNU General Public License along
345 | with this program; if not, write to the Free Software Foundation, Inc.,
346 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
347 |
348 | Also add information on how to contact you by electronic and paper mail.
349 |
350 | If the program is interactive, make it output a short notice like this
351 | when it starts in an interactive mode:
352 |
353 | Gnomovision version 69, Copyright (C) year name of author
354 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
355 | This is free software, and you are welcome to redistribute it
356 | under certain conditions; type `show c' for details.
357 |
358 | The hypothetical commands `show w' and `show c' should show the appropriate
359 | parts of the General Public License. Of course, the commands you use may
360 | be called something other than `show w' and `show c'; they could even be
361 | mouse-clicks or menu items--whatever suits your program.
362 |
363 | You should also get your employer (if you work as a programmer) or your
364 | school, if any, to sign a "copyright disclaimer" for the program, if
365 | necessary. Here is a sample; alter the names:
366 |
367 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
368 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
369 |
370 | , 1 April 1989
371 | Ty Coon, President of Vice
372 |
373 | This General Public License does not permit incorporating your program into
374 | proprietary programs. If your program is a subroutine library, you may
375 | consider it more useful to permit linking proprietary applications with the
376 | library. If this is what you want to do, use the GNU Lesser General
377 | Public License instead of this License.
378 | */
379 |
380 | module.exports = (() => {
381 | const config = {
382 | info: {
383 | name: 'SendTimestamps',
384 | version: '2.2.2',
385 | description: 'Send timestamps in your messages easily by right clicking the text input.',
386 | author: 'Taimoor',
387 | authorId: '220161488516546561',
388 | authorLink: 'https://github.com/Taimoor-Tariq',
389 | github: 'https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/SendTimestamps.plugin.js',
390 | github_raw: 'https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/SendTimestamps.plugin.js',
391 | authors: [{
392 | name: 'Taimoor',
393 | discord_id: '220161488516546561'
394 | }, {
395 | name: 'PseudoResonance'
396 | }
397 | ]
398 | },
399 | changelog: [{
400 | title: 'v2.2.2 - Update',
401 | type: 'fixed',
402 | items: ['Updated to BetterDiscord 1.9.3']
403 | }
404 | ],
405 | main: 'index.js',
406 | };
407 |
408 | return !global.ZeresPluginLibrary
409 | ? class {
410 | constructor() {
411 | this._config = config;
412 | }
413 | getName() {
414 | return config.info.name;
415 | }
416 | getAuthor() {
417 | return config.info.authors.map((a) => a.name).join(', ');
418 | }
419 | getDescription() {
420 | return config.info.description;
421 | }
422 | getVersion() {
423 | return config.info.version;
424 | }
425 | load() {
426 | BdApi.UI.showConfirmationModal('Library Missing', `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
427 | confirmText: 'Download Now',
428 | cancelText: 'Cancel',
429 | onConfirm: () => {
430 | require('request').get('https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js', async(error, response, body) => {
431 | if (error)
432 | return require('electron').shell.openExternal('https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js');
433 | await new Promise((r) => require('fs').writeFile(require('path').join(BdApi.Plugins.folder, '0PluginLibrary.plugin.js'), body, r));
434 | });
435 | },
436 | });
437 | }
438 | start() {}
439 | stop() {}
440 | }
441 | : (([Plugin, Api]) => {
442 | const plugin = (Plugin, Api) => {
443 | const {
444 | PluginUtilities,
445 | Patcher,
446 | Modals,
447 | DOMTools,
448 | WebpackModules,
449 | DiscordModules: {
450 | React,
451 | MessageActions,
452 | Slider,
453 | Dropdown,
454 | SwitchRow,
455 | LocaleManager,
456 | DiscordPermissions
457 | },
458 | } = Api;
459 | let ComponentDispatch = null;
460 | let ComponentActions = null;
461 | const css = `.timestamp-button {
462 | margin-top: 4px;
463 | max-height: 40px;
464 | justify-content: center;
465 | background-color: transparent;
466 | }
467 |
468 | .timestamp-button button {
469 | min-height: 32px;
470 | min-width: 32px;
471 | background-color: transparent;
472 | }
473 | .timestamp-button svg {
474 | width: 20px;
475 | height: 20px;
476 | color: var(--interactive-normal);
477 | }
478 | .timestamp-button svg:hover {
479 | color: var(--interactive-hover);
480 | }
481 |
482 | .channel-attach-button {
483 | display: flex;
484 | margin-right: 8px;
485 | }
486 |
487 | .channel-attach-button .attachButton-_ACFSu {
488 | padding: 10px 4px;
489 | }
490 |
491 | .timestamp-input-label {
492 | font-size: 16px;
493 | color: var(--text-normal);
494 | margin-left: 4px;
495 | }
496 |
497 | .timestamp-input {
498 | font-size: 16px;
499 | box-sizing: border-box;
500 | width: 100%;
501 | border-radius: 3px;
502 | color: var(--text-normal);
503 | background-color: var(--deprecated-text-input-bg);
504 | border: 1px solid var(--deprecated-text-input-border);
505 | transition: border-color 0.2s ease-in-out;
506 | padding: 10px;
507 | margin: 8px 0 12px 0px;
508 | }
509 |
510 | .timestamp-input-dropdown {
511 | font-size: 16px;
512 | border-radius: 3px;
513 | color: var(--text-normal);
514 | background-color: var(--deprecated-text-input-bg);
515 | border: 1px solid var(--deprecated-text-input-border);
516 | transition: border-color 0.2s ease-in-out;
517 | margin: 8px 0 12px 0px;
518 | }
519 |
520 | input::-webkit-calendar-picker-indicator {
521 | width: 16px;
522 | height: 16px;
523 | background-size: contain;
524 | background-position: center;
525 | background-repeat: no-repeat;
526 | filter: invert(80%);
527 | cursor: pointer;
528 | }
529 |
530 | input[type='time']::-webkit-calendar-picker-indicator {
531 | background-image: url("data:image/svg+xml,%3Csvg role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z'%3E%3C/path%3E%3C/svg%3E");
532 | }
533 | input[type='date']::-webkit-calendar-picker-indicator {
534 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7v-5z'/%3E%3C/svg%3E");
535 | }
536 |
537 | .timestamp-formats-wrapper {
538 | position: absolute;
539 | top: 0;
540 | width: 90%;
541 | }
542 |
543 | .timestamp-formats-selector {
544 | position: absolute;
545 | bottom: 0;
546 | width: 100%;
547 | background-color: var(--background-tertiary);
548 | box-shadow: var(--elevation-high);
549 | color: var(--text-normal);
550 | border-radius: 5px;
551 | padding: 8px 12px;
552 | }
553 |
554 | .timestamp-formats-selects {
555 | display: flex;
556 | flex-direction: row;
557 | padding: 8px 12px;
558 | gap: 8px;
559 | flex-wrap: wrap;
560 | }
561 |
562 | .timestamp-formats-selects select {
563 | padding: 8px 12px;
564 | border-radius: 5px;
565 | border: 1px solid var(--background-secondary);
566 | background-color: var(--channeltextarea-background);
567 | color: var(--text-normal);
568 | font-size: 1rem;
569 | flex-grow: 1;
570 | }
571 | `;
572 |
573 | const Button = WebpackModules.getByProps('Button').Button;
574 |
575 | const canSendMessages = (channelId) => {
576 | return BdApi.findModule(BdApi.Webpack.Filters.byProps('getChannelPermissions')).canWithPartialContext(DiscordPermissions.SEND_MESSAGES, {
577 | channelId
578 | });
579 | };
580 |
581 | let unpatchButton = null;
582 |
583 | return class SendTimestamp extends Plugin {
584 | constructor() {
585 | super();
586 |
587 | this.defaultSettings = {
588 | timestampFormat: 'f',
589 | };
590 |
591 | this.forceOnRight = false;
592 | this.locale = LocaleManager.getLocale() ?? 'en';
593 |
594 | this.sendFormatOptions = {
595 | 0: 'F',
596 | };
597 |
598 | this.replaceTextAreaText = (text) => {
599 | if (!ComponentDispatch) {
600 | this.getDiscordInternals()
601 | }
602 | ComponentDispatch.dispatchToLastSubscribed(ComponentActions.CLEAR_TEXT);
603 | setImmediate(() => {
604 | ComponentDispatch.dispatchToLastSubscribed(ComponentActions.INSERT_TEXT, {
605 | content: text,
606 | plainText: text
607 | });
608 | });
609 | };
610 | }
611 |
612 | onStart() {
613 | this.getDiscordInternals();
614 | PluginUtilities.addStyle(this.getName(), css);
615 | this.patchButton();
616 | }
617 |
618 | onStop() {
619 | this.domObserver?.unsubscribeAll();
620 | PluginUtilities.removeStyle(this.getName());
621 | Patcher.unpatchAll();
622 | if (unpatchButton)
623 | unpatchButton();
624 | }
625 |
626 | load() {
627 | const myAdditions = (e) => {
628 | const pluginCard = e.target.querySelector(`#${this.getName()}-card`);
629 | if (pluginCard) {
630 | const controls = pluginCard.querySelector('.bd-controls');
631 | const changeLogButton = DOMTools.createElement(
632 | ``);
633 | changeLogButton.addEventListener('click', () => {
634 | Api.Modals.showChangelogModal(this.getName(), this.getVersion(), this._config.changelog);
635 | });
636 |
637 | if (!controls.querySelector('.bd-changelog-button') && this._config.changelog?.length > 0)
638 | controls.prepend(changeLogButton);
639 | }
640 | };
641 |
642 | this.domObserver = new Api.DOMTools.DOMObserver();
643 | this.domObserver.subscribeToQuerySelector(myAdditions, `#${this.getName()}-card`);
644 |
645 | let userSettings = this.loadSettings();
646 | if (!userSettings.usingVersion || userSettings.usingVersion < this.getVersion()) {
647 | this.saveSettings({
648 | ...this.loadSettings(),
649 | usingVersion: this.getVersion()
650 | });
651 | Api.Modals.showChangelogModal(this.getName(), this.getVersion(), this._config.changelog);
652 | }
653 | }
654 |
655 | getDiscordInternals() {
656 | ComponentDispatch = BdApi.Webpack.getModule(m => m.dispatchToLastSubscribed && m.emitter?._events?.INSERT_TEXT, {
657 | searchExports: true
658 | });
659 | let getComponentActionsKey = null;
660 | const setComponentActionsKey = (val) => {
661 | getComponentActionsKey = val;
662 | };
663 | const getComponentActionsModule = ZeresPluginLibrary.WebpackModules.getModule((m) => {
664 | for (const k in m) {
665 | for (const j in m[k]) {
666 | if (m[k][j]?.toString().includes("CLEAR_TEXT")) {
667 | setComponentActionsKey(k);
668 | return true;
669 | }
670 | }
671 | }
672 | });
673 | ComponentActions = getComponentActionsModule[getComponentActionsKey];
674 | }
675 |
676 | showTimestampModal() {
677 | const inputFormat = this.settings.timestampFormat;
678 |
679 | const getRelativeTime = (timestamp) => {
680 | const timeElapsed = timestamp - new Date(new Date().getTime() - new Date().getTimezoneOffset() * 180000);
681 | const units = {
682 | year: 24 * 60 * 60 * 1000 * 365,
683 | month: (24 * 60 * 60 * 1000 * 365) / 12,
684 | day: 24 * 60 * 60 * 1000,
685 | hour: 60 * 60 * 1000,
686 | minute: 60 * 1000,
687 | second: 1000,
688 | };
689 |
690 | for (let u in units)
691 | if (Math.abs(timeElapsed) > units[u] || u == 'second')
692 | return new Intl.RelativeTimeFormat('en', {
693 | numeric: 'auto'
694 | }).format(Math.round(timeElapsed / units[u]), u);
695 | };
696 |
697 | const updateTimeFormat = (format) => {
698 | this.settings.timestampFormat = format;
699 | this.saveSettings();
700 | };
701 |
702 | const isValidDate = (d) => {
703 | return d instanceof Date && !isNaN(d);
704 | };
705 |
706 | let inputTimestamp = new Date();
707 |
708 | class TimestampModalBody extends React.Component {
709 | constructor(props) {
710 | super(props);
711 | this.state = {
712 | timestamp: new Date(new Date(inputTimestamp).getTime() - new Date().getTimezoneOffset() * 60000),
713 | returnTimestamp: inputTimestamp,
714 | timestampFormat: inputFormat,
715 |
716 | formatOptions: [{
717 | value: 't',
718 | label: 'Short Time'
719 | }, {
720 | value: 'T',
721 | label: 'Long Time'
722 | }, {
723 | value: 'd',
724 | label: 'Short Date'
725 | }, {
726 | value: 'D',
727 | label: 'Long Date'
728 | }, {
729 | value: 'f',
730 | label: 'Short Date/Time'
731 | }, {
732 | value: 'F',
733 | label: 'Long Date/Time'
734 | }, {
735 | value: 'R',
736 | label: 'Relative Time'
737 | },
738 | ],
739 | };
740 | }
741 |
742 | componentDidMount() {
743 | this.updateFormatOptions();
744 | }
745 |
746 | componentDidUpdate(prevProps, prevState) {
747 | if (prevState.timestamp != this.state.timestamp) {
748 | inputTimestamp = this.state.returnTimestamp;
749 | this.updateFormatOptions();
750 | }
751 | }
752 |
753 | updateFormatOptions() {
754 | const time = new Date(new Date(this.state.timestamp).getTime() - new Date().getTimezoneOffset() * 2 * 60000);
755 | this.setState({
756 | formatOptions: [{
757 | value: 't',
758 | label: time.toLocaleString(undefined, {
759 | hour: '2-digit',
760 | minute: '2-digit'
761 | }).replace(' at', '')
762 | }, {
763 | value: 'T',
764 | label: time.toLocaleString(undefined, {
765 | timeStyle: 'medium'
766 | }).replace(' at', '')
767 | }, {
768 | value: 'd',
769 | label: time.toLocaleString(undefined, {
770 | dateStyle: 'short'
771 | }).replace(' at', '')
772 | }, {
773 | value: 'D',
774 | label: time.toLocaleString(undefined, {
775 | dateStyle: 'long'
776 | }).replace(' at', '')
777 | }, {
778 | value: 'f',
779 | label: time.toLocaleString(undefined, {
780 | dateStyle: 'long',
781 | timeStyle: 'short'
782 | }).replace(' at', '')
783 | }, {
784 | value: 'F',
785 | label: time.toLocaleString(undefined, {
786 | dateStyle: 'full',
787 | timeStyle: 'short'
788 | }).replace(' at', '')
789 | }, {
790 | value: 'R',
791 | label: getRelativeTime(time)
792 | },
793 | ],
794 | });
795 | }
796 |
797 | render() {
798 | const FormatDropdown = React.createElement('div', {
799 | className: 'timestamp-input-group',
800 | children: [
801 | React.createElement('label', {
802 | className: 'timestamp-input-label',
803 | children: 'Format',
804 | }),
805 | React.createElement('div', {
806 | className: 'timestamp-input-dropdown',
807 | children: [
808 | React.createElement(Dropdown, {
809 | onChange: (format) => {
810 | this.setState({
811 | timestampFormat: format,
812 | });
813 | updateTimeFormat(format);
814 | },
815 | value: this.state.timestampFormat,
816 | options: this.state.formatOptions,
817 | }),
818 | ],
819 | }),
820 | ],
821 | });
822 |
823 | const DatePicker = React.createElement('div', {
824 | className: 'timestamp-input-group',
825 | children: [
826 | React.createElement('label', {
827 | className: 'timestamp-input-label',
828 | children: 'Date',
829 | }),
830 | React.createElement('input', {
831 | className: 'timestamp-input',
832 | type: 'date',
833 | value: this.state.timestamp.toISOString().split('T')[0],
834 | onChange: (e) => {
835 | const date = new Date(`${e.target.value}T${this.state.timestamp.toISOString().split('T')[1]}`);
836 | if (isValidDate(date))
837 | this.setState({
838 | timestamp: date,
839 | returnTimestamp: new Date(new Date(date).getTime() + new Date().getTimezoneOffset() * 60000),
840 | });
841 | },
842 | }),
843 | ],
844 | });
845 |
846 | const TimePicker = React.createElement('div', {
847 | className: 'timestamp-input-group',
848 | children: [
849 | React.createElement('label', {
850 | className: 'timestamp-input-label',
851 | children: 'Time',
852 | }),
853 | React.createElement('input', {
854 | className: 'timestamp-input',
855 | type: 'time',
856 | value: this.state.timestamp.toISOString().split('T')[1].split('.')[0].split(':').slice(0, 2).join(':'),
857 | onChange: (e) => {
858 | let date = new Date(`${this.state.timestamp.toISOString().split('T')[0]}T${e.target.value}:00.000Z`);
859 | if (isValidDate(date))
860 | this.setState({
861 | timestamp: date,
862 | returnTimestamp: new Date(new Date(date).getTime() + new Date().getTimezoneOffset() * 60000),
863 | });
864 | },
865 | }),
866 | ],
867 | });
868 |
869 | return React.createElement('div', {
870 | children: [DatePicker, TimePicker, FormatDropdown],
871 | });
872 | }
873 | }
874 |
875 | Modals.showModal('Select Date and Time', [React.createElement(TimestampModalBody)], {
876 | confirmText: 'Enter',
877 | onConfirm: () => {
878 | let ts_msg = ``;
879 | if (!ComponentDispatch) {
880 | this.getDiscordInternals()
881 | }
882 | ComponentDispatch.dispatchToLastSubscribed(ComponentActions.INSERT_TEXT, {
883 | content: ts_msg,
884 | plainText: ts_msg
885 | });
886 | },
887 | });
888 | }
889 |
890 | patchButton() {
891 | unpatchButton = BdApi.ContextMenu.patch('textarea-context', (menu, props) => {
892 | menu.props.children.splice(menu.props.children.length - 1, 0, ZeresPluginLibrary.DiscordModules.React.createElement(BdApi.ContextMenu.Group, null, ZeresPluginLibrary.DiscordModules.React.createElement(BdApi.ContextMenu.Item, {
893 | id: 'timestamp',
894 | label: 'Add Timestamp',
895 | action: () => {
896 | this.showTimestampModal();
897 | },
898 | disabled: false
899 | })));
900 | })
901 | }
902 | };
903 | };
904 | return plugin(Plugin, Api);
905 | })(global.ZeresPluginLibrary.buildPlugin(config));
906 | })();
907 | /*@end@*/
908 |
--------------------------------------------------------------------------------
/EditUploads.plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name EditUploads
3 | * @authorLink https://github.com/PseudoResonance
4 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/EditUploads.plugin.js
5 | */
6 |
7 | // !!! Hey there! If you didn't come here from the BetterDiscord Discord server ( https://discord.gg/2HScm8j ) !!! //
8 | // !!! then please do not use whatever you were using that led you here, getting plugins from places other than !!! //
9 | // !!! the #plugin repo channel in the BD server can be dangerous, as they can be malicious and do bad things. !!! //
10 | module.exports = (() => {
11 |
12 | const config = {
13 | info: {
14 | name: "EditUploads",
15 | authors: [
16 | {
17 | name: "Qwerasd"
18 | },
19 | {
20 | name: "PseudoResonance",
21 | discord_id: "152927763605618689",
22 | github_username: "PseudoResonance"
23 | }
24 | ],
25 | version: "0.1.2",
26 | description: "Edit image files before uploading. Uses icons from icons8 https://icons8.com/",
27 | github: "https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/EditUploads.plugin.js",
28 | github_raw: "https://raw.githubusercontent.com/PseudoResonance/BetterDiscord-Theme/master/EditUploads.plugin.js"
29 | },
30 | changelog: [
31 | {
32 | title: "Update",
33 | type: "fixed",
34 | items: [
35 | "Updated to work with new Discord attachments system"
36 | ]
37 | }
38 | ]
39 | };
40 |
41 | return !global.ZeresPluginLibrary ? class {
42 | constructor() {
43 | this._config = config;
44 | }
45 |
46 | getName = () => config.info.name;
47 | getAuthor = () => config.info.description;
48 | getVersion = () => config.info.version;
49 |
50 | load() {
51 | BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`, {
52 | confirmText: "Download Now",
53 | cancelText: "Cancel",
54 | onConfirm: () => {
55 | require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (err, res, body) => {
56 | if (err) return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
57 | await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
58 | });
59 | }
60 | });
61 | }
62 |
63 | start() {}
64 | stop() {}
65 | } : (([Plugin, Api]) => {
66 |
67 | const plugin = (Plugin, Api) => {
68 | const { Settings, DOMTools, PluginUtilities } = Api;
69 |
70 | return class EditUploads extends Plugin {
71 |
72 | // Initialize plugin
73 | constructor() {
74 | super();
75 | this.colorWhiteClass = BdApi.findModuleByProps('colorWhite').colorWhite.split(' ')[0];
76 | this.lookLinkClass = BdApi.findModuleByProps('lookLink').lookLink.split(' ')[0];
77 | this.uploadModalClass = BdApi.findModuleByProps('uploadModal').uploadModal.split(' ')[0];
78 | this.textAreaClass = BdApi.findModuleByProps('slateTextArea').slateTextArea.split(' ')[0];
79 | this.descriptionClass = BdApi.findModuleByProps('file', 'filename', 'comment', 'description').description;
80 | this.iconClass = BdApi.findModuleByProps('file', 'filename', 'comment', 'description').icon;
81 | this.icons = {
82 | rectangle: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABZSURBVFiF7ZYxCoBAEAMT8f8/EkH8V2xOOBThmlssZtotMtkqEkAjyZ46tjvXnUAqC9u2JK1fh1k8iy4zw0ZAAAEEEEAAAQQQeC2i6mnWf+AszD0KswB+zgV/VXy+7ejmKwAAAABJRU5ErkJggg==',
83 | filledRectangle: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAIAAAACAAF+ftPjAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA3LTI5VDAxOjE3LTA3OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNy0yOVQwMToxOToyMC0wNzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNy0yOVQwMToxOToyMC0wNzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpkNjAxZjcyYS0zODNmLWFjNDktYTMwZS0xOTMwNzExMzczZmIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZDYwMWY3MmEtMzgzZi1hYzQ5LWEzMGUtMTkzMDcxMTM3M2ZiIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZDYwMWY3MmEtMzgzZi1hYzQ5LWEzMGUtMTkzMDcxMTM3M2ZiIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpkNjAxZjcyYS0zODNmLWFjNDktYTMwZS0xOTMwNzExMzczZmIiIHN0RXZ0OndoZW49IjIwMjEtMDctMjlUMDE6MTctMDc6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6OUQQGAAAAQElEQVRYw+3XIQ4AIAwDwP3/T4QQHjYUDj0mrkl1zzYyM342ADoBZtZlvADVAQAAAAAAAAAA6AfYhePLLwAAuD0/rllvKVK7PgAAAABJRU5ErkJggg==',
84 | crop: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFkSURBVFiF5ZY/TsMwGMXfBz0CQlyChQkWWOAIWSs6IFDDnx2JkyBK587cgQNwBBhgQoxItD+GuhCKGzuNEwnxpCiJ/OX9nmNbtvTfBKzHFO0AGZAlhveBN2A7VDjCKSE8BybO9hVYay0AcMm3xkBv1tYpFO1KunKvmx6TYnsVdSTtzWwk5WZ260uZ4VGovYLGwOE8d2WJHi2rOzMbRlXWnQNAF/iY+wMjX23yPwB0JQ0krWo65qVKGgDIJQ0dfCKpV/pBiVHlIeDnOv+acKEhSBIAOPXBWwlQBm88QAjeaADgLARvLIAHvnC2Jw8AnMfCkweoCk8aALioCk8WYFl4kgDAcQE+AfrRRlOvfXf9OmPEBijC8yrwGHXCJTJNN5YjMxukDrBoN3z21N2QTlnR2KdrSe8JOhiUdwjM7AHYknQiaaMmY9bbJ0n37vmxpme8QsuwzUOpVzGroK4O3P2lBdYf1CeOTDUFiQFoXAAAAABJRU5ErkJggg==',
85 | blur: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIQSURBVFiF1ZZPS1RhFMbPa1CtZjJpldIfA2nATRAhbRoNW7gI+gIFQgulIj+EG6EwCsrWkoIDgiD0BVoURGnFlCuFFKKVocxUzs+FL8xT3hv3vXdmpAMXzr3vec7znPP+ua/Z/2xAJ3DyoMjbgU/AR+BYq8kPAYvUbQFoa6WAJ+y38VaR3xLS7/4B2AGGmk1+Aah4wipwGSgCv/y3H0ChWeR5YEWqvy9jY/K9DOSaIWBKSOYAJ2MOKMn400aTDwI1n3w1qkLfoTUfUwOuNor8OLAuia/8I7YoQr8C7Y0Q8EBa+yxB/HOJn8hK3u1Xe2zrIzA5mYoKcCaLgHmp5noA7obgSmnJCzKfr3TVJ8A64LWsm540Ah5JFYMp8EOCfxgKPkL9iC2HVC852oAvPsc34HBUXNwfbMDMOrw/45wjVIBzrmZms/71hJkVQwT0ib8YSh6D7YsKiBPQJf7nDALK4p8KEaAt/51BgGIjpzFOwIb4XTExSUyx6yECPojfn0HAgPjLiVHsXTh/+i30JsM2fOtzVIF8aIIZOUhuphAwLPjpULwB56QLW8DFAOwlYDvzDwm4J1Vs+apir9++7beFHGA0Fbkkfcyf9g64C5wHjvqn4MW+/yt2MhO5iLhD/V6QxCrASEPIRcRZ4IWsiyirAtPA6aR502yvvJldM7Neqx+vq2a2ZGYvnXOboTkP1HYBffUf8YXFeiQAAAAASUVORK5CYII=',
86 | inverseBlur: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAF9SURBVFiF1ZW7SgNBGEa/UbCwMEaxMuIFRRBs7Ky0E7HxDbQMgq9hIwiCgtiLlRcQBF9DiAhWgkkhWHgpNoo5FnFJNDtLdmc24tf+8J2zw8780n8OUACG/wqeB26AEtDfaXg3cEkjF0BXJwX2ac1Wp+DrEXCAT2Ala/gcEFgEAF6BmazgOeAuBh7mFujLQuCwDXiYA9/w1QTwMMu+4ANAJYVAGcj7ENhJAQ+z7QqfAT4cBN6BKReBcwd4mBOXr695EKgB0zZO3PtdlGRS2f+M+e6yDlsC9EoqS/K15Z4kFYwxwe+B7QQWPcIlaVDSQtTAJjDvER7baRMYyUBgNIkAGQhEdtoEKhkIPCQRKGUgENlpu4Y5SY+SejzBA0lDxpi334PIEzDGPEs68wSXpNMoeGyASerLxDUBMJ5KGyh6EFhLBW+S2HOA7zrBmyQ2gWoCcABseIE3SUwAx8T/F1XgCBhrtzfxuqV+RZckzarxvN5LupZ0ZYx5Sdr5p/kC40fFCUHhtX0AAAAASUVORK5CYII=',
87 | brush: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHUSURBVFiF7dbPi01xGMfxc5oZk8IQm8m9GKMuhkgWdlIsZmEtu/FvGOyt/AkkTUhTQmYh2VKUUhKiMcbPMaaslPuymOfkdOfeaPqeyeJ+6nROz+f0vJ/nfH+dLOuqqwqEEVzHm7iuYddKwQ/hu6Wax86q4QcDBDdQQx23I3a1Svh+zBUg9JS8zRF/XxV8L76UOu9t8WvhvasCvhufAnATfS1+TxQFE6nhDXyI5HfQ3+L3xQooJuH2lPCRv3S+GnfD/4x9KeFDmC4tsYtYU/L7cSu8rziQEr7F4uYC3/Arnh9jbXQ+VVXnNbyK5I8wEPPgecQu4V48f8SelPBBvIjkT7Ch5DVKXwJmpdp6MRrLqNhemxhreWddDAfMoJEKflZnnYt31uNhxKaxIxX8aHT7E+PYFteZiDVxMiYfvMVQEngU8CASn2/jjbd8jdfYmhI+HInnMdDGHyzBX6K+XFZvh/iJuE/meb7Qxm/GfSHLssN5ns8ut4C2wtPo7lgHvxiCyaTgSF6P5HOWHqubcCEmIYxWUcBYuTuswnFM4Ed4TZxODg/glYBMWdyAil+sQvdxpCp47s/5XtazGPfhSsClAorlN4PLOJV0ff9DARtXFNjV/6Df96UwpUt/UBgAAAAASUVORK5CYII=',
88 | undo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHESURBVFiF7daxaxRREMfxWSPBSrAIKCpqQIIiWPg/WBgVEbUVLAQbU8Q/w1YEQTAgiFhExcJGggn4B1iIEEknCFoYYxHDxY/FTchx3mXv9vaSQn+wDOzsm/my896biagojOBu1fUDCWOYh51IfgKLUtudfBLLWrSdyW/gl7/1FR/wGrewf1gA02h0AGjXGu7j4DAgJvGjLeE+jOMyHmM136/g0jAgTmGp2x7AUTxL9zqmqiaaw5suvtJjiKks2TrOVwFYwNst/KN4WBLjdv6J7zjQN0QdwtOEeLBTAOOax7eBw+3+XR0WdK19FRVFsRQRLyJiJCIulgJExO586tTztOdqjtubMJH74ONOAexNgOV2X6c98A7zdTOkLcq/5FvS1naXb1WCTpvwfdrTdQFExJm0n3oBWEh7pUaAjab0qvRLHMv7ewVjg2bG8WzRaz1fx5jNms0MmLzAy4x1r5+FR/ATv3FzAIDplmbU36SE6wnQqAIxcDvOIHds6hEO9bCmnoGkJeBVm2PYqubYdS3LNIo9OJl/7IlhjGR5MmYMYSgtvxrbQCLiQkScjYiJiNjo758jYjGabXe2KIov/cT9r39bfwAudYMcsxLSggAAAABJRU5ErkJggg==',
89 | redo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHcSURBVFiF7da7ahVhFAXgfYwGG4uACDEJkRALtRGfwUabGBVsRNDKCwh5AN/AB9BCBSUIIipoE0REg72VRSQgtgEhXtAYPZ9F9oFjcpKZYSYK4mo2zL/2Xuu/zf4jagJX0Ve3Th0D8BQDf9MAvMW+v2kAPuDwZooN4iJm8AYL1mIJ58rW3FpSeDgirkTE2YjYVkDvi4jmzgOO43PO7ivuYBJjGFg1+0UcbVJ8Cu0sfg+jPTgdzONAk+IT+IllXN6ABy+xc53x53hWVXwIH7P4pQLuDfRvMD6LF1UN3Ezx6UqJTQCj+IFv2LPZelt6fJuIlav0sNVqvWtKaL2z0Os/cCTjo6bEu7TahSzM5f6PN2ygHLpO/44/odfrDLQyFi9XBeR1fFWG2NmCvQ2Kj2TNhdVjvVZgPuOhpgxExMGMr8sYeJLxWIMGTmacLWTmb3g5+/pYXWXswpfsK2ua2XpJ13LPHjRgYDpr3a+StLvrOk7VEL+QNT5hpGpyqXa8Qf757CltnK6a3ylS+CDpkTOC25nTrrOCnYKrn2R3cQb7sR392UFP5X4vJXcRJ2qJd5kYxnV8V4xl3FKylbeKKb8ZGYyIyVhp2eMRMZRD7yNiLiJmIuJxk238P/59/ALb21MuiNBdkwAAAABJRU5ErkJggg==',
90 | check: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAFMSURBVFiF7ZW/TsJQFIe/gw4+iJuWDsBr+ArGQOL/mMsiGHddpA4ii0ZHQxwdfAcvg+V9ehxaYgNFaSkdtL+tuaf3++7J6S2UKVPmv0eKgLgf7X0VvQFe/LrXiq+trxpeteZI0TvCw+5Mr1dWDQcmcEXpFibgWtMB+hE8UKHpN7zH6bqVzIBrTUfhKnoMVGiNa95TUm3uAmnguQukhecqkAWem0BW+FyBrZHZXFMuUX3zG7evP21Qte1D0Mm0K8ixX+8NFpVP/AwrARfALiLDEJAc15qDZeDzBSq8hxsioP0QNAtXuF8GPlfgs+YNVWgCASAKA8e2z2PwjsKA+CWTAQ6/DKEzMnuiPESiChhFNgS9jkpSDVxqAUi4z7/fy9z2VAIw0wmAQEWa41rveRk4LPgzilp8StgBBTnJA54626OzqmONUzi4TJk/nS/UUpvIjjxSkwAAAABJRU5ErkJggg==',
91 | eraser: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGcSURBVFiF7ZbLLgNxFMZP7SppVy6phAg7vIDnUCxq0YVWIkqUuBPPw6J7JUTUA9CEBYKQ1EK6d/tZOE3+RnUunZlVTzKbM+f7ft/MnJmMSKt8KGAcuAQqwD7QEyZ8h79VAYbCgC8r8APIAr3AqfYegcEg4bUrfwOSRj8KFI07MRwa3BLiUGdegJGg4GMN5tqBI19DAEvGM085mI8BJdU8A32hwQ1dHDhXbaFZOEDOg75btdVm4QBXQMKlR0a1ZbfwvAo/gVWFuwoBpFVPvTemkXBbRe/ApPY6gQvt3wP9Nh5zwJceGa/wCcs5RyEs8KwvcKchgFxgcLsQFviCG3jSWLi0Q03CWMxrYMVYuDXHcDUrq3Dapc4MUat1V3A1qqq4w4N23oBvuIarSUENDoCoC12Kn88zwKYnuBr1AU9qVHQSApgy4Fue4ZYQt2p4BsQazM7qpgMsNg3/J0QJiNvA877BLSFu6oUIHG6ABoAHBZ0AXcCM8Z7v+sGJ2IUQkWMR8f4nYwVGIr+YbTbDdyIyKiJ7IvLqV4hWtcqsb8bJP3WiOIYMAAAAAElFTkSuQmCC',
92 | save: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAB+gAAAfoBF4pEbwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAD7SURBVFiF7ZdNbsJADEadwjE4UdmgnACJImVTfs4LQShtbkHzunGkgIIZTwIUNd9mIsf+/DSZjGZEBqmABNgCO+BEN/0ACy/AqmPTNogPD8BeC1NgVLsY+WdqiVcuCC0ASJpGHQCyBsQyBKDVKBZAn+cKUAHrhwO4ILwAoT4ayzRcPQXAio8Nr28RmcRAiMhXaOKb8S4TkTKieam1PsVOeVd/awYeogFgALgJAEyBw+XWaygH3t0kxg5WOJrXOob6hwC49gevz99fA/8KoD7FJH03aXiaa+Cg46xvABFJdcyvZtD/sbxNnxZAfTGJ+e9vqQA23OHzDnp9/QK8BlZMPbVohgAAAABJRU5ErkJggg=='
93 | };
94 | this.tools = [{
95 | name: 'rectangle',
96 | icon: this.icons.rectangle,
97 | settings: {
98 | thickness: {
99 | type: 'range',
100 | min: 2,
101 | max: 30,
102 | default: 10
103 | },
104 | color: {
105 | type: 'color',
106 | value: '#ff0000'
107 | }
108 | },
109 | initialize: function() {
110 | this.imageCopy = document.createElement('canvas');
111 | this.imageCopyCtx = this.imageCopy.getContext('2d');
112 | },
113 | selected: async function(canvas, ctx, helpers) {
114 | ctx.lineWidth = Math.max(1, this.settings.thickness.value * (canvas.width / parseInt(canvas.style.width)));
115 | ctx.lineJoin = "round";
116 | ctx.lineCap = "round";
117 | ctx.strokeStyle = this.settings.color.value;
118 | this.imageCopy.width = canvas.width;
119 | this.imageCopy.height = canvas.height;
120 | this.imageCopyCtx.drawImage(canvas, 0, 0);
121 | },
122 | mouseDown: function(_, __, location) {
123 | this.startLoc = location;
124 | },
125 | mouseMove: function(_, ctx, location) {
126 | ctx.drawImage(this.imageCopy, 0, 0);
127 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
128 | ctx.strokeRect(x, y, w, h);
129 | },
130 | mouseUp: function(canvas, ctx, location) {
131 | ctx.drawImage(this.imageCopy, 0, 0);
132 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
133 | ctx.strokeRect(x, y, w, h);
134 | this.imageCopyCtx.drawImage(canvas, 0, 0);
135 | }
136 | },{
137 | name: 'filled rectangle',
138 | icon: this.icons.filledRectangle,
139 | settings: {
140 | thickness: {
141 | type: 'range',
142 | min: 2,
143 | max: 30,
144 | default: 10
145 | },
146 | color: {
147 | type: 'color',
148 | value: '#ff0000'
149 | }
150 | },
151 | initialize: function() {
152 | this.imageCopy = document.createElement('canvas');
153 | this.imageCopyCtx = this.imageCopy.getContext('2d');
154 | },
155 | selected: async function(canvas, ctx, helpers) {
156 | ctx.lineWidth = Math.max(1, this.settings.thickness.value * (canvas.width / parseInt(canvas.style.width)));
157 | ctx.lineJoin = "round";
158 | ctx.lineCap = "round";
159 | ctx.fillStyle = this.settings.color.value;
160 | this.imageCopy.width = canvas.width;
161 | this.imageCopy.height = canvas.height;
162 | this.imageCopyCtx.drawImage(canvas, 0, 0);
163 | },
164 | mouseDown: function(_, __, location) {
165 | this.startLoc = location;
166 | },
167 | mouseMove: function(_, ctx, location) {
168 | ctx.drawImage(this.imageCopy, 0, 0);
169 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
170 | ctx.fillRect(x, y, w, h);
171 | },
172 | mouseUp: function(canvas, ctx, location) {
173 | ctx.drawImage(this.imageCopy, 0, 0);
174 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
175 | ctx.fillRect(x, y, w, h);
176 | this.imageCopyCtx.drawImage(canvas, 0, 0);
177 | }
178 | },
179 | {
180 | name: 'draw',
181 | icon: this.icons.brush,
182 | settings: {
183 | size: {
184 | type: 'range',
185 | min: 2,
186 | max: 150,
187 | default: 10
188 | },
189 | color: {
190 | type: 'color',
191 | value: '#ff0000'
192 | }
193 | },
194 | initialize: () => {},
195 | selected: function(canvas, ctx) {
196 | ctx.strokeStyle = this.settings.color.value;
197 | ctx.lineWidth = Math.max(1, this.settings.size.value * (canvas.width / parseInt(canvas.style.width)));
198 | ctx.lineJoin = "round";
199 | ctx.lineCap = "round";
200 | },
201 | mouseDown: function(_, ctx, location) {
202 | ctx.beginPath();
203 | ctx.moveTo(location.x, location.y);
204 | },
205 | mouseMove: function(_, ctx, location) {
206 | ctx.lineTo(location.x, location.y);
207 | ctx.stroke();
208 | },
209 | mouseUp: function(_, ctx, location) {
210 | ctx.lineTo(location.x, location.y);
211 | ctx.stroke();
212 | }
213 | },
214 | {
215 | name: 'erase',
216 | icon: this.icons.eraser,
217 | settings: {
218 | size: {
219 | type: 'range',
220 | min: 2,
221 | max: 150,
222 | default: 20
223 | },
224 | },
225 | initialize: function(canvas) {
226 | this.imageCopy = document.createElement('canvas');
227 | this.imageCopy.width = canvas.width;
228 | this.imageCopy.height = canvas.height;
229 | this.imageCopyCtx = this.imageCopy.getContext('2d');
230 | this.imageCopyCtx.drawImage(canvas, 0, 0);
231 | this.scratch = document.createElement('canvas');
232 | this.scratchCtx = this.scratch.getContext('2d');
233 | this.crop = [0, 0, canvas.width, canvas.height];
234 | },
235 | selected: function(canvas) {
236 | this.scratch.width = canvas.width;
237 | this.scratch.height = canvas.height;
238 | this.scratchCtx.lineWidth = this.settings.size.value * (canvas.width / parseInt(canvas.style.width));
239 | this.scratchCtx.lineJoin = "round";
240 | this.scratchCtx.lineCap = "round";
241 | this.scratchCtx.strokeStyle = 'white';
242 | this.scratchCtx.clearRect(0, 0, this.scratch.width, this.scratch.height);
243 | },
244 | mouseDown: function(_, ctx, location) {
245 | this.scratchCtx.beginPath();
246 | this.scratchCtx.moveTo(location.x, location.y);
247 | this.scratchCtx.globalCompositeOperation = 'source-in';
248 | this.scratchCtx.drawImage(this.imageCopy, ...this.crop, 0, 0, this.scratch.width, this.scratch.height);
249 | this.scratchCtx.globalCompositeOperation = 'source-over';
250 | ctx.drawImage(this.scratch, 0, 0);
251 | },
252 | mouseMove: function(_, ctx, location) {
253 | this.scratchCtx.lineTo(location.x, location.y);
254 | this.scratchCtx.stroke();
255 | this.scratchCtx.globalCompositeOperation = 'source-in';
256 | this.scratchCtx.drawImage(this.imageCopy, ...this.crop, 0, 0, this.scratch.width, this.scratch.height);
257 | this.scratchCtx.globalCompositeOperation = 'source-over';
258 | ctx.drawImage(this.scratch, 0, 0);
259 | },
260 | mouseUp: function(_, ctx, location) {
261 | this.scratchCtx.lineTo(location.x, location.y);
262 | this.scratchCtx.stroke();
263 | const data = this.scratchCtx.getImageData(0, 0, this.scratch.width, this.scratch.height).data;
264 | for (var i = 3; i < data.length; i += 4) {
265 | if (data[i] < 255)
266 | data[i] = 0;
267 | }
268 | this.scratchCtx.putImageData(new ImageData(data, this.scratch.width), 0, 0);
269 | this.scratchCtx.globalCompositeOperation = 'source-in';
270 | this.scratchCtx.drawImage(this.imageCopy, ...this.crop, 0, 0, this.scratch.width, this.scratch.height);
271 | this.scratchCtx.globalCompositeOperation = 'source-over';
272 | ctx.drawImage(this.scratch, 0, 0);
273 | }
274 | },
275 | {
276 | name: 'blur',
277 | icon: this.icons.blur,
278 | settings: {
279 | amount: {
280 | type: 'range',
281 | min: 1,
282 | max: 9,
283 | default: 3
284 | },
285 | },
286 | initialize: function() {
287 | this.imageCopy = document.createElement('canvas');
288 | this.imageCopyCtx = this.imageCopy.getContext('2d');
289 | this.blurred = document.createElement('canvas');
290 | this.blurredCtx = this.blurred.getContext('2d');
291 | },
292 | selected: function(canvas, ctx, helpers) {
293 | ctx.lineWidth = Math.max(1, 2 * (canvas.width / parseInt(canvas.style.width)));
294 | ctx.lineJoin = "round";
295 | ctx.lineCap = "round";
296 | ctx.strokeStyle = 'white';
297 | this.imageCopy.width = canvas.width;
298 | this.imageCopy.height = canvas.height;
299 | this.imageCopyCtx.drawImage(canvas, 0, 0);
300 | const blurredCanvas = helpers.blurImage(canvas, this.settings.amount.value);
301 | this.blurred.width = canvas.width;
302 | this.blurred.height = canvas.height;
303 | this.blurredCtx.putImageData(blurredCanvas, 0, 0, 0, 0, canvas.width, canvas.height);
304 | },
305 | mouseDown: function(_, __, location) {
306 | this.startLoc = location;
307 | },
308 | mouseMove: function(_, ctx, location) {
309 | ctx.drawImage(this.imageCopy, 0, 0);
310 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
311 | ctx.drawImage(this.blurred, x, y, w, h, x, y, w, h);
312 | ctx.strokeRect(x, y, w, h);
313 | },
314 | mouseUp: function(canvas, ctx, location) {
315 | ctx.drawImage(this.imageCopy, 0, 0);
316 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
317 | ctx.drawImage(this.blurred, x, y, w, h, x, y, w, h);
318 | this.imageCopyCtx.drawImage(canvas, 0, 0);
319 | }
320 | },
321 | {
322 | name: 'inverse blur',
323 | icon: this.icons.inverseBlur,
324 | settings: {
325 | amount: {
326 | type: 'range',
327 | min: 1,
328 | max: 9,
329 | default: 3
330 | },
331 | },
332 | initialize: function() {
333 | this.imageCopy = document.createElement('canvas');
334 | this.imageCopyCtx = this.imageCopy.getContext('2d');
335 | this.blurred = document.createElement('canvas');
336 | this.blurredCtx = this.blurred.getContext('2d');
337 | },
338 | selected: async function(canvas, ctx, helpers) {
339 | ctx.lineWidth = Math.max(1, 4 * (canvas.width / parseInt(canvas.style.width)));
340 | ctx.lineJoin = "round";
341 | ctx.lineCap = "round";
342 | ctx.strokeStyle = 'white';
343 | this.imageCopy.width = canvas.width;
344 | this.imageCopy.height = canvas.height;
345 | this.imageCopyCtx.drawImage(canvas, 0, 0);
346 | const blurredCanvas = await helpers.blurImage(canvas, this.settings.amount.value);
347 | this.blurred.width = canvas.width;
348 | this.blurred.height = canvas.height;
349 | this.blurredCtx.putImageData(blurredCanvas, 0, 0, 0, 0, canvas.width, canvas.height);
350 | },
351 | mouseDown: function(_, __, location) {
352 | this.startLoc = location;
353 | },
354 | mouseMove: function(_, ctx, location) {
355 | ctx.drawImage(this.blurred, 0, 0);
356 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
357 | ctx.drawImage(this.imageCopy, x, y, w, h, x, y, w, h);
358 | ctx.strokeRect(x, y, w, h);
359 | },
360 | mouseUp: function(canvas, ctx, location) {
361 | ctx.drawImage(this.blurred, 0, 0);
362 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
363 | ctx.drawImage(this.imageCopy, x, y, w, h, x, y, w, h);
364 | this.blurredCtx.drawImage(canvas, 0, 0);
365 | }
366 | },
367 | {
368 | name: 'crop',
369 | icon: this.icons.crop,
370 | settings: {},
371 | initialize: function(_, __, tools) {
372 | this.imageCopy = document.createElement('canvas');
373 | this.imageCopyCtx = this.imageCopy.getContext('2d');
374 | this.greyed = document.createElement('canvas');
375 | this.greyedCtx = this.greyed.getContext('2d');
376 | this.eraser = tools.find(t => t.name === 'erase');
377 | },
378 | selected: async function(canvas, ctx) {
379 | ctx.lineWidth = Math.max(1, 4 * (canvas.width / parseInt(canvas.style.width)));
380 | ctx.lineJoin = "round";
381 | ctx.lineCap = "round";
382 | ctx.strokeStyle = 'white';
383 | this.imageCopy.width = canvas.width;
384 | this.imageCopy.height = canvas.height;
385 | this.imageCopyCtx.drawImage(canvas, 0, 0);
386 | this.greyed.width = canvas.width;
387 | this.greyed.height = canvas.height;
388 | this.greyedCtx.drawImage(canvas, 0, 0);
389 | this.greyedCtx.globalAlpha = 0.6;
390 | this.greyedCtx.fillRect(0, 0, canvas.width, canvas.height);
391 | this.greyedCtx.globalAlpha = 1;
392 | },
393 | mouseDown: function(_, __, location) {
394 | this.startLoc = location;
395 | },
396 | mouseMove: function(_, ctx, location) {
397 | ctx.drawImage(this.greyed, 0, 0);
398 | const [x, y, w, h] = [this.startLoc.x, this.startLoc.y, location.x - this.startLoc.x, location.y - this.startLoc.y];
399 | ctx.drawImage(this.imageCopy, x, y, w, h, x, y, w, h);
400 | ctx.strokeRect(x, y, w, h);
401 | },
402 | mouseUp: function(canvas, ctx, location, helpers) {
403 | const [x, y, w, h] = [
404 | this.startLoc.x >= location.x ? location.x : this.startLoc.x,
405 | this.startLoc.y >= location.y ? location.y : this.startLoc.y,
406 | Math.abs(location.x - this.startLoc.x),
407 | Math.abs(location.y - this.startLoc.y)
408 | ];
409 | if (w < 2 || h < 2)
410 | return;
411 | canvas.width = w;
412 | canvas.height = h;
413 | ctx.drawImage(this.imageCopy, x, y, w, h, 0, 0, w, h);
414 | this.imageCopy.width = w;
415 | this.imageCopy.height = h;
416 | this.imageCopyCtx.drawImage(canvas, 0, 0);
417 | this.greyed.width = canvas.width;
418 | this.greyed.height = canvas.height;
419 | this.greyedCtx.drawImage(canvas, 0, 0);
420 | this.greyedCtx.globalAlpha = 0.6;
421 | this.greyedCtx.fillRect(0, 0, canvas.width, canvas.height);
422 | this.greyedCtx.globalAlpha = 1;
423 | this.eraser.crop = [x, y, w, h];
424 | helpers.fitCanvas(canvas, canvas.parentNode);
425 | ctx.lineWidth = Math.max(1, 4 * (canvas.width / parseInt(canvas.style.width)));
426 | ctx.lineJoin = "round";
427 | ctx.lineCap = "round";
428 | ctx.strokeStyle = 'white';
429 | }
430 | }
431 | ];
432 | this.restoreToolDefaults();
433 | this.mouseCurrentlyDown = false;
434 | this.toolHelpers = {
435 | getDPI: function() {
436 | const div = document.createElement('div');
437 | div.style.width = '1in';
438 | div.style.height = '0';
439 | document.body.appendChild(div);
440 | const dpi = div.offsetWidth * window.devicePixelRatio;
441 | div.remove();
442 | return dpi;
443 | },
444 | blurImage: function(image, amount) {
445 | const {
446 | width,
447 | height
448 | } = image;
449 | const scaleFactor = this.getDPI();
450 | const dpiFactor = scaleFactor / 96;
451 | const canvas = document.createElement('canvas');
452 | canvas.width = width;
453 | canvas.height = height;
454 | const ctx = canvas.getContext('2d');
455 | ctx.drawImage(image, 0, 0, width, height, 0, 0, width / dpiFactor, height / dpiFactor);
456 | ctx.filter = `blur(${amount}px)`;
457 | ctx.drawImage(image, 0, 0, width, height, 0, 0, width / dpiFactor, height / dpiFactor);
458 | ctx.filter = '';
459 | ctx.drawImage(canvas, 0, 0, width / dpiFactor, height / dpiFactor, 0, 0, width, height);
460 | return ctx.getImageData(0, 0, width, height);
461 | },
462 | fitCanvas: function(canvas, wrapper) {
463 | const {
464 | width,
465 | height
466 | } = wrapper.getBoundingClientRect();
467 | const canvasAspectRatio = canvas.width / canvas.height;
468 | const wrapperAspectRatio = width / height;
469 | wrapperAspectRatio >= canvasAspectRatio ?
470 | (canvas.style.height = height + 'px') && (canvas.style.width = (height * canvasAspectRatio) + 'px') :
471 | (canvas.style.width = width + 'px') && (canvas.style.height = (width / canvasAspectRatio) + 'px');
472 | }
473 | };
474 | }
475 |
476 | // Start plugin
477 | onStart() {
478 | this.cancelPatch = BdApi.monkeyPatch(BdApi.findModule(m => m.displayName === 'UploadModal').prototype, 'render', {
479 | after: _ => {
480 | requestAnimationFrame(this.addButtonToDescription.bind(this));
481 | }
482 | });
483 | this.cancelPatchAttachment = BdApi.monkeyPatch(BdApi.findModule(m => m.displayName === 'UploadAttachmentModal').prototype, 'render', {
484 | after: _ => {
485 | requestAnimationFrame(this.addButtonToDescription.bind(this));
486 | }
487 | });
488 | PluginUtilities.addStyle('EditUploads-CSS', `
489 | #EditUploadsEditButton {
490 | padding: 0;
491 | background: none;
492 | background-color: transparent;
493 | }
494 |
495 | #EditUploadsEditButton span {
496 | font-size: 127%;
497 | }
498 |
499 | #EditUploadsEditButton:hover span {
500 | text-decoration: underline;
501 | }
502 |
503 | #EditUploadsModal {
504 | z-index: 9999;
505 | width: 90%;
506 | height: 90%;
507 | position: fixed;
508 | display: block;
509 | top: 5%;
510 | left: 5%;
511 | background-color: rgb(40,42,47);
512 | box-shadow: 0 0 40px rgba(0,0,0,0.75);
513 | border-radius: 1em;
514 | color: white;
515 | overflow: hidden;
516 | }
517 |
518 | #EditUploadsToolbar {
519 | background-color: rgb(20,21,23);
520 | padding: 1em 0 0.5em;
521 | text-align: center;
522 | position: absolute;
523 | bottom: 0;
524 | width: 100%;
525 | }
526 |
527 | #EditUploadsToolbar .tool {
528 | margin-right: 5px;
529 | margin-left: 5px;
530 | cursor: pointer;
531 | padding: 5px 4px 3px;
532 | border-radius: 5px;
533 | }
534 |
535 | #EditUploadsToolbar .tool:hover {
536 | background-color: rgba(255,255,255,0.25);
537 | }
538 |
539 | #EditUploadsToolbar .toolIconWrapper {
540 | position: relative;
541 | }
542 |
543 | #EditUploadsToolbar .toolIconWrapper:hover::after {
544 | content: attr(aria-label);
545 | text-transform: capitalize;
546 | background: black;
547 | border: 2px solid #222;
548 | border-radius: 5px;
549 | padding: 0.25ex 1ch;
550 | position: absolute;
551 | bottom: calc(28px + 0.75ch); left: 0;
552 | transform: translateX(calc(21px - 50%));
553 | box-shadow: 0 2px 5px rgba(0,0,0,0.35);
554 | z-index: 999999;
555 | white-space: nowrap;
556 | }
557 |
558 | #EditUploadsToolbar .selected .tool {
559 | background-color: rgba(255,255,255,0.25);
560 | }
561 |
562 | #EditUploadsToolbar .tool:active {
563 | background-color: rgba(0,0,0,0.75);
564 | }
565 |
566 | #EditUploadsToolbar .toolSettings {
567 | display: none;
568 | position: relative;
569 | top: -8px;
570 | text-transform: capitalize;
571 | padding-right: 0.5ch;
572 | border-right: 1px solid rgba(255,255,255,0.25);
573 | }
574 |
575 | #EditUploadsModal .toolSettings input {
576 | margin: 0 1ch;
577 | position: relative;
578 | top: 3.5px;
579 | padding: 0;
580 | border: none;
581 | }
582 |
583 | #EditUploadsModal .toolSettings input[type="color"] {
584 | top: 7px;
585 | background: none;
586 | }
587 |
588 | #EditUploadsModal .toolSettings input[type="color"]::-webkit-color-swatch-wrapper {
589 | padding: 0;
590 | }
591 |
592 | #EditUploadsModal .toolSettings input[type="color"]::-webkit-color-swatch {
593 | border: none;
594 | border-radius: 5px;
595 | }
596 |
597 | #EditUploadsModal .selected .toolSettings {
598 | display: inline;
599 | }
600 |
601 | #EditUploadsModal .dividerLeft,
602 | #EditUploadsModal .dividerRight {
603 | position: relative;
604 | }
605 |
606 | #EditUploadsModal .dividerLeft::before {
607 | content: "";
608 | width: 1px;
609 | height: 100%;
610 | position: absolute;
611 | left: 0;
612 | top: -8px;
613 | background-color: rgba(255,255,255,0.25);
614 | }
615 |
616 | #EditUploadsModal .dividerRight::after {
617 | content: "";
618 | width: 1px;
619 | height: 100%;
620 | position: absolute;
621 | right: 0;
622 | top: -8px;
623 | background-color: rgba(255,255,255,0.25);
624 | }
625 |
626 | #EditUploadsCanvasWrapper {
627 | position: absolute;
628 | height: calc(100% - 14px - 2.5em);
629 | width: 100%;
630 | left: 0;
631 | top: 0;
632 | text-align: center;
633 | }
634 |
635 | #EditUploadsCanvas {
636 | display: inline;
637 | position: relative;
638 | top: 50%;
639 | transform: translateY(-50%);
640 | }
641 | `);
642 | }
643 |
644 | // Stop plugin
645 | onStop() {
646 | this.cancelPatch();
647 | this.cancelPatchAttachment();
648 | PluginUtilities.removeStyle('EditUploads-CSS');
649 | }
650 |
651 | clamp(num, min, max) {
652 | return Math.min(Math.max(num, min), max);
653 | }
654 | createIcon(icon, label) {
655 | const iconWrapper = document.createElement('span');
656 | const img = document.createElement('img');
657 | img.src = icon;
658 | img.className = 'tool';
659 | img.width = 20;
660 | img.height = 20;
661 | iconWrapper.setAttribute('aria-label', label);
662 | iconWrapper.className = 'toolIconWrapper';
663 | iconWrapper.appendChild(img);
664 | return iconWrapper;
665 | }
666 | createToolbarButton(icon, label) {
667 | const span = document.createElement('span');
668 | const iconWrapper = this.createIcon(icon, label);
669 | span.className = 'toolWrapper';
670 | span.appendChild(iconWrapper);
671 | return span;
672 | }
673 | createUndo(toolbar, canvas, ctx, that) {
674 | const eraser = that.tools.find(t => t.name === 'erase');
675 | const span = document.createElement('span');
676 | const undoButton = this.createToolbarButton(this.icons.undo, 'undo');
677 | const canvasHistory = [
678 | [ctx.getImageData(0, 0, canvas.width, canvas.height), [0, 0, canvas.width, canvas.height]]
679 | ];
680 | const undoHistory = [];
681 | const undo = () => {
682 | if (!canvasHistory.length)
683 | return;
684 | const [last, lastCrop] = canvasHistory.pop();
685 | if (last) {
686 | undoHistory.push([ctx.getImageData(0, 0, canvas.width, canvas.height), eraser.crop]);
687 | [canvas.width, canvas.height] = [last.width, last.height];
688 | ctx.putImageData(last, 0, 0);
689 | that.fitCanvas(canvas, canvas.parentNode);
690 | eraser.crop = lastCrop;
691 | that.currentTool.selected(canvas, ctx, that.toolHelpers);
692 | }
693 | };
694 | undoButton.addEventListener('click', undo);
695 | span.appendChild(undoButton);
696 | const redoButton = this.createToolbarButton(this.icons.redo, 'redo');
697 | const redo = () => {
698 | if (!undoHistory.length)
699 | return;
700 | const [next, nextCrop] = undoHistory.pop();
701 | if (next) {
702 | canvasHistory.push([ctx.getImageData(0, 0, canvas.width, canvas.height), eraser.crop]);
703 | [canvas.width, canvas.height] = [next.width, next.height];
704 | ctx.putImageData(next, 0, 0);
705 | that.fitCanvas(canvas, canvas.parentNode);
706 | eraser.crop = nextCrop;
707 | that.currentTool.selected(canvas, ctx, that.toolHelpers);
708 | }
709 | };
710 | redoButton.addEventListener('click', redo);
711 | span.appendChild(redoButton);
712 | span.style.marginRight = '5px';
713 | span.classList.add('dividerRight');
714 | toolbar.appendChild(span);
715 | const undoFunc = () => {
716 | undoHistory.length = 0;
717 | canvasHistory.push([ctx.getImageData(0, 0, canvas.width, canvas.height), eraser.crop]);
718 | if (canvasHistory.length > 20) {
719 | canvasHistory.shift();
720 | }
721 | };
722 | return [undoFunc, undo, redo];
723 | }
724 | createSaveDefaultSettings() {
725 | const span = document.createElement('span');
726 | span.style.marginLeft = '5px';
727 | span.style.paddingLeft = '5px';
728 | span.classList.add('dividerLeft');
729 | const button = this.createToolbarButton(this.icons.save, 'save tool settings');
730 | button.addEventListener('click', _ => {
731 | this.tools.forEach((tool) => {
732 | for (var key in tool.settings) {
733 | const setting = tool.settings[key];
734 | if (setting.value) {
735 | this.setToolDefault(tool.name, key, setting.value);
736 | setting.default = setting.value;
737 | }
738 | }
739 | });
740 | });
741 | span.appendChild(button);
742 | return span;
743 | }
744 | restoreToolDefaults() {
745 | this.tools.forEach((tool) => {
746 | for (var key in tool.settings) {
747 | const setting = tool.settings[key];
748 | setting.default = this.getToolDefault(tool.name, key) || setting.default;
749 | }
750 | });
751 | }
752 | setToolDefault(tool, setting, value) {
753 | return BdApi.saveData('EditUploads', `${tool}_${setting}_default`, value);
754 | }
755 | getToolDefault(tool, setting) {
756 | return BdApi.loadData('EditUploads', `${tool}_${setting}_default`);
757 | }
758 | createToolSettings(tool, canvas, ctx) {
759 | const span = document.createElement('span');
760 | span.className = 'toolSettings';
761 | for (var key in tool.settings) {
762 | const setting = tool.settings[key];
763 | span.appendChild(document.createTextNode(' ' + key));
764 | const input = Object.assign(document.createElement('input'), setting);
765 | if (setting.default) {
766 | input.value = setting.default;
767 | setting.value = setting.default;
768 | }
769 | input.addEventListener('input', _ => {
770 | setting.value = input.value;
771 | tool.selected(canvas, ctx, this.toolHelpers);
772 | });
773 | span.appendChild(input);
774 | }
775 | return span;
776 | }
777 | createTools(toolbar, canvas, ctx) {
778 | const [undoChangeFunction, undoFunc, redoFunc] = this.createUndo(toolbar, canvas, ctx, this);
779 | this.currentTool = null;
780 | this.tools.forEach(function(tool) {
781 | tool.initialize(canvas, ctx, this.tools);
782 | const item = this.createToolbarButton(tool.icon, tool.name);
783 | item.appendChild(this.createToolSettings(tool, canvas, ctx));
784 | item.firstElementChild.firstElementChild.addEventListener('click', _ => {
785 | if (this.currentTool === tool) {
786 | this.currentTool = null;
787 | const selected = toolbar.querySelector('.toolWrapper.selected');
788 | if (selected)
789 | selected.classList.remove('selected');
790 | return;
791 | }
792 | const selected = toolbar.querySelector('.toolWrapper.selected');
793 | if (selected)
794 | selected.classList.remove('selected');
795 | tool.selected(canvas, ctx, this.toolHelpers);
796 | this.currentTool = tool;
797 | item.classList.add('selected');
798 | });
799 | toolbar.appendChild(item);
800 | }.bind(this));
801 | canvas.addEventListener('mousedown', e => {
802 | this.mouseCurrentlyDown = true;
803 | if (!this.currentTool)
804 | return;
805 | undoChangeFunction();
806 | const scaleFactor = canvas.width / parseInt(canvas.style.width);
807 | const {
808 | left: x,
809 | top: y
810 | } = canvas.getBoundingClientRect();
811 | const canvasX = (e.clientX - x) * scaleFactor;
812 | const canvasY = (e.clientY - y) * scaleFactor;
813 | const boundedX = this.clamp(canvasX, 0, canvas.width);
814 | const boundedY = this.clamp(canvasY, 0, canvas.height);
815 | this.currentTool.mouseDown.bind(this.currentTool)(canvas, ctx, {
816 | x: boundedX,
817 | y: boundedY
818 | }, this.toolHelpers);
819 | });
820 | const mousemoveHandler = e => {
821 | if (!this.currentTool)
822 | return;
823 | if (this.mouseCurrentlyDown) {
824 | const scaleFactor = canvas.width / parseInt(canvas.style.width);
825 | const {
826 | left: x,
827 | top: y
828 | } = canvas.getBoundingClientRect();
829 | const canvasX = (e.clientX - x) * scaleFactor;
830 | const canvasY = (e.clientY - y) * scaleFactor;
831 | const boundedX = this.clamp(canvasX, 0, canvas.width);
832 | const boundedY = this.clamp(canvasY, 0, canvas.height);
833 | this.currentTool.mouseMove.bind(this.currentTool)(canvas, ctx, {
834 | x: boundedX,
835 | y: boundedY
836 | }, this.toolHelpers);
837 | }
838 | };
839 | document.body.addEventListener('mousemove', mousemoveHandler);
840 | const mouseupHandler = e => {
841 | if (!this.mouseCurrentlyDown)
842 | return;
843 | this.mouseCurrentlyDown = false;
844 | if (!this.currentTool)
845 | return;
846 | const scaleFactor = canvas.width / parseInt(canvas.style.width);
847 | const {
848 | left: x,
849 | top: y
850 | } = canvas.getBoundingClientRect();
851 | const canvasX = (e.clientX - x) * scaleFactor;
852 | const canvasY = (e.clientY - y) * scaleFactor;
853 | const boundedX = this.clamp(canvasX, 0, canvas.width);
854 | const boundedY = this.clamp(canvasY, 0, canvas.height);
855 | this.currentTool.mouseUp.bind(this.currentTool)(canvas, ctx, {
856 | x: boundedX,
857 | y: boundedY
858 | }, this.toolHelpers);
859 | };
860 | document.body.addEventListener('mouseup', mouseupHandler);
861 | return [{
862 | up: mouseupHandler,
863 | move: mousemoveHandler
864 | }, undoFunc, redoFunc];
865 | }
866 | createModal(canvas, ctx, confirm) {
867 | const cleanup = () => {
868 | modal.remove();
869 | document.removeEventListener('keydown', keyDownHandler);
870 | document.body.removeEventListener('mouseup', handlers.up);
871 | document.body.removeEventListener('mousemove', handlers.move);
872 | //@ts-ignore
873 | const textArea = document.getElementsByClassName(this.uploadModalClass)[0].getElementsByClassName(this.textAreaClass)[0];
874 | if (textArea) {
875 | textArea.focus();
876 | }
877 | };
878 | const modal = document.createElement('div');
879 | modal.id = 'EditUploadsModal';
880 | const toolbar = document.createElement('div');
881 | toolbar.id = 'EditUploadsToolbar';
882 | const [handlers, undoFunc, redoFunc] = this.createTools(toolbar, canvas, ctx);
883 |
884 | function keyDownHandler(e) {
885 | if (e.keyCode === 13)
886 | e.preventDefault(), e.stopPropagation();
887 | if (e.keyCode === 27)
888 | e.preventDefault(), e.stopPropagation(), cleanup();
889 | if (e.keyCode === 90 /*z*/ && (e.ctrlKey || e.metaKey)) {
890 | undoFunc();
891 | return;
892 | }
893 | if (e.keyCode === 89 /*y*/ && (e.ctrlKey || e.metaKey)) {
894 | redoFunc();
895 | return;
896 | }
897 | if (e.keyCode === 83 /*s*/ && (e.ctrlKey || e.metaKey)) {
898 | confirm();
899 | cleanup();
900 | return;
901 | }
902 | }
903 | document.addEventListener('keydown', keyDownHandler);
904 | toolbar.appendChild(this.createSaveDefaultSettings());
905 | const confirmButton = this.createToolbarButton(this.icons.check, 'done');
906 | confirmButton.addEventListener('click', _ => {
907 | confirm();
908 | cleanup();
909 | });
910 | toolbar.appendChild(confirmButton);
911 | modal.appendChild(toolbar);
912 | const canvasWrapper = document.createElement('div');
913 | canvasWrapper.id = 'EditUploadsCanvasWrapper';
914 | canvas.id = 'EditUploadsCanvas';
915 | canvasWrapper.appendChild(canvas);
916 | modal.appendChild(canvasWrapper);
917 | const closeButton = document.createElement('span');
918 | closeButton.innerText = 'X';
919 | closeButton.style.position = 'absolute';
920 | closeButton.style.top = '2ch';
921 | closeButton.style.right = '3ex';
922 | closeButton.style.cursor = 'pointer';
923 | closeButton.style.zIndex = '100';
924 | closeButton.addEventListener('click', cleanup);
925 | modal.appendChild(closeButton);
926 | return modal;
927 | }
928 | fitCanvas(canvas, wrapper) {
929 | const {
930 | width,
931 | height
932 | } = wrapper.getBoundingClientRect();
933 | const canvasAspectRatio = canvas.width / canvas.height;
934 | const wrapperAspectRatio = width / height;
935 | wrapperAspectRatio >= canvasAspectRatio ?
936 | (canvas.style.height = height + 'px') && (canvas.style.width = (height * canvasAspectRatio) + 'px') :
937 | (canvas.style.width = width + 'px') && (canvas.style.height = (width / canvasAspectRatio) + 'px');
938 | }
939 | editFile(file, modalWrapper) {
940 | return new Promise(resolve => {
941 | const reader = new FileReader();
942 | const canvas = document.createElement('canvas');
943 | const ctx = canvas.getContext('2d');
944 | const image = new Image();
945 | image.onload = _ => {
946 | canvas.width = image.width;
947 | canvas.height = image.height;
948 | ctx.drawImage(image, 0, 0);
949 | modalWrapper.parentNode.appendChild(this.createModal(canvas, ctx, _ => {
950 | canvas.toBlob(blob => {
951 | resolve(new File([blob], file.name, {
952 | type: file.type
953 | }));
954 | });
955 | }));
956 | const canvasWrapper = document.getElementById('EditUploadsCanvasWrapper');
957 | this.fitCanvas(canvas, canvasWrapper);
958 | };
959 | reader.onload = _ => {
960 | image.src = reader.result;
961 | };
962 | reader.readAsDataURL(file);
963 | });
964 | }
965 | startEdit() {
966 | const uploadModal = document.getElementsByClassName(this.uploadModalClass)[0];
967 | const modalWrapper = uploadModal.parentNode.parentNode.parentNode;
968 | const reactInstance = this.getReactInstance(uploadModal);
969 | const stateNode = reactInstance.return.stateNode;
970 | const icon = uploadModal.getElementsByClassName(this.iconClass)[0];
971 | const newNodeFormat = typeof reactInstance.return.stateNode.props.upload.item.file != "undefined";
972 | const file = (newNodeFormat ? reactInstance.return.stateNode.props.upload.item.file : reactInstance.return.stateNode.props.upload.file);
973 | this.editFile(file, modalWrapper)
974 | .then((newFile) => {
975 | stateNode.setState({
976 | upload: Object.assign(newNodeFormat ? stateNode.props.upload.item : stateNode.props.upload, {
977 | file: newFile,
978 | size: newFile.size
979 | }),
980 | file: newFile
981 | });
982 | if (this.getReactInstance(icon).return.stateNode != null)
983 | this.getReactInstance(icon).return.stateNode.forceUpdate();
984 | });
985 | }
986 | createButton() {
987 | const button = document.createElement('button');
988 | button.id = 'EditUploadsEditButton';
989 | button.className = 'button';
990 | button.style.minWidth = '0px';
991 | const span = document.createElement('span');
992 | span.innerText = 'Edit';
993 | span.className = `${this.colorWhiteClass} ${this.lookLinkClass}`;
994 | button.appendChild(span);
995 | button.addEventListener('click', this.startEdit.bind(this));
996 | return button;
997 | }
998 | addButtonToDescription() {
999 | const reactInstace = this.getReactInstance(document.getElementsByClassName(this.uploadModalClass)[0]);
1000 | if (!(reactInstace && reactInstace.return.stateNode.props.upload && reactInstace.return.stateNode.props.upload.isImage))
1001 | return;
1002 | if (document.getElementById('EditUploadsEditButton'))
1003 | return;
1004 | const desc = document.getElementsByClassName(this.descriptionClass)[0];
1005 | desc.appendChild(this.createButton());
1006 | }
1007 | getReactInstance(element) {
1008 | return BdApi.getInternalInstance(element);
1009 | }
1010 | }
1011 | };
1012 |
1013 | return plugin(Plugin, Api);
1014 | })(global.ZeresPluginLibrary.buildPlugin(config));
1015 | })();
1016 |
--------------------------------------------------------------------------------
/XposeRAW.theme.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @name XposeRAW
3 | * @author PseudoResonance
4 | * @version 3.2.2
5 | * @description Custom theme.
6 | * @source https://github.com/PseudoResonance/BetterDiscord-Theme/blob/master/XposeRAW.theme.css
7 | */
8 |
9 | :root, .theme-dark, .theme-light {
10 | --background-screen-cover:0, 0, 0; /* Color of screen over background */
11 | --background-screen-opacity:0.45; /* Value from 0-1 - 0 being least opaque, 1 being most opaque */
12 | --background-url:url("https://i.imgur.com/avzaqfC.jpg"); /* URL for background */
13 | --message-spacing:1.0625rem;
14 | --header-height:1.375rem;
15 | }
16 |
17 | /*
18 | * Variables
19 | */
20 |
21 | :root, .theme-dark, .theme-light {
22 | --xposeVersion:"3.2.2";
23 | --blur-radius:10px;
24 | }
25 |
26 | /*
27 | * Theme Colors
28 | */
29 |
30 | .theme-dark {
31 | --header-primary:#fff;
32 | --header-secondary:#b9bbbe;
33 | --text-normal:#dcddde;
34 | --text-muted:#72767d;
35 | --text-link:#00b0f4;
36 | --channels-default:#c9cccf;
37 | --interactive-normal:#d5d6d8;
38 | --interactive-hover:#dcddde;
39 | --interactive-active:#fff;
40 | --interactive-muted:#838a95;
41 | --background-primary:transparent;
42 | --background-secondary:transparent;
43 | --background-secondary-alt:transparent;
44 | --background-tertiary:transparent;
45 | --modal-footer-background:transparent;
46 | --home-background:transparent;
47 | --bg-overlay-chat:transparent;
48 | --bg-overlay-2:transparent;
49 | --primary-600:transparent;
50 | --background-accent:rgba(79, 84, 92, 0.32);
51 | --background-floating:rgba(24, 25, 28, 0.4);
52 | --background-mobile-primary:#36393f;
53 | --background-mobile-secondary:#2f3136;
54 | --background-modifier-hover:rgba(79, 84, 92, 0.16);
55 | --background-modifier-active:rgba(79, 84, 92, 0.24);
56 | --background-modifier-selected:rgba(79, 84, 92, 0.32);
57 | --background-modifier-accent:hsla(0, 0%, 100%, 0.06);
58 | --background-mentioned:transparent;
59 | --background-mentioned-hover:transparent;
60 | --background-message-hover:transparent;
61 | --background-help-warning:rgba(250, 166, 26, 0.1);
62 | --background-help-info:rgba(0, 176, 244, 0.1);
63 | --scrollbar-thin-thumb:transparent;
64 | --scrollbar-thin-track:transparent;
65 | --scrollbar-auto-thumb:transparent;
66 | --scrollbar-auto-track:transparent;
67 | --scrollbar-auto-scrollbar-color-thumb:transparent;
68 | --scrollbar-auto-scrollbar-color-track:transparent;
69 | --elevation-stroke:0 0 0 1px rgba(4, 4, 5, 0.15);
70 | --elevation-low:transparent;
71 | --elevation-medium:transparent;
72 | --elevation-high:transparent;
73 | --logo-primary:#fff;
74 | --focus-primary:#00b0f4;
75 | --radio-group-dot-foreground:#8ea1e1;
76 | --guild-header-text-shadow:0 1px 1px rgba(0, 0, 0, 0.4);
77 | --channeltextarea-background:transparent;
78 | --activity-card-background:var(--background-attachment);
79 | --textbox-markdown-syntax:#8e9297;
80 | /* Custom Variables */
81 | --background-attachment:rgba(47, 49, 54, 0.2);
82 | --code-block-border:rgba(20, 20, 20, 0.8);
83 | --attachment-border:rgba(20, 20, 20, 0.2);
84 | --background-code-block:rgba(0, 0, 0, 0.3);
85 | --background-code-inline:rgba(0, 0, 0, 0.5);
86 | --background-scrollbar-preview:rgba(255,255,255,0.15);
87 | --background-scrollbar-hover:rgba(255,255,255,0.2);
88 | --background-scrollbar-active:rgba(255,255,255,0.25);
89 | --new-message-color:#f04747;
90 | --background-modifier-hover-brand:rgba(92, 111, 177, 0.16);
91 | --background-modifier-hover-danger:rgba(240, 71, 71, 0.16);
92 | --background-modifier-active-brand:rgba(92, 111, 177, 0.24);
93 | --background-modifier-active-danger:rgba(240, 71, 71, 0.24);
94 | --background-floating-hover:rgba(24, 25, 28, 0.5);
95 | --background-notice:rgba(78, 93, 148, 0.4);
96 | --background-notice-info:rgba(74, 143, 225, 0.4);
97 | --background-notice-warning:rgba(250, 168, 26, 0.4);
98 | --background-notice-error:rgba(237, 66, 69, 0.4);
99 | --background-notice-success:rgba(59, 165, 93, 0.4);
100 | --playback-bar:rgba(0, 0, 0, 0.3);
101 | }
102 |
103 | .theme-light {
104 | --header-primary:#060607;
105 | --header-secondary:#4f5660;
106 | --text-normal:#2e3338;
107 | --text-muted:#747f8d;
108 | --text-link:#0067e0;
109 | --channels-default:#3a3f45;
110 | --interactive-normal:#4f5660;
111 | --interactive-hover:#2e3338;
112 | --interactive-active:#060607;
113 | --interactive-muted:#c7ccd1;
114 | --background-primary:transparent;
115 | --background-secondary:transparent;
116 | --background-secondary-alt:transparent;
117 | --background-tertiary:transparent;
118 | --modal-footer-background:transparent;
119 | --home-background:transparent;
120 | --bg-overlay-chat:transparent;
121 | --bg-overlay-2:transparent;
122 | --primary-600:transparent;
123 | --background-accent:rgba(116, 127, 141, 0.24);
124 | --background-floating:rgba(255, 255, 255, 0.4);
125 | --background-mobile-primary:#f8f9f9;
126 | --background-mobile-secondary:#fff;
127 | --background-modifier-hover:rgba(116, 127, 141, 0.08);
128 | --background-modifier-active:rgba(116, 127, 141, 0.16);
129 | --background-modifier-selected:rgba(116, 127, 141, 0.24);
130 | --background-modifier-accent:rgba(6, 6, 7, 0.08);
131 | --background-mentioned:transparent;
132 | --background-mentioned-hover:transparent;
133 | --background-message-hover:transparent;
134 | --background-help-warning:rgba(250, 166, 26, 0.1);
135 | --background-help-info:rgba(0, 103, 224, 0.1);
136 | --scrollbar-thin-thumb:transparent;
137 | --scrollbar-thin-track:transparent;
138 | --scrollbar-auto-thumb:transparent;
139 | --scrollbar-auto-track:transparent;
140 | --scrollbar-auto-scrollbar-color-thumb:transparent;
141 | --scrollbar-auto-scrollbar-color-track:transparent;
142 | --elevation-stroke:0 0 0 1px rgba(6, 6, 7, 0.08);
143 | --elevation-low:transparent;
144 | --elevation-medium:transparent;
145 | --elevation-high:transparent;
146 | --logo-primary:#7289da;
147 | --focus-primary:#00b0f4;
148 | --radio-group-dot-foreground:#4e5d94;
149 | --guild-header-text-shadow:0 1px 1px hsla(0, 0%, 100%, 0.4);
150 | --channeltextarea-background:transparent;
151 | --activity-card-background:var(--background-attachment);
152 | --textbox-markdown-syntax:#6a7480;
153 | /* Custom Variables */
154 | --background-attachment:rgba(240, 240, 240, 0.2);
155 | --code-block-border:rgba(240, 240, 240, 0.8);
156 | --attachment-border:rgba(240, 240, 240, 0.2);
157 | --background-code-block:rgba(255, 255, 255, 0.3);
158 | --background-code-inline:rgba(255, 255, 255, 0.5);
159 | --background-scrollbar-preview:rgba(0,0,0,0.15);
160 | --background-scrollbar-hover:rgba(0,0,0,0.2);
161 | --background-scrollbar-active:rgba(0,0,0,0.25);
162 | --new-message-color:#f04747;
163 | --background-modifier-hover-brand:rgba(92, 111, 177, 0.16);
164 | --background-modifier-hover-danger:rgba(240, 71, 71, 0.26);
165 | --background-floating-hover:rgba(255, 255, 255, 0.5);
166 | --background-notice:rgba(78, 93, 148, 0.4);
167 | --background-notice-error:rgba(237, 66, 69, 0.4);
168 | --playback-bar:rgba(0, 0, 0, 0.3);
169 | }
170 |
171 | /*
172 | * Discord Styling
173 | */
174 |
175 | /* Chat */
176 |
177 | /* Remove Spacer Between Messages and Text Input */
178 | [class*="messagesWrapper_"] [class*="scrollerSpacer_"] {
179 | height:5px;
180 | }
181 |
182 | [class*="messagesWrapper_"] ~ form [class*="channelTextArea_"] {
183 | margin-bottom:10px;
184 | padding-top:10px;
185 | margin-top:0px;
186 | }
187 |
188 | /* Decrease Height of Special Channel Messages (ex: Announcements) */
189 | [class*="messagesWrapper_"] ~ form [class*="content_"] {
190 | margin-bottom:-45px;
191 | }
192 |
193 | [class*="messagesWrapper_"] ~ form [class*="content_"] ~ [class*="buttonContainer_"] {
194 | margin-bottom:-35px;
195 | }
196 |
197 | /* Resize/Move Message Buttons */
198 | [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] [class*="button_"] {
199 | height:16px;
200 | width:16px;
201 | min-height:16px;
202 | min-width:16px;
203 | color:rgba(185, 187, 190, 0.6);
204 | }
205 |
206 | [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] [class*="button_"]:hover, [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] [class*="button_"]:active, [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] [class*="button_"][class*="selected_"] {
207 | color:rgba(155, 157, 160, 0.6) !important;
208 | background-color:transparent !important;
209 | }
210 |
211 | [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] {
212 | top:-4px;
213 | padding-right:0.875rem;
214 | }
215 |
216 | [class*="messagesWrapper_"] [class*="groupStart_"]:not([class*="compact_"]) [class*="buttonContainer_"] {
217 | top:var(--header-height);
218 | }
219 |
220 | [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] [class*="button_"] {
221 | padding:0px;
222 | }
223 |
224 | [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] div {
225 | box-shadow:none !important;
226 | border:0px !important;
227 | }
228 |
229 | /* Rotate Settings Button */
230 | [class*="messagesWrapper_"] [class*="buttonContainer_"] [class*="buttons_"] [class*="button_"]:last-child:not([class*="dangerous_"]) {
231 | transform:rotate(90deg);
232 | padding-right:0px;
233 | padding-left:0px;
234 | margin-right:0px;
235 | margin-left:0px;
236 | }
237 |
238 | /* Margin on Text to Accomodate Buttons */
239 | [class*="messagesWrapper_"] [class*="message_"] [class*="markup_"]:not([contenteditable="true"])::before {
240 | content:'';
241 | width:6.75rem;
242 | height:16px;
243 | display:inline-flex;
244 | float:right;
245 | }
246 |
247 | /* Make Text Area Wider */
248 | [class*="messagesWrapper_"] [class*="message_"] {
249 | padding-right:0.875rem !important;
250 | }
251 |
252 | /* Spacer Between Message Groups */
253 | [class*="messagesWrapper_"] [class*="groupStart_"]::after {
254 | content:'';
255 | border-top:thin solid var(--background-modifier-accent);
256 | top:calc(0px - var(--message-spacing));
257 | left:1rem;
258 | right:0.875rem;
259 | display:block;
260 | position:absolute;
261 | }
262 |
263 | [class*="messagesWrapper_"] [class*="groupStart_"]:not([class*="compact_"]) {
264 | margin-top:calc(var(--message-spacing) * 2);
265 | }
266 |
267 | [class*="messagesWrapper_"] [class*="divider_"] + [class*="groupStart_"]::after {
268 | display:none;
269 | }
270 |
271 | [class*="messagesWrapper_"] [class*="divider_"] + [class*="groupStart_"]:not([class*="compact_"]) {
272 | margin-top:calc(var(--message-spacing) * 0.5);
273 | }
274 |
275 | [class*="messagesWrapper_"] [class*="divider_"] + [class*="groupStart_"][class*="compact_"] {
276 | margin-top:0;
277 | }
278 |
279 | /* Spacing Between Messages Within a Group */
280 | [class*="messagesWrapper_"] [class*="message_"] {
281 | padding-top:0px;
282 | padding-bottom:0px;
283 | }
284 |
285 | /* Message Divider */
286 | [class*="messagesWrapper_"] [class*="divider_"] {
287 | width:100%;
288 | margin-left:0;
289 | border:none;
290 | height:calc(var(--message-spacing) * 1.5) !important;
291 | }
292 |
293 | [class*="messagesWrapper_"] [class*="divider_"] span {
294 | background-color:transparent;
295 | padding:0;
296 | display:flex;
297 | align-items:center;
298 | text-align:center;
299 | font-size:14px;
300 | line-height:22px;
301 | height:calc(var(--message-spacing) * 2) !important;
302 | left:1rem;
303 | right:0.875rem;
304 | position:absolute;
305 | }
306 |
307 | [class*="messagesWrapper_"] [class*="divider_"] span::before {
308 | margin-right:8px;
309 | }
310 |
311 | [class*="messagesWrapper_"] [class*="divider_"] span::after {
312 | margin-left:8px;
313 | }
314 |
315 | [class*="messagesWrapper_"] [class*="divider_"] span::before, [class*="messagesWrapper_"] [class*="divider_"] span::after {
316 | content:'';
317 | flex:1;
318 | }
319 |
320 | [class*="messagesWrapper_"] [class*="divider_"][class*="isUnread_"] span:nth-child(2) {
321 | display:none;
322 | }
323 |
324 | /* Spacing Correction */
325 | .group-spacing-0 [class*="divider_"][class*="hasContent_"] {
326 | margin-top:4px;
327 | margin-bottom:4px;
328 | }
329 |
330 | .group-spacing-4 [class*="divider_"][class*="hasContent_"] {
331 | margin-top:8px;
332 | margin-bottom:8px;
333 | }
334 |
335 | .group-spacing-8 [class*="divider_"][class*="hasContent_"] {
336 | margin-top:12px;
337 | margin-bottom:12px;
338 | }
339 |
340 | .group-spacing-16 [class*="divider_"][class*="hasContent_"] {
341 | margin-top:20px;
342 | margin-bottom:20px;
343 | }
344 |
345 | .group-spacing-24 [class*="divider_"][class*="hasContent_"] {
346 | margin-top:28px;
347 | margin-bottom:28px;
348 | }
349 |
350 | /* New Message Color */
351 | [class*="messagesWrapper_"] [class*="divider_"][class*="isUnread_"] span {
352 | color:var(--new-message-color);
353 | }
354 |
355 | [class*="messagesWrapper_"] [class*="divider_"][class*="isUnread_"] span::before, [class*="messagesWrapper_"] [class*="divider_"][class*="isUnread_"] span::after {
356 | border-bottom:thin solid var(--new-message-color);
357 | }
358 |
359 | [class*="messagesWrapper_"] [class*="divider_"][class*="isUnread_"] span svg {
360 | display:none;
361 | }
362 |
363 | /* Timestamp Divider Color */
364 | [class*="messagesWrapper_"] [class*="divider_"]:not([class*="isUnread_"]) span::before, [class*="messagesWrapper_"] [class*="divider_"]:not([class*="isUnread_"]) span::after {
365 | border-bottom:thin solid var(--text-muted);
366 | }
367 |
368 | /* Mention Background */
369 | [class*="mention"] {
370 | border-radius:0;
371 | transition:background-color 50ms ease-out,color 50ms ease-out;
372 | -webkit-transition:background-color 50ms ease-out,color 50ms ease-out;
373 | }
374 |
375 | [class*="mention"][class*="interactive"], [class*="mention"][class*="roleMention"] {
376 | background-color:rgba(114,137,218,.1);
377 | color:#7289da;
378 | }
379 |
380 | [class*="mention"][class*="interactive"]:hover, [class*="mention"][class*="roleMention"]:hover {
381 | background-color:rgba(114,137,218,.7);
382 | color:#fff;
383 | }
384 |
385 | [class*="messagesWrapper_"] [class*="message_"][class*="mentioned_"] [class*="mention"][class*="interactive"]:hover {
386 | color:#7289da;
387 | }
388 |
389 | [class*="iconMention_"]:not(#foo) {
390 | padding-left:20px;
391 | }
392 |
393 | /* Reactions */
394 | [class*="reaction_"] {
395 | background:var(--background-modifier-accent);
396 | border-radius:.25rem;
397 | border:none;
398 | margin:0 .125rem .125rem 0;
399 | padding:0;
400 | -webkit-transition:background-color .1s ease;
401 | transition:background-color .1s ease;
402 | }
403 |
404 | [class*="reaction_"]:hover, [class*="reaction_"]:active {
405 | background-color:rgba(252, 245, 255, 0.05);
406 | }
407 |
408 | [class*="reaction_"][class*="reactionMe_"] {
409 | background-color:rgba(114,137,218,.3);
410 | }
411 |
412 | [class*="reaction_"] [class*="reactionInner_"] {
413 | padding:0 .375rem;
414 | }
415 |
416 | [class*="reaction_"] [class*="reactionCount_"]:not(#foo) {
417 | color:var(--text-muted);
418 | }
419 |
420 | [class*="reaction_"]:hover [class*="reactionCount_"]:not(#foo) {
421 | color:var(--interactive-hover);
422 | }
423 |
424 | [class*="reaction_"][class*="reactionMe_"] [class*="reactionCount_"]:not(#foo), [class*="reaction_"][class*="reactionMe_"]:hover [class*="reactionCount_"]:not(#foo) {
425 | color:#7289da;
426 | }
427 |
428 | /* Transparent New Messages / Jump to Bottom */
429 | [class*="messagesWrapper_"] [class*="barBase_"], [class*="sidebar_"] [class*="unreadBar_"]:not([aria-hidden="true"]) {
430 | background-color:var(--background-floating) !important;
431 | left:1rem;
432 | right:1.375rem;
433 | padding-bottom:0px;
434 | backdrop-filter:blur(var(--blur-radius));
435 | border-radius:4px !important;
436 | }
437 |
438 | [class*="messagesWrapper_"] [class*="barBase_"]:hover, [class*="messagesWrapper_"] [class*="barBase_"]:active {
439 | background-color:var(--background-floating-hover) !important;
440 | }
441 |
442 | [data-list-id="guildsnav"] [class*="unreadMentionsBar_"] {
443 | backdrop-filter:blur(var(--blur-radius));
444 | }
445 |
446 | /* Transparent Notice */
447 |
448 | [class*="notice_"]:not([class*="-message"]) {
449 | background-color:var(--background-notice) !important;
450 | backdrop-filter:blur(var(--blur-radius));
451 | }
452 |
453 | [class*="notice-info"]:not([class*="-message"]) {
454 | background-color:var(--background-notice-info) !important;
455 | backdrop-filter:blur(var(--blur-radius));
456 | }
457 |
458 | [class*="notice-warning"]:not([class*="-message"]) {
459 | background-color:var(--background-notice-warning) !important;
460 | backdrop-filter:blur(var(--blur-radius));
461 | }
462 |
463 | [class*="notice-error"]:not([class*="-message"]) {
464 | background-color:var(--background-notice-error) !important;
465 | backdrop-filter:blur(var(--blur-radius));
466 | }
467 |
468 | [class*="notice-success"]:not([class*="-message"]) {
469 | background-color:var(--background-notice-success) !important;
470 | backdrop-filter:blur(var(--blur-radius));
471 | }
472 |
473 | [class*="notice_"] [class*="notice-close"], [class*="notice_"] [class*="notice-content"], [class*="notice_"] [class*="notice-button"] {
474 | background-color:transparent !important;
475 | backdrop-filter:none;
476 | }
477 |
478 | /* Scrollbars */
479 | .scroller-2FKFPG::-webkit-scrollbar, .scrollerBase-289Jih::-webkit-scrollbar, .textContainer-C0szpm::-webkit-scrollbar, .scrollbarGhostHairline-1mSOM1::-webkit-scrollbar {
480 | width:8px;
481 | height:8px;
482 | }
483 |
484 | .scroller-2FKFPG::-webkit-scrollbar-track-piece, .scroller-2FKFPG::-webkit-scrollbar-track, .scrollerBase-289Jih::-webkit-scrollbar-track-piece, .scrollerBase-289Jih::-webkit-scrollbar-track, .textContainer-C0szpm::-webkit-scrollbar-track-piece, .textContainer-C0szpm::-webkit-scrollbar-track, .scrollbarGhostHairline-1mSOM1::-webkit-scrollbar-track {
485 | background:transparent !important;
486 | border:none !important;
487 | }
488 |
489 | .scroller-2FKFPG::-webkit-scrollbar-thumb, .scrollerBase-289Jih::-webkit-scrollbar-thumb, .textContainer-C0szpm::-webkit-scrollbar-thumb, .scrollbarGhostHairline-1mSOM1::-webkit-scrollbar-thumb {
490 | background:var(--scrollbar-auto-thumb)!important;
491 | border:none !important;
492 | border-radius:0!important;
493 | width:4px;
494 | border-left:6px solid var(--lvl3)!important;
495 | }
496 |
497 | .scroller-2FKFPG:hover::-webkit-scrollbar-thumb, .scrollerBase-289Jih:hover::-webkit-scrollbar-thumb, .textContainer-C0szpm:hover::-webkit-scrollbar-thumb, .scrollbarGhostHairline-1mSOM1:hover::-webkit-scrollbar-thumb {
498 | background:var(--background-scrollbar-preview)!important;
499 | border-radius:0!important;
500 | }
501 |
502 | .scroller-2FKFPG::-webkit-scrollbar-thumb:hover, .scrollerBase-289Jih::-webkit-scrollbar-thumb:hover, .textContainer-C0szpm::-webkit-scrollbar-thumb:hover, .scrollbarGhostHairline-1mSOM1::-webkit-scrollbar-thumb:hover {
503 | background:var(--background-scrollbar-hover) !important;
504 | }
505 |
506 | .scroller-2FKFPG::-webkit-scrollbar-thumb:active, .scrollerBase-289Jih::-webkit-scrollbar-thumb:active, .textContainer-C0szpm::-webkit-scrollbar-thumb:active, .scrollbarGhostHairline-1mSOM1::-webkit-scrollbar-thumb:active {
507 | background:var(--background-scrollbar-active) !important;
508 | }
509 |
510 | /* Code Blocks, Embeds, Attachments */
511 | [class*="messagesWrapper_"] [class*="attachment_"]:not([class*="embedFull_"]), [class*="messagesWrapper_"] [class*="wrapper_"][class*="userSelectNone_"], [class*="messagesWrapper_"] [class*="giftCodeContainer_"], [class*="messagesWrapper_"] [class*="wrapperAudio_"], [class*="messagesWrapper_"] [class*="invite_"] {
512 | background:var(--background-attachment) !important;
513 | backdrop-filter:blur(var(--blur-radius));
514 | border:1px solid var(--attachment-border) !important;
515 | }
516 |
517 | [class*="messagesWrapper_"] [class*="giftCodeContainer_"] [class*="tile_"], [class*="messagesWrapper_"] [class*="giftCodeContainer_"] [class*="tile_"] [class*="invalidPoop_"] {
518 | background-color:transparent !important;
519 | }
520 |
521 | [class*="messagesWrapper_"] [class*="giftCodeContainer_"] {
522 | width:auto;
523 | }
524 |
525 | [class*="embedFull_"] {
526 | background:var(--background-attachment) !important;
527 | backdrop-filter:blur(var(--blur-radius));
528 | border-left:4px solid rgb(32, 34, 37);
529 | border-right:1px solid var(--attachment-border) !important;
530 | border-top:1px solid var(--attachment-border) !important;
531 | border-bottom:1px solid var(--attachment-border) !important;
532 | }
533 |
534 | [class*="chatContainer_"] code {
535 | border:1px solid var(--code-block-border) !important;
536 | background:var(--background-code-block) !important;
537 | backdrop-filter:blur(var(--blur-radius));
538 | border-radius:0 !important;
539 | }
540 |
541 | code.inline {
542 | background:var(--background-code-inline) !important;
543 | border-radius:0 !important;
544 | }
545 |
546 | /* TODO Bugfix for Misaligned Blur in Pinned Messages */
547 | .layerContainer-2v_Sit [class*="messagesPopoutWrap_"] code.inline{
548 | backdrop-filter:none !important;
549 | }
550 |
551 | [class*="textContainer_"] > pre > code, [class*="modalTextContainer_"] > pre > code {
552 | overflow:visible !important;
553 | }
554 |
555 | [class*="modalTextContainer_"] {
556 | overflow:scroll visible !important;
557 | }
558 |
559 | [class*="messagesWrapper_"] [class*="wrapperAudio_"] [class*="audioControls_"], [class*="messagesWrapper_"] [class*="volumeButtonSlider_"] [class*="mediaBarInteraction_"], [class*="messagesWrapper_"] [class*="volumeButtonSlider_"] [class*="mediaBarInteractionDragging_"], [class*="messagesWrapper_"] [class*="videoControls_"] {
560 | background-color:var(--playback-bar);
561 | }
562 |
563 | /* Tooltips */
564 | [class*="tooltipRight_"] {
565 | backdrop-filter:blur(var(--blur-radius));
566 | background-color:var(--background-floating);
567 | border-radius:0;
568 | padding:0px 0px 0px 8px;
569 | margin-left:-8px;
570 | -webkit-clip-path:polygon(8px 4px, 9px 2px, 10px 1px, 12px 0px, calc(100% - 4px) 0%, calc(100% - 2px) 1px, calc(100% - 1px) 2px, 100% 4px, 100% calc(100% - 4px), calc(100% - 1px) calc(100% - 2px), calc(100% - 2px) calc(100% - 1px), calc(100% - 4px) 100%, 12px 100%, 10px calc(100% - 1px), 9px calc(100% - 2px), 8px calc(100% - 4px), 8px calc(50% + 5px), 3px 50%, 8px calc(50% - 5px)) !important;
571 | }
572 |
573 | [class*="tooltipBottom_"] {
574 | backdrop-filter:blur(var(--blur-radius));
575 | background-color:var(--background-floating);
576 | border-radius:0;
577 | padding:8px 0px 0px 0px;
578 | margin-top:-8px;
579 | -webkit-clip-path:polygon(0 12px, 1px 10px, 2px 9px, 4px 8px, calc(50% - 5px) 8px, 50% 3px, calc(50% + 5px) 8px, calc(100% - 4px) 8px, calc(100% - 2px) 9px, calc(100% - 1px) 10px, 100% 12px, 100% calc(100% - 4px), calc(100% - 1px) calc(100% - 2px), calc(100% - 2px) calc(100% - 1px), calc(100% - 4px) 100%, 4px 100%, 2px calc(100% - 1px), 1px calc(100% - 2px), 0 calc(100% - 4px)) !important;
580 | }
581 |
582 | [class*="tooltipLeft_"] {
583 | backdrop-filter:blur(var(--blur-radius));
584 | background-color:var(--background-floating);
585 | border-radius:0;
586 | padding:0px 0px 0px 8px;
587 | margin-left:-8px;
588 | -webkit-clip-path:polygon(0px 4px, 1px 2px, 2px 1px, 4px 0px, calc(100% - 12px) 0%, calc(100% - 10px) 1px, calc(100% - 9px) 2px, calc(100% - 8px) 4px, calc(100% - 8px) calc(50% - 5px), 100% 50%, calc(100% - 8px) calc(50% + 5px), calc(100% - 8px) calc(100% - 4px), calc(100% - 9px) calc(100% - 2px), calc(100% - 10px) calc(100% - 1px), calc(100% - 12px) 100%, 4px 100%, 2px calc(100% - 1px), 1px calc(100% - 2px), 0px calc(100% - 4px)) !important;
589 | }
590 |
591 | [class*="tooltipTop_"], .layerContainer-2v_Sit .toolbar-2bjZV7, #EditUploadsToolbar .toolIconWrapper:not(#foo):hover::after /* EditUploads Plugin Tooltips */ {
592 | backdrop-filter:blur(var(--blur-radius));
593 | background-color:var(--background-floating);
594 | border-radius:0;
595 | padding:0px 0px 8px 0px;
596 | margin-bottom:-8px;
597 | -webkit-clip-path:polygon(0 4px, 1px 2px, 2px 1px, 4px 0px, calc(100% - 4px) 0px, calc(100% - 2px) 1px, calc(100% - 1px) 2px, 100% 4px, 100% calc(100% - 12px), calc(100% - 1px) calc(100% - 10px), calc(100% - 2px) calc(100% - 9px), calc(100% - 4px) calc(100% - 8px), calc(50% + 5px) calc(100% - 8px), 50% calc(100% - 3px), calc(50% - 5px) calc(100% - 8px), 4px calc(100% - 8px), 2px calc(100% - 9px), 1px calc(100% - 10px), 0 calc(100% - 12px)) !important;
598 | }
599 |
600 | [class*="tooltipPointer_"], .layerContainer-2v_Sit .toolbar-2bjZV7::before, [class*="mediaBarWrapper_"] [class*="bubble_"]::before {
601 | display:none !important;
602 | }
603 |
604 | /* Small Media Player Time Tooltip */
605 | [class*="mediaBarWrapper_"] [class*="bubble_"] {
606 | line-height:12px;
607 | background-color:var(--background-floating);
608 | border-radius:0;
609 | padding:8px 12px;
610 | margin-bottom:-8px;
611 | -webkit-clip-path:polygon(0 4px, 1px 2px, 2px 1px, 4px 0px, calc(100% - 4px) 0px, calc(100% - 2px) 1px, calc(100% - 1px) 2px, 100% 4px, 100% calc(100% - 12px), calc(100% - 1px) calc(100% - 10px), calc(100% - 2px) calc(100% - 9px), calc(100% - 4px) calc(100% - 8px), calc(50% + 5px) calc(100% - 8px), 50% calc(100% - 3px), calc(50% - 5px) calc(100% - 8px), 4px calc(100% - 8px), 2px calc(100% - 9px), 1px calc(100% - 10px), 0 calc(100% - 12px)) !important;
612 |
613 | }
614 |
615 | /* Popouts */
616 |
617 | [id*="popout_"]::before, [id*="popout_"] [class*="submenu_"]::before, .theme-dark:not([id*="popout_"]) > [role="menu"]::before, .theme-dark:not([id*="popout_"]) > [role="menu"] [class*="submenu_"]::before, [class*="profileCustomizationPreview_"]::before, [role*="dialog"] > div::before {
618 | content:'';
619 | position:absolute;
620 | top:0;
621 | bottom:0;
622 | left:0;
623 | right:0;
624 | z-index:-1;
625 | backdrop-filter:blur(var(--blur-radius));
626 | }
627 |
628 | [id*="popout_"], [id*="popout_"] [class*="submenu_"], .theme-dark:not([id*="popout_"]) > [role="menu"], .theme-dark:not([id*="popout_"]) > [role="menu"] [class*="submenu_"], [class*="profileCustomizationPreview_"], [role*="dialog"] > div {
629 | box-shadow:none !important;
630 | background-color:var(--background-floating) !important;
631 | }
632 |
633 | [role="menu"] {
634 | box-shadow:none !important;
635 | background:transparent;
636 | }
637 |
638 | /* Transparent Message Preview */
639 | .layerContainer-2lfOPe [class*="content_"] .message-2qRu38 {
640 | background-color:var(--background-primary) !important;
641 | border:none !important;
642 | box-shadow:none !important;
643 | }
644 |
645 | /* Fix Misaligned Blur on Livestream Watchers Popout */
646 | .layerContainer-2lfOPe [class*="translate_"] > .popoutWrapper-3TSaaB {
647 | left:0;
648 | }
649 |
650 | .layerContainer-2lfOPe [class*="translate_"] > .container-2dqNWc {
651 | margin-left:0;
652 | }
653 |
654 | .layerContainer-2lfOPe [class*="translate_"] > [class*="streamPreviewWrapper_"] {
655 | padding-left:0;
656 | }
657 |
658 | /* Autocomplete */
659 | [class*="autocomplete_"] {
660 | box-shadow:none !important;
661 | background-color:var(--background-floating) !important;
662 | backdrop-filter:blur(var(--blur-radius));
663 | }
664 |
665 | [class*="autocomplete_"] [class*="categoryHeader_"] {
666 | background-color:transparent !important;
667 | }
668 |
669 | [class*="autocomplete_"] [class*="autocompleteRow_"] [class*="selected_"] {
670 | background-color:var(--background-modifier-hover) !important;
671 | }
672 |
673 | /* Emoji Information */
674 | .popoutContainer-1MXdqN {
675 | box-shadow:none !important;
676 | background-color:var(--background-floating) !important;
677 | }
678 |
679 | .layerContainer-2lfOPe [role="menu"]::before {
680 | content:'';
681 | position:absolute;
682 | top:0;
683 | bottom:0;
684 | left:0;
685 | right:0;
686 | backdrop-filter:blur(var(--blur-radius));
687 | box-shadow:none !important;
688 | }
689 |
690 | /* User Popouts */
691 | [class*="userPopoutOverlayBackground_"], [class*="userProfileModalOverlayBackground_"], [class*="userPopoutInner_"] [class*="bodyInnerWrapper_"], [class*="tabBarContainer_"] {
692 | background:transparent !important;
693 | box-shadow:none !important;
694 | }
695 |
696 | [class*="userPopoutInner_"] [class*="headerPlaying_"]:not(#foo):not(#bar), [class*="userProfileModalOverlayBackground_"] [class*="topSectionPlaying_"] {
697 | background-color:rgba(114, 137, 218, 0.35) !important;
698 | }
699 |
700 | [class*="userPopoutInner_"] [class*="headerSpotify_"]:not(#foo):not(#bar), [class*="userProfileModalOverlayBackground_"] [class*="topSectionSpotify_"] {
701 | background-color:rgba(29, 185, 84, 0.4) !important;
702 | }
703 |
704 | [class*="userPopoutInner_"] [class*="headerXbox_"]:not(#foo):not(#bar), [class*="userProfileModalOverlayBackground_"] [class*="topSectionXbox_"] {
705 | background-color:rgba(16, 124, 16, 0.4) !important;
706 | }
707 |
708 | [class*="userPopoutInner_"] [class*="headerStreaming_"]:not(#foo):not(#bar), [class*="userProfileModalOverlayBackground_"] [class*="topSectionStreaming_"] {
709 | background-color:rgba(89, 54, 149, 0.45) !important;
710 | }
711 |
712 | [class*="userPopoutInner_"] [class*="aboutMeSection_"] {
713 | background:transparent !important;
714 | }
715 |
716 | .activityUserPopout-2yItg2, .headerFill-adLl4x {
717 | background-color:transparent !important;
718 | }
719 |
720 | [class*="accountProfilePopoutWrapper_"] {
721 | left:0 !important;
722 | }
723 |
724 | /* Profile Banner */
725 | [class*="userPopoutInner_"] [class*="popoutBanner_"]:not(#foo):not(#bar), [role="dialog"] [class*="profileBanner_"] {
726 | opacity:0.7 !important;
727 | }
728 |
729 | /* Mask Out Profile Picture from Banner for Transparent Profile Pictures */
730 | [class*="userPopoutInner_"] [class*="popoutBanner_"]:not(#foo):not(#bar) {
731 | mask:radial-gradient(circle 46px at 62px 56px,transparent 97%,#fff 100%);
732 | -webkit-mask:radial-gradient(circle 46px at 62px 56px,transparent 97%,#fff 100%);
733 | }
734 |
735 | [role="dialog"] [class*="profileBanner_"] {
736 | mask:radial-gradient(circle 68px at 82px 101px,transparent 97%,#fff 100%);
737 | -webkit-mask:radial-gradient(circle 68px at 82px 101px,transparent 97%,#fff 100%);
738 | }
739 |
740 | /* Premium Profile Banners */
741 | [class*="userPopoutInner_"] [class*="popoutBannerPremium_"]:not(#foo):not(#bar), [class*="profileBannerPreview_"] [class*="banner_"] {
742 | mask:radial-gradient(circle 46px at 62px 116px,transparent 97%,#fff 100%);
743 | -webkit-mask:radial-gradient(circle 46px at 62px 116px,transparent 97%,#fff 100%);
744 | }
745 |
746 | [role="dialog"] [class*="profileBannerPremium_"] {
747 | mask:radial-gradient(circle 68px at 82px 207px,transparent 97%,#fff 100%);
748 | -webkit-mask:radial-gradient(circle 68px at 82px 207px,transparent 97%,#fff 100%);
749 | }
750 |
751 | /* Image Preview */
752 | .layerContainer-2lfOPe [role="dialog"] [class*="downloadLink_"], .layerContainer-2lfOPe [role="dialog"] [class*="operations_"] {
753 | position:relative;
754 | top:0;
755 | }
756 |
757 | .layerContainer-2lfOPe [role="dialog"] [class*="imageWrapper_"] {
758 | margin-left:auto;
759 | margin-right:auto;
760 | min-width:0;
761 | }
762 |
763 | .layerContainer-2lfOPe [role="dialog"] [class*="gallery_"] {
764 | pointer-events:none;
765 | }
766 |
767 | .layerContainer-2lfOPe [role="dialog"] [class*="imageWrapper_"] ~ *, .layerContainer-2lfOPe [role="dialog"] [class*="imageWrapper_"] {
768 | pointer-events:auto;
769 | }
770 |
771 | .layerContainer-2lfOPe [role="dialog"]:not(#emoji-picker-tab-panel) [class*="gallery_"]:not(#foo) {
772 | background-color:transparent !important;
773 | }
774 |
775 | /* Search */
776 | [role="listbox"] [class*="resultsGroup_"] [class*="option"], [role="listbox"] [class*="queryContainer_"] {
777 | background:transparent;
778 | }
779 |
780 | [role="listbox"] [class*="resultsGroup_"] [class*="option"]:hover {
781 | background:var(--background-modifier-hover);
782 | }
783 |
784 | [role="listbox"] [class*="resultsGroup_"] [class*="option"]:active {
785 | background:var(--background-modifier-active);
786 | }
787 |
788 | [role="listbox"] [class*="resultsGroup_"] [class*="option"]::before, [role="listbox"] [class*="resultsGroup_"] [role="option"]::after {
789 | display:none !important;
790 | }
791 |
792 | [id="search-results"] [class*="searchResultGroup_"] [class*="buttonsContainer_"] [class*="button_"], .card-3x20HF {
793 | background-color:var(--background-floating) !important;
794 | backdrop-filter:blur(var(--blur-radius));
795 | }
796 |
797 | [class*="calendarPicker_"], .react-datepicker, .react-datepicker__header {
798 | background:transparent !important;
799 | }
800 |
801 | .react-datepicker__day--outside-month:not(#foo) {
802 | background:var(--background-accent) !important;
803 | }
804 |
805 | .react-datepicker__day:hover {
806 | background:var(--background-modifier-hover) !important;
807 | }
808 |
809 | .react-datepicker__day--outside-month:hover {
810 | background:var(--background-floating) !important;
811 | }
812 |
813 | .react-datepicker__day--disabled, .react-datepicker__day--disabled:hover {
814 | background:var(--background-floating-hover) !important;
815 | }
816 |
817 | .react-datepicker__navigation {
818 | border-color:transparent !important;
819 | }
820 |
821 | /* Settings/Inputs */
822 | *:not([class*="copyInput_"]) > * > [class*="inputWrapper_"]:not([class*="copyInput_"]) *, [class*="copyInput_"], [class*="emojiInput_"], [class*="emojiRow_"]:hover [class*="emojiInput_"], [class*="emojiRow_"]:focus-within [class*="emojiInput_"], .container-CpszHS, [class*="peopleColumn_"] [class*="addInputWrapper_"] form, [class*="quickswitcher_"] input {
823 | backdrop-filter:blur(var(--blur-radius));
824 | background-color:var(--background-attachment) !important;
825 | }
826 |
827 | *:not([class*="copyInput_"]) > * > [class*="inputWrapper_"]:not([class*="copyInput_"]) *:hover, [class*="copyInput_"]:hover, .container-CpszHS:hover, [class*="peopleColumn_"] [class*="addInputWrapper_"] form:hover, [class*="quickswitcher_"] input:hover {
828 | background-color:var(--background-modifier-hover) !important;
829 | }
830 |
831 | *:not([class*="copyInput_"]) > * > [class*="inputWrapper_"]:not([class*="copyInput_"]) *:active, [class*="copyInput_"]:active, .container-CpszHS:active, [class*="peopleColumn_"] [class*="addInputWrapper_"] form:active, [class*="quickswitcher_"] input:active {
832 | background-color:var(--background-modifier-active) !important;
833 | }
834 |
835 | [class*="select_"] > div > [class*="-control"], .bd-select {
836 | backdrop-filter:blur(var(--blur-radius));
837 | background-color:var(--background-attachment);
838 | border-color:rgba(0, 0, 0, 0.6);
839 | }
840 |
841 | [class*="select_"] > div > [class*="-control"]:hover, .bd-select:hover {
842 | background-color:var(--background-modifier-hover);
843 | border-color:rgba(0, 0, 0, 0.6);
844 | }
845 |
846 | [class*="select_"] > div > [class*="-control"]:active, .bd-select:active, .bd-select.menu-open {
847 | background-color:var(--background-modifier-active);
848 | border-color:rgba(0, 0, 0, 0.6);
849 | }
850 |
851 | [class*="select_"] > div > [class*="-menu"], .bd-select .bd-select-options {
852 | border-top:none;
853 | border-color:rgba(0, 0, 0, 0.6);
854 | backdrop-filter:blur(var(--blur-radius));
855 | box-shadow:none !important;
856 | background-color:var(--background-floating) !important;
857 | }
858 |
859 | [class*="select_"] > div:hover {
860 | background-color:var(--background-modifier-hover);
861 | }
862 |
863 | [class*="select_"] > div:active {
864 | background-color:var(--background-modifier-active);
865 | }
866 |
867 | [class*="radioBar_"] {
868 | backdrop-filter:blur(var(--blur-radius));
869 | background-color:var(--background-attachment);
870 | }
871 |
872 | [tabindex="0"] [class*="radioBar_"] {
873 | background-color:var(--background-modifier-selected);
874 | }
875 |
876 | [class*="auditLog_"], .card-3Qj_Yx {
877 | backdrop-filter:blur(var(--blur-radius));
878 | background-color:var(--background-attachment);
879 | }
880 |
881 | .card-FDVird::before {
882 | background-color:var(--background-modifier-hover) !important;
883 | border:none !important;
884 | }
885 |
886 | [class*="tiers_"] [class*="tier_"], [class*="connectionList_"] [class*="connection_"], [class*="guildSubscriptionSlots_"], [class*="codeRedemptionRedirect_"]:not(#foo) {
887 | backdrop-filter:blur(var(--blur-radius));
888 | background-color:var(--background-attachment);
889 | }
890 |
891 | .layerContainer-2lfOPe [class*="selectGuild_"]:hover {
892 | background-color:var(--background-modifier-hover);
893 | }
894 |
895 | [class*="tiers_"] [class*="tierBody_"] {
896 | background-color:transparent;
897 | }
898 |
899 | [class*="tiers_"] [class*="tierHeader_"]:not([class*="tierHeaderUnlocked_"]) {
900 | background-color:var(--background-accent);
901 | }
902 |
903 | /* Other Pages */
904 | [class*="applicationStore_"], [class*="guilds_"] ~ * [class*="sidebar_"] ~ [class*="container_"], [class*="guilds_"] ~ * [class*="sidebar_"] ~ [class*="pageWrapper_"], .container-3wLKDe, .themed-Hp1KC_, .container-2IKOsH, .container-1um7CU {
905 | background-color:transparent !important;
906 | }
907 |
908 | [class*="nowPlayingColumn_"] [class*="itemCard_"], [class*="peopleListItem_"] [class*="actions_"] [class*="actionButton_"], [class*="pageWrapper_"] [class*="guildListSection_"] [class*="guildList_"] [class*="loaded_"], [class*="pageWrapper_"] [class*="guildList_"] [class*="card_"], [class*="nowPlayingColumn_"] [class*="card_"] {
909 | backdrop-filter:blur(var(--blur-radius));
910 | background-color:var(--activity-card-background);
911 | }
912 |
913 | [class*="nowPlayingColumn_"] [class*="itemCard_"] [class*="inset_"] {
914 | background-color:var(--background-attachment);
915 | }
916 |
917 | [class*="nowPlayingColumn_"] [class*="itemCard_"]:not(#foo):hover, [class*="peopleListItem_"] [class*="actions_"] [class*="actionButton_"]:hover, [class*="pageWrapper_"] [class*="guildListSection_"] [class*="guildList_"] [class*="loaded_"]:hover, [class*="pageWrapper_"] [class*="guildList_"] [class*="card_"]:hover {
918 | background-color:var(--background-modifier-hover);
919 | }
920 |
921 | [class*="nowPlayingColumn_"] [class*="itemCard_"]:not(#foo):active, [class*="nowPlayingColumn_"] [class*="itemCard_"][class*=" active_"], [class*="peopleListItem_"] [class*="actions_"] [class*="actionButton_"]:active, [class*="pageWrapper_"] [class*="guildListSection_"] [class*="guildList_"] [class*="loaded_"]:active, [class*="pageWrapper_"] [class*="guildList_"] [class*="card_"]:active {
922 | background-color:var(--background-modifier-active);
923 | }
924 |
925 | .layerContainer-2lfOPe [class*="perksModal_"] {
926 | background-color:transparent;
927 | backdrop-filter:blur(var(--blur-radius));
928 | }
929 |
930 | .layerContainer-2lfOPe [class*="perksModal_"] [class*="perks_"] [class*="perk_"], .layerContainer-2lfOPe [class*="perksModal_"] [class*="tier_"] {
931 | backdrop-filter:blur(var(--blur-radius));
932 | background-color:var(--background-attachment);
933 | }
934 |
935 | .layerContainer-2lfOPe [class*="perksModal_"] [class*="tier_"] [class*="tierBody_"] {
936 | background-color:transparent;
937 | }
938 |
939 | .layerContainer-2lfOPe [class*="perksModal_"] [class*="tier_"] [class*="tierHeader_"] {
940 | background-color:var(--background-accent);
941 | }
942 |
943 | /* Preset Colors */
944 | .theme-dark [class*="lookFilled_"][class*="colorGrey_"] {
945 | background-color:rgba(79, 84, 95, 0.6);
946 | }
947 |
948 | .theme-light [class*="lookFilled_"][class*="colorGrey_"] {
949 | background-color:rgba(116, 127, 141, 0.6);
950 | }
951 |
952 | [class*="colorDefault_"][class*="focused_"] {
953 | background-color:var(--background-modifier-hover);
954 | }
955 |
956 | [class*="colorDefault_"][class*="colorBrand_"][class*="focused_"] {
957 | background-color:var(--background-modifier-hover-brand);
958 | }
959 |
960 | [class*="colorDefault_"][class*="colorDanger_"][class*="focused_"] {
961 | background-color:var(--background-modifier-hover-danger);
962 | }
963 |
964 | [class*="colorDefault_"][class*="focused_"]:active:not(#foo), [class*="colorDefault_"]:active:not(#foo) {
965 | background-color:var(--background-modifier-active);
966 | }
967 |
968 | [class*="colorDefault_"][class*="colorBrand_"][class*="focused_"]:active:not(#foo), [class*="colorDefault_"][class*="colorBrand_"]:active:not(#foo) {
969 | background-color:var(--background-modifier-active-brand);
970 | }
971 |
972 | [class*="colorDefault_"][class*="colorDanger_"][class*="focused_"]:active:not(#foo), [class*="colorDefault_"][class*="colorDanger_"]:active:not(#foo) {
973 | background-color:var(--background-modifier-active-danger);
974 | }
975 |
976 | /* Emoji Picker */
977 | #emoji-picker-tab-panel [class*="categoryItemDefaultCategorySelected_"], [class*="categoryItemDefaultCategorySelected_"]:hover {
978 | background-color:var(--background-modifier-selected);
979 | }
980 |
981 | #emoji-picker-tab-panel [class*="diversitySelector_"] [class*="diversitySelectorOptions_"] {
982 | z-index:10;
983 | background-color:var(--background-floating);
984 | backdrop-filter:blur(var(--blur-radius));
985 | }
986 |
987 | /* Emoji Picker Unicode Shortcut */
988 | .layerContainer-2lfOPe [class*="drawerSizingWrapper_"] > [class*="contentWrapper_"] [class*="categoryList_"] {
989 | display:flex;
990 | flex-direction:column;
991 | flex-wrap:nowrap;
992 | align-items:stretch;
993 | }
994 |
995 | .layerContainer-2lfOPe [class*="drawerSizingWrapper_"] > [class*="contentWrapper_"] [class*="categoryList_"] [class*="scroller_"] {
996 | height:auto !important;
997 | flex-grow:1;
998 | align-self:flex-start;
999 | position:relative !important;
1000 | width:100%;
1001 | }
1002 |
1003 | #emoji-picker-tab-panel [class*="unicodeShortcut_"], .layerContainer-2lfOPe [class*="drawerSizingWrapper_"] > [class*="contentWrapper_"] [class*="standardStickerShortcut_"] {
1004 | flex-shrink:0;
1005 | flex-grow:0;
1006 | align-self:flex-end;
1007 | position:relative !important;
1008 | width:100%;
1009 | }
1010 |
1011 | #emoji-picker-tab-panel [class*="unicodeShortcut_"][class*="unicodeShortcutInvisible_"], .layerContainer-2lfOPe [class*="drawerSizingWrapper_"] > [class*="contentWrapper_"] [class*="standardStickerShortcut_"][class*="invisibleShortcut_"] {
1012 | display:none;
1013 | }
1014 |
1015 | #gif-picker-tab-panel [class*="header_"] {
1016 | box-shadow:none !important;
1017 | }
1018 |
1019 | /* Status Emoji Picker */
1020 | .layerContainer-2lfOPe [class*="emojiButtonContainer_"] {
1021 | z-index:1000000 !important;
1022 | }
1023 |
1024 |
1025 | /* Upload Modal */
1026 | [class*="uploadModal_"] {
1027 | position:fixed !important;
1028 | top:0px !important;
1029 | left:0px !important;
1030 | background:transparent !important;
1031 | box-shadow:none !important;
1032 | }
1033 |
1034 | [class*="uploadModal_"] [class*="file_"] [class*="icon_"] {
1035 | border:0 !important;
1036 | box-shadow:none !important;
1037 | margin:0 !important;
1038 | background-position:50% !important;
1039 | position:fixed !important;
1040 | width:80vw !important;
1041 | left:10vw !important;
1042 | top:70px !important;
1043 | background-size:contain !important;
1044 | background-color:transparent !important;
1045 | background-repeat:no-repeat !important;
1046 | background-image:none;
1047 | }
1048 |
1049 | [class*="uploadModal_"] [class*="file_"] [class*="description_"] {
1050 | display:none;
1051 | }
1052 |
1053 | [class*="uploadModal_"] [class*="comment_"]{
1054 | position:fixed !important;
1055 | width:80vw !important;
1056 | left:10vw !important;
1057 | height:calc(100vh - 50px) !important;
1058 | top:0vh !important;
1059 | margin:0 !important;
1060 | padding:0 !important;
1061 | }
1062 |
1063 | [class*="uploadModal_"] [class*="comment_"] [class*="label_"] {
1064 | display:none;
1065 | }
1066 |
1067 | [class*="uploadModal_"] [class*="comment_"] [class*="inputWrapper_"] {
1068 | margin:0;
1069 | padding:0;
1070 | }
1071 |
1072 | [class*="uploadModal_"] [class*="comment_"] [class*="inputWrapper_"] input {
1073 | backdrop-filter:none !important;
1074 | background-color:transparent !important;
1075 | border:none !important;
1076 | position:fixed !important;
1077 | left:0 !important;
1078 | width:100vw !important;
1079 | height:20px !important;
1080 | padding:0 !important;
1081 | text-align:center !important;
1082 | color:var(--header-primary) !important;
1083 | font-size:18px !important;
1084 | font-weight:700 !important;
1085 | font-style:normal !important;
1086 | line-height:20px !important;
1087 | vertical-align:baseline !important;
1088 | }
1089 |
1090 | [class*="uploadModal_"] [class*="comment_"] [class*="inputWrapper_"]:nth-of-type(2) input {
1091 | top:16px !important;
1092 | }
1093 |
1094 | [class*="uploadModal_"] [class*="comment_"] [class*="inputWrapper_"]:nth-of-type(4) input {
1095 | top:44px !important;
1096 | }
1097 |
1098 | [class*="uploadModal_"] [class*="comment_"] [class*="inputWrapper_"] input:hover, [class*="uploadModal_"] [class*="comment_"] [class*="inputWrapper_"] input:active {
1099 | background-color:transparent !important;
1100 | }
1101 |
1102 | [class*="uploadModal_"] [class*="footer_"] {
1103 | position:fixed !important;
1104 | background:transparent !important;
1105 | width:80vw !important;
1106 | left:10vw !important;
1107 | height:calc(100vh - 10px) !important;
1108 | top:0vh !important;
1109 | margin:0 !important;
1110 | padding:0 !important;
1111 | margin-bottom:5px !important;
1112 | align-items:flex-end !important;
1113 | z-index:-1;
1114 | box-shadow:none !important;
1115 | }
1116 |
1117 | [class*="uploadModal_"] [class*="comment_"] [class*="checkboxWrapper_"] {
1118 | position:fixed !important;
1119 | background:transparent !important;
1120 | width:100vw !important;
1121 | left:10vw !important;
1122 | height:24px !important;
1123 | top:calc(100vh - 36px) !important;
1124 | margin:0 !important;
1125 | padding:0 !important;
1126 | margin-bottom:5px !important;
1127 | align-items:flex-end !important;
1128 | z-index:-2;
1129 | }
1130 |
1131 | [class*="uploadModal_"] [class*="comment_"] [class*="checkboxWrapper_"] [class*="label_"] {
1132 | display:block !important;
1133 | }
1134 |
1135 | /* Upload Modal Sizing */
1136 | @media only screen and (max-height: 300px) {
1137 | [class*="uploadModal_"] [class*="file_"] [class*="icon_"] {
1138 | height:50vh !important;
1139 | }
1140 |
1141 | [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"], [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"] [class*="scrollableContainer_"] {
1142 | max-height:calc(100vh - (50vh + 22px + 20px));
1143 | }
1144 | }
1145 |
1146 | @media only screen and (min-height: 301px) {
1147 | [class*="uploadModal_"] [class*="file_"] [class*="icon_"] {
1148 | height:50vh !important;
1149 | }
1150 |
1151 | [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"], [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"] [class*="scrollableContainer_"] {
1152 | max-height:calc(100vh - (50vh + 22px + 105px));
1153 | }
1154 | }
1155 |
1156 | @media only screen and (min-height: 500px) {
1157 | [class*="uploadModal_"] [class*="file_"] [class*="icon_"] {
1158 | height:65vh !important;
1159 | }
1160 |
1161 | [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"], [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"] [class*="scrollableContainer_"] {
1162 | max-height:calc(100vh - (65vh + 22px + 105px));
1163 | }
1164 | }
1165 |
1166 | @media only screen and (min-height: 700px) {
1167 | [class*="uploadModal_"] [class*="file_"] [class*="icon_"] {
1168 | height:75vh !important;
1169 | }
1170 |
1171 | [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"], [class*="uploadModal_"] [class*="comment_"] [class*="channelTextArea_"] [class*="scrollableContainer_"] {
1172 | max-height:calc(100vh - (75vh + 22px + 105px));
1173 | }
1174 | }
1175 |
1176 | /* Shrink Guilds Inside Expanded Folder */
1177 | [data-list-id="guildsnav"] [role="group"][id|="folder-items"] [class*="blobContainer_"] > [class*="wrapper_"], [data-list-id="guildsnav"] [role="group"][id|="folder-items"] [class*="blobContainer_"] > [class*="wrapper_"] > svg, [data-list-id="guildsnav"] [role="group"][id|="folder-items"] [class*="pill_"] {
1178 | width:38px;
1179 | height:38px;
1180 | }
1181 |
1182 | [data-list-id="guildsnav"] [role="group"][id|="folder-items"] {
1183 | height:auto !important;
1184 | }
1185 |
1186 | [data-list-id="guildsnav"] [class|="expandedFolderBackground"] {
1187 | bottom:auto;
1188 | top:auto;
1189 | height:calc(100% + 5px);
1190 | clip-path:inset(0px 0px 0px 0px);
1191 | transition:clip-path 250ms ease, background-color 250ms ease;
1192 | }
1193 |
1194 | [data-list-id="guildsnav"] [class|="expandedFolderBackground"][class*="collapsed_"] {
1195 | background-color:transparent !important;
1196 | clip-path:inset(0px 0px 100% 0px);
1197 | }
1198 |
1199 | @keyframes guildFadeIn {
1200 | 0% {
1201 | opacity:0;
1202 | clip-path:inset(0px 0px 100% 0px);
1203 | }
1204 | 100% {
1205 | opacity:1;
1206 | clip-path:inset(0px 0px 0px 0px);
1207 | }
1208 | }
1209 |
1210 | [data-list-id="guildsnav"] [id|="folder-items"] {
1211 | opacity:1;
1212 | clip-path:inset(0px 0px 0px 0px);
1213 | transition:clip-path 250ms ease, opacity 250ms ease;
1214 | animation:guildFadeIn 250ms ease;
1215 | }
1216 |
1217 | [data-list-id="guildsnav"] [class|="expandedFolderBackground"][class*="collapsed_"] ~ [id|="folder-items"] {
1218 | opacity:0;
1219 | clip-path:inset(0px 0px 100% 0px);
1220 | }
1221 |
1222 | /* Transparent Guilds with No Image */
1223 | [data-list-id="guildsnav"] [class*="listItem_"] [class*="childWrapper_"], [data-list-id="guildsnav"] [class*="listItem_"] [class*="circleIconButton_"] {
1224 | background-color:rgba(54,57,63,0.85);
1225 | }
1226 |
1227 | [data-list-id="guildsnav"] [class*="listItem_"] [class*="circleIconButton_"][class*="selected_"] {
1228 | background-color:rgba(67,181,129,0.85);
1229 | }
1230 |
1231 | [data-list-id="guildsnav"] [class*="listItem_"] [data-list-item-id*="_home"][class*="selected_"] > div {
1232 | background-color:rgba(114,137,218,0.85);
1233 | }
1234 |
1235 | [class|="expandedFolderBackground"][class*="collapsed_"] ~ [class|="listItem"] .wrapper-28eC3z::before {
1236 | content:'';
1237 | position:absolute;
1238 | top:0;
1239 | bottom:0;
1240 | left:0;
1241 | right:0;
1242 | backdrop-filter:blur(var(--blur-radius));
1243 | border-radius:16px;
1244 | }
1245 |
1246 | [class|="expandedFolderBackground"] {
1247 | backdrop-filter:blur(var(--blur-radius));
1248 | }
1249 |
1250 | /* Fix Server List Outlined when Clicking */
1251 | [data-list-id="guildsnav"] {
1252 | outline:none !important;
1253 | }
1254 |
1255 | /* Background Image */
1256 | #app-mount::before {
1257 | content:'';
1258 | width:100%;
1259 | height:100%;
1260 | left:0;
1261 | top:0;
1262 | position:absolute;
1263 | background-image:linear-gradient(to right,rgba(var(--background-screen-cover), var(--background-screen-opacity)) 0%,rgba(var(--background-screen-cover), var(--background-screen-opacity)) 100%),var(--background-url); /* Please use this to change background image */
1264 | background-size:cover;
1265 | z-index:-5;
1266 | }
1267 |
1268 | /*
1269 | * Plugin Specific Styling
1270 | */
1271 |
1272 | /* BetterDiscord Plugin Settings */
1273 | .bd-addon-settings-wrap .plugin-inputs {
1274 | transition:all 300ms cubic-bezier(0.47, 0, 0.745, 0.715) !important;
1275 | transform:scaleY(1) !important;
1276 | overflow:inherit !important;
1277 | }
1278 |
1279 | .bd-addon-settings-wrap .plugin-inputs.collapsed {
1280 | transform:scaleY(0) !important;
1281 | }
1282 |
1283 | .bd-addon-settings-wrap .plugin-input-group > [class*="title"] {
1284 | cursor:pointer;
1285 | }
1286 |
1287 | .bd-addon-card {
1288 | backdrop-filter:blur(var(--blur-radius));
1289 | box-shadow:none !important;
1290 | background-color:var(--background-floating) !important;
1291 | }
1292 |
1293 | /* BetterDiscord/ZeresPluginLibrary Toasts */
1294 | .bd-toasts .bd-toast:not([class*="toast_"]), #app-mount .toasts .toast:not([class*="toast_"]) {
1295 | background-color:var(--background-floating) !important;
1296 | }
1297 |
1298 | #app-mount .toasts .toast {
1299 | box-shadow:none !important;
1300 | }
1301 |
1302 | /* EditUploads Plugin */
1303 | #EditUploadsEditButton {
1304 | top:calc(100vh - 1em - 25px) !important;
1305 | position:fixed !important;
1306 | z-index:10000 !important;
1307 | }
1308 |
1309 | #EditUploadsModal {
1310 | border-radius:0;
1311 | box-shadow:none !important;
1312 | background:none !important;
1313 | border:none !important;
1314 | }
1315 |
1316 | #EditUploadsCanvasWrapper, #EditUploadsToolbar {
1317 | box-shadow:none !important;
1318 | background:none !important;
1319 | border:none !important;
1320 | }
1321 |
1322 | #EditUploadsModal::before {
1323 | content:'';
1324 | position:fixed;
1325 | left:0;
1326 | top:0;
1327 | right:0;
1328 | bottom:0;
1329 | backdrop-filter:blur(var(--blur-radius));
1330 | }
1331 |
1332 | #EditUploadsToolbar .toolIconWrapper:not(#foo):hover::after {
1333 | backdrop-filter:blur(var(--blur-radius));
1334 | box-shadow:none !important;
1335 | background-color:var(--background-floating) !important;
1336 | border:none;
1337 | margin-bottom:-2px;
1338 | padding:8px 12px 16px 12px;
1339 | }
1340 |
1341 | /* ImageUtilities Plugin */
1342 | [class*="lensBackdrop_"] {
1343 | backdrop-filter:blur(var(--blur-radius));
1344 | }
1345 |
1346 | /* CharCounter Plugin */
1347 | [class*="messagesWrapper_"] ~ form [class*="charCounter_"] {
1348 | bottom:-0.5em;
1349 | right:10px;
1350 | }
1351 |
1352 | [class*="uploadModal_"] [class*="comment_"] [class*="charCounterAdded_"] {
1353 | position:static !important;
1354 | }
1355 |
1356 | [class*="uploadModal_"] [class*="comment_"] [class*="charCounterAdded_"] [class*="charCounter_"] {
1357 | bottom:0;
1358 | right:-1em;
1359 | }
1360 |
1361 | /* GuildProfile Plugin */
1362 | .guild-profile [class*="body_"] {
1363 | background-color:var(--background-floating) !important;
1364 | }
1365 |
1366 | /* Display Version Info */
1367 | [class*="sidebar_"] [role="tablist"] [class*="info_"]::after {
1368 | content:"Xpose Theme Version: v" var(--xposeVersion);
1369 | color:var(--text-muted);
1370 | font-size:12px;
1371 | line-height:16px;
1372 | }
1373 |
1374 | .bd-addon-list [id*="XposeLoader"] .bd-addon-header .bd-title .bd-meta::after {
1375 | content:"Theme Version: v" var(--xposeVersion);
1376 | padding-left:1em;
1377 | }
1378 |
--------------------------------------------------------------------------------