Themes - Bonk.io

Recolors elements in Bonk.io to customizable colors, and allows toggling your theme with a hotkey

目前為 2021-11-23 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Themes - Bonk.io
// @description  Recolors elements in Bonk.io to customizable colors, and allows toggling your theme with a hotkey
// @author       Excigma
// @namespace    https://greasyfork.org/users/416480
// @license      GPL-3.0
// @version      0.1.7
// @match        https://bonk.io/*
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(() => {
	const configuration_metadata_map = {
		hotkey: {
			default_dark: "d",
			default_light: "d",
			description: "Hotkey used to toggle the theme on and off\n(Ctrl + Alt + <key>)",
			datatype: "char"
		},
		colored_text: {
			default_dark: "#40c99e",
			default_light: "#032a71",
			description: "Text color of custom games with friends online in the room, and 'Added by' in picks",
			datatype: "color"
		},
		primary_text: {
			default_dark: "#f8fafd",
			default_light: "#000000",
			description: "Primary text color",
			datatype: "color"
		},
		secondary_text: {
			default_dark: "#bebebe",
			default_light: "#505050",
			description: "Secondary text color\nNote: Some of the texts are lightened by 'brightness_lighter' above\nthis will have no affect on such texts (Eg: Chat usernames)",
			datatype: "color"
		},
		window_color: {
			default_dark: "#2b6351",
			default_light: "#009688",
			description: "Color of window titles\n(Eg: Custom games, Leave Game, Chat, Level Select, ...)",
			datatype: "color"
		},
		window_text: {
			default_dark: "#f8fafd",
			default_light: "#ffffff",
			description: "Color of text in window titles\n(Eg: Custom games, Leave Game, Chat, Level Select, ...)",
			datatype: "color"
		},
		page_background: {
			default_dark: "#111111",
			default_light: "#1a2733",
			description: "Used for the main page's background",
			datatype: "color"
		},
		primary_background: {
			default_dark: "#222222",
			default_light: "#e2e2e2",
			description: "Used as the main background color of windows\n(Eg: Chat background, Leave lobby background, Login page)",
			datatype: "color"
		},
		secondary_background: {
			default_dark: "#333333",
			default_light: "#f3f3f3",
			description: "Secondary background color for things inside windows\n(Eg: Auto login, Joining room status text, Map Editor inputs, ...)",
			datatype: "color"
		},
		behind_lobby_background: {
			default_dark: "#131313",
			default_light: "#1a2733",
			description: "Color for the Lobby's background\n(Eg: Behind the Lobby and Map Editor)",
			datatype: "color"
		},
		red_text: {
			default_dark: "#e14747",
			default_light: "#cc3333",
			description: "Used for player nerf indicator in the lobby",
			datatype: "color"
		},
		blue_text: {
			default_dark: "#179be8",
			default_light: "#0955c7",
			description: "Used for [Load] when a map is suggested, and Copy link is clicked",
			datatype: "color"
		},
		green_text: {
			default_dark: "#17e88b",
			default_light: "#155824",
			description: "Used for player buff and in chat friend requests",
			datatype: "color"
		},
		purple_text: {
			default_dark: "#8f68e8",
			default_light: "#6033cc",
			description: "Used for output of /curate",
			datatype: "color"
		},
		magenta_text: {
			default_dark: "#d23cfb",
			default_light: "#800d6e",
			description: "Used for when [Load] is clicked, or when you are now the host of the game",
			datatype: "color"
		},
		button_color: {
			default_dark: "#4f382f",
			default_light: "#795548",
			description: "Button/dropdown color",
			datatype: "color"
		},
		hover_button_color: {
			default_dark: "#3a2a24",
			default_light: "#7f5d51",
			description: "Button/dropdown hover color",
			datatype: "color"
		},
		active_button_color: {
			default_dark: "#362620",
			default_light: "#4b252b",
			description: "Button/dropdown click color",
			datatype: "color"
		},
		disabled_button_color: {
			default_dark: "#444444",
			default_light: "#777777",
			description: "Button/dropdown disabled color",
			datatype: "color"
		},
		button_text: {
			default_dark: "#f8fafd",
			default_light: "#ffffff",
			description: "Button/dropdown text color",
			datatype: "color"
		},
		xp_bar_fill: {
			default_dark: "#473aaf",
			default_light: "#473aaf",
			description: "XP bar fill color at the top of your screen whilst in-game",
			datatype: "color"
		},
		top_bar_opacity: {
			default_dark: "0.8",
			default_light: "0.8",
			description: "Opacity of the 'top bar' that contains your:\nSkin, Username, Level, Volume Settings, ...",
			datatype: "percentage"
		},
		top_bar_color: {
			default_dark: "#333333",
			default_light: "#273749",
			description: "Color of the 'top bar'\nMake sure this value is dark, the icon colors are white and can't really be easily changed",
			datatype: "color"
		},
		top_bar_hover_color: {
			default_dark: "#222222",
			default_light: "#2d435a",
			description: "Color of buttons in the 'top bar' then hovered",
			datatype: "color"
		},
		top_bar_text: {
			default_dark: "#bebebe",
			default_light: "#bebebe",
			description: "Color of text in the 'top bar'",
			datatype: "color"
		},
		football_background: {
			default_dark: "#161616",
			default_light: "#5a7f64",
			description: "Color for the Football gamemode's background\n(Note: Requires restarting the football to apply)\nWarning: Unstable, experimental",
			datatype: "color"
		},
		border_color: {
			default_dark: "#333333",
			default_light: "#a5acb0",
			description: "Color used for subtile borders\n(Eg. Settings border, line separating chat and messages in lobby)",
			datatype: "color"
		},
		mini_menu_color: {
			default_dark: "#191919",
			default_light: "#1e2833",
			description: "Used for Tooltips and 'Filter by' in Custom Games",
			datatype: "color"
		},
		mini_menu_text: {
			default_dark: "#bebebe",
			default_light: "#ffffff",
			description: "Used for Tooltips and 'Filter by' in Custom Games",
			datatype: "color"
		},
		scrollbar_background: {
			default_dark: "#191919",
			default_light: "#dddddd",
			description: "Used for the scrollbar's background (Chromium only)",
			datatype: "color"
		},
		scrollbar_thumb: {
			default_dark: "#555555",
			default_light: "#aaaaaa",
			description: "Used for the scrollbar's thumb - which is the thing you drag (Chromium only)",
			datatype: "color"
		},
		list_headers: {
			default_dark: "#2b3839",
			default_light: "#a8bcc0",
			description: "Used for the colors of lists\n(Eg: Friends list and the sections in the Map Editor, ...)",
			datatype: "color"
		},
		table_color: {
			default_dark: "#444444",
			default_light: "#c1cdd2",
			description: "Stripe color for use in tables\n(Eg: Custom games, Friends list, Map Editor, Skin Editor, ...)",
			datatype: "color"
		},
		table_alt_color: {
			default_dark: "#333333",
			default_light: "#d2dbde",
			description: "Alternative stripe color used in tables",
			datatype: "color"
		},
		table_hover_color: {
			default_dark: "#222222",
			default_light: "#aac5d7",
			description: "Color used when a row in a table is hovered",
			datatype: "color"
		},
		table_active_color: {
			default_dark: "#111111",
			default_light: "#9cc8d6",
			description: "Color used when a row in a table is selected",
			datatype: "color"
		},
		brightness_lighter: {
			default_dark: "2.5",
			default_light: "1",
			description: "Used to lighten text that have bad contrast with dark colors\nNote: Change to 1 if making a light theme\n(0.5 = 50%, 1.5 = 150% brightness)",
			datatype: "brightness"
		},
		brightness_darker: {
			default_dark: "0.5",
			default_light: "1",
			description: "Used to darken images that that are too bright\nNote: Change to 1 if making a light theme\n(0.5 = 50%, 1.5 = 150% brightness)",
			datatype: "brightness"
		},
		description_invert: {
			default_dark: "1",
			default_light: "0",
			description: "How much to invert description at the bottom of the page by\n(Value from 0 to 1, Don't use 0.5)",
			datatype: "percentage"
		},
	};

	const datatype_metadata_map = {
		"color": {
			value: "value",
			attributes: {
				type: "color"
			}
		},
		"percentage": {
			value: "value",
			attributes: {
				type: "number",
				max: 1,
				min: 0,
				step: 0.01
			}
		},
		"brightness": {
			value: "value",
			attributes: {
				type: "number",
				max: 10,
				min: 0,
				step: 0.05
			}
		},
		"char": {
			value: "value",
			attributes: { 
				type: "text" ,
				maxLength: 1 
			}
		},
	};

	const default_configuration = {};
	for (const [key, value] of Object.entries(configuration_metadata_map)) 
		default_configuration[key] = value.default_dark;
		
	// If running inside maingameframe, inject our injectors
	if (unsafeWindow.parent !== unsafeWindow) {
		unsafeWindow.bonk_theme = {
			on: true,
			get_football_background: () => parseInt(getComputedStyle(document.documentElement)
				.getPropertyValue("--bonk_theme_football_background").trim().slice(1), 16),
			get_hotkey: () => getComputedStyle(document.documentElement)
				.getPropertyValue("--bonk_theme_hotkey").trim() || default_configuration.hotkey
		};

		// Injector for football mode, if it doesn't work, ...well football will be bright
		if (!unsafeWindow.bonkCodeInjectors) unsafeWindow.bonkCodeInjectors = [];
		unsafeWindow.bonkCodeInjectors.push(bonkCode => {
			try {
				// Default football background color, not used elsewhere (yet?)
				const FOOTBALL_BACKGROUND_COLOR = "0x5a7f64";
				let newBonkCode = bonkCode;
				newBonkCode = newBonkCode
					.replace(
						FOOTBALL_BACKGROUND_COLOR,
						`(window.bonk_theme.on ? window.bonk_theme.get_football_background() : ${FOOTBALL_BACKGROUND_COLOR})`
					);
				if (bonkCode === newBonkCode) throw "[Themes] Injection failed!";

				console.log("Themes injector run");
				return newBonkCode;
			} catch (er) {
				// Silently ignore errors, only football's background will be affected
				console.log("[Themes] Failed to inject");
				console.error(er);
				return bonkCode;
			}
		});
	}

	document.addEventListener("DOMContentLoaded", () => {
		document.body.classList.add("themed-colors");
		// Get the currently applied theme
		const get_stored_theme = () => {
		// Get the stored theme, if it doesn't exist, it will be null
			let stored_theme = localStorage.getItem("bonk_theme");
			try {
				stored_theme = JSON.parse(stored_theme);
			} catch (er) {
			// If the stored theme is somehow corrupted - use blank theme
				console.log("[Themes] Failed to load theme from localstorage");
				console.log(`[Themes] ${stored_theme}`);
				console.error(er);
				alert("Failed to load theme from localstorage");
				stored_theme = {};
			}
			// Merge the stored theme with the default (if null, it will overwrite)
			return {...default_configuration, ...stored_theme};
		};

		// Get the stored theme when the page loads
		const configuration = get_stored_theme();
	
		// Check if the script is running inside maingameframe or not
		if (unsafeWindow.parent === unsafeWindow) {
			// Outer bonk.io website
			// Hotkey to turn the script on and off
			const maingameframe = document.getElementById("maingameframe");
			document.addEventListener("keydown", evt => {
				if (evt.key?.toLowerCase() === maingameframe.contentWindow.bonk_theme.get_hotkey() && evt.ctrlKey && evt.altKey) {			
					document.body.classList.toggle("themed-colors");
					maingameframe.contentDocument.body.classList.toggle("themed-colors");
					maingameframe.contentWindow.bonk_theme.on = !maingameframe.contentWindow.bonk_theme.on;
				}
			});

			// Flag to check whether the theme container already exists
			let theme_container_created = false;
			// Create the menu only when someone clicks on the thing in the menu
			// Show the theme container when you click the menu command thing
			// eslint-disable-next-line no-undef
			GM_registerMenuCommand("Edit theme", () => {
				// Creating a new theme_container, don't make new ones after this
				if (theme_container_created) return document.getElementById("theme_container").style.display = "block";
				theme_container_created = true;

				// Create the theme menu. This is really ugly but there's like no other way idk.
				const theme_container = document.createElement("div");
				const theme_container_title = document.createElement("p");
				const configuration_container = document.createElement("div");
				const configuration_container_label = document.createElement("label");
				const configuration_list = document.createElement("div");
				const configuration_json = document.createElement("textarea");
				const configuration_json_label = document.createElement("label");
				const theme_cancel = document.createElement("div");
				const theme_save = document.createElement("div");
				const theme_reset_dark = document.createElement("div");
				const theme_reset_light = document.createElement("div");
		
				// Add the IDs so we can style them with CSS
				theme_container_title.id = "theme_container_title";
				theme_container.id = "theme_container";
				configuration_container_label.id = "configuration_container_label";
				configuration_list.id = "configuration_list";
				configuration_json.id = "configuration_json";
				theme_cancel.id = "theme_cancel";
				theme_save.id = "theme_save";
				theme_reset_dark.class = "theme_reset";
				theme_reset_light.class = "theme_reset";

				// Add the classes
				theme_container_title.classList.add("classicTopBar");
				theme_cancel.classList.add("brownButton", "brownButton_classic", "buttonShadow");
				theme_save.classList.add("brownButton", "brownButton_classic", "buttonShadow");
				theme_reset_dark.classList.add("brownButton", "brownButton_classic", "buttonShadow");
				theme_reset_light.classList.add("brownButton", "brownButton_classic", "buttonShadow");

				// Add text to buttons and titles
				theme_container_title.textContent = "Theme Editor";
				theme_cancel.textContent = "CANCEL";
				theme_save.textContent = "SAVE";
				theme_reset_dark.textContent = "RESET THEME TO DARK PRESET";
				theme_reset_light.textContent = "RESET THEME TO LIGHT PRESET";
				configuration_container_label.textContent = "Hover over settings for more info";
				configuration_json_label.textContent = "Share, Backup or Import theme data - Copy or paste themes from/into the textbox below";
				configuration_json_label.for = "configuration_json";
				configuration_json.value = JSON.stringify(configuration);
				
				theme_container.appendChild(theme_container_title);

				configuration_list.appendChild(configuration_container_label);
				configuration_list.appendChild(document.createElement("br"));

				// Populate the themes list with the theme that is currently loaded
				for (const [key, value] of Object.entries(configuration)) {
				// Check if the setting exists
					if (!configuration_metadata_map[key]) continue;
					const datatype_metadata = datatype_metadata_map[configuration_metadata_map[key].datatype];
					const configuration_container = document.createElement("div");
					const configuration_name = document.createElement("p");
					const configuration_value = document.createElement("input");
			
					configuration_container.classList.add("configuration_container");
					configuration_name.classList.add("configuration_name");
					configuration_value.classList.add("configuration_value");

					configuration_container.dataset["configuration_name"] = key;
					configuration_container.title = configuration_metadata_map[key].description ?? "No description";
					configuration_name.textContent = key.replace(/_/g, " ");

					for (const [key, value] of Object.entries(datatype_metadata.attributes))
						configuration_value[key] = value;
					configuration_value[datatype_metadata.value] = value;
					// Using "oninput" instead of addEventListener so we can call it somewhere else
					configuration_value.oninput = () => {
						if (!datatype_metadata.ignore_change) {
						// Update the CSS variable on both bonk.io and maingameframe
							document.documentElement.style
								.setProperty(`--bonk_theme_${key}`, configuration_value.value);
							maingameframe.contentDocument.documentElement.style
								.setProperty(`--bonk_theme_${key}`, configuration_value.value);
							// Store it in the current configuration (so it can be saved later)
							configuration[key] = configuration_value[datatype_metadata.value];
						}

						// Update the data at the bottom
						configuration_json.value = JSON.stringify(configuration);
					};

					// Add the current config to the list of settings
					configuration_container.appendChild(configuration_name);
					configuration_container.appendChild(configuration_value);
					configuration_list.appendChild(configuration_container);
				}

				// Add the Save and Cancel buttons
				configuration_container.appendChild(theme_cancel);
				configuration_container.appendChild(theme_save);

				configuration_list.appendChild(document.createElement("br")); // this
				configuration_list.appendChild(configuration_container);
				configuration_list.appendChild(document.createElement("br")); // is
				configuration_list.appendChild(configuration_json_label);
				configuration_list.appendChild(document.createElement("br")); // so
				configuration_list.appendChild(configuration_json);
				configuration_list.appendChild(document.createElement("br")); // bad
				configuration_list.appendChild(theme_reset_dark);
				configuration_list.appendChild(theme_reset_light);
				theme_container.appendChild(configuration_list);

				// When save is clicked, try save theme to localstorage
				theme_save.addEventListener("click", () => {
					try {
						localStorage.setItem("bonk_theme", JSON.stringify(configuration));
						theme_container.style.display = "none";
					} catch (er) {
						console.log("[Themes] Failed to save theme to localstorage");
						console.log(`[Themes] ${JSON.stringify(configuration)}`);
						console.error(er);
						alert("Failed to save theme to localstorage");
					}
				});

				// Reset the state of configuration from localstorage
				theme_cancel.addEventListener("click", () => {
					configuration_json.value = JSON.stringify(get_stored_theme());
					configuration_json.oninput();
					theme_container.style.display = "none";
				});

				// Get the currently saved theme, and overwrite everything with it
				theme_reset_dark.addEventListener("click", () => {
					configuration_json.value = JSON.stringify(default_configuration);
					configuration_json.oninput();
				});

				theme_reset_light.addEventListener("click", () => {
					// Light theme defaults are not calculated by default, so we need to calculate it here now
					const default_light_configuration = {};
					for (const [key, value] of Object.entries(configuration_metadata_map)) 
						default_light_configuration[key] = value.default_light;

					configuration_json.value = JSON.stringify(default_light_configuration);
					configuration_json.oninput();
				});

				configuration_json.oninput = () => {
					try {
						const new_theme = JSON.parse(configuration_json.value);
						// Update the configurations
						const configuration_list_children = Array.from(configuration_list.children);
						for (const configuration_container of configuration_list_children) {
							const key = configuration_container.dataset["configuration_name"];
							const value = new_theme[key];
							if (!value) continue;

							const configuration_value = configuration_container.querySelector("input");
							const datatype_meta = datatype_metadata_map[configuration_metadata_map[key].datatype];
						
							configuration_value[datatype_meta.value] = value;
							configuration_value.oninput();
						}

						// If success, remove the border
						configuration_json.style.setProperty("border", "none");
					} catch (er) {
						console.log("[Themes] Failed to load theme from <textarea>");
						console.log(`[Themes] ${configuration_json.value}`);
						console.error(er);
						// If there is an error, set a red border
						configuration_json.style.setProperty("border", "3px solid var(--bonk_theme_red)", "important");
					}
				};

				// Add the theme dialog to body
				document.body.appendChild(theme_container);
			});
		} else {
			// This code is for inside maingameframe
			// Hotkey to turn the script on and off
			document.addEventListener("keydown", evt => {
				if (evt.key?.toLowerCase() === unsafeWindow.bonk_theme.get_hotkey() && evt.ctrlKey && evt.altKey) {
					unsafeWindow.bonk_theme.on = !unsafeWindow.bonk_theme.on;
					document.body.classList.toggle("themed-colors");
					unsafeWindow.parent.document.body.classList.toggle("themed-colors");
				}
			});
		}

		// List of default CSS variables
		let root_vars = [];
		for (const [key, value] of Object.entries(configuration))
			root_vars.push(`--bonk_theme_${key}: ${value};`);

		// eslint-disable-next-line no-undef
		GM_addStyle(`
	/* CSS Variables, so we can edit their values on the fly */
	:root {
		${root_vars.join("\n		")}
	}

	/* Page background color */
	.themed-colors #pagecontainer,
	body.themed-colors { 
		background-color: var(--bonk_theme_page_background) !important;
		--kkleeMultiSelectColour: ${configuration.colored_text};
		--kkleeErrorColour: ${configuration.red_text};
		--kkleeCheckboxTfsTrue: ${configuration.green_text};
		--kkleeCheckboxTfsFalse: ${configuration.red_text};
	}

	/* Dark scroll bar tracks on Firefox and Chromium-based browsers */
	.themed-colors {
		scrollbar-color: var(--bonk_theme_scrollbar_thumb) var(--bonk_theme_scrollbar_background) !important;
	}

	.themed-colors ::-webkit-scrollbar-track {
		background-color: var(--bonk_theme_scrollbar_background) !important;
	}

	.themed-colors ::-webkit-scrollbar-thumb { 
		background-color: var(--bonk_theme_scrollbar_thumb) !important;
	}

	/* Color tooltips */
	.themed-colors #friendsToolTip,
	.themed-colors #pretty_top_replay_report_tooltip,
	.themed-colors #pretty_top_replay_fav_tooltip,
	.themed-colors #newbonklobby_tooltip,
	.themed-colors #mapeditor_rightbox_shapeaddmenucontainer,
	.themed-colors #mapeditor_leftbox_createmenucontainerleft,
	.themed-colors #mapeditor_leftbox_createmenucontainerright,
	.themed-colors #mapeditor_leftbox_copywindow,
	.themed-colors #mapeditor_rightbox_newjointmenu,
	.themed-colors .newbonklobby_playerentry_menu,
	.themed-colors .newbonklobby_playerentry_menu_submenu {
		background-color: var(--bonk_theme_mini_menu_color) !important;
		color: var(--bonk_theme_mini_menu_text) !important;
	}

	/* Color the lobby's background */
	.themed-colors #bonkiocontainer {
		background-color: var(--bonk_theme_behind_lobby_background) !important;
	}
	
	/* Dialogs - I can just do windowShadow, but it might break things if new things are added */
	/* It's better to have a new thing visibly light rather than broken */
	.themed-colors .bt-primary-background,
	.themed-colors #autoLoginContainer,
	.themed-colors #guestOrAccountContainer_accountBox,
	.themed-colors #guestOrAccountContainer_guestBox,
	.themed-colors #guestContainer, 
	.themed-colors .accountContainer,
	.themed-colors #registerwindow_remember_label,
	.themed-colors #loginwindow_remember_label,
	.themed-colors #loginwindow,
	.themed-colors #registerwindow,
	.themed-colors #settingsContainer,
	.themed-colors .settingsHeading,
	.themed-colors .redefineControls_selectionCell:hover,
	.themed-colors #newswindow,
	.themed-colors #skinmanager,
	.themed-colors .skineditor_shapewindow,
	.themed-colors .skineditor_shapewindow_imagecontainer,
	.themed-colors #skineditor_propertiesbox,
	.themed-colors #skineditor_propertiesbox_table,
	.themed-colors #skineditor_previewbox,
	.themed-colors #skineditor_layerbox,
	.themed-colors #skineditor_layerbox_baselabel,
	.themed-colors #quickPlayWindow,
	.themed-colors #roomlistcreatewindow,
	.themed-colors .roomlistcreatewindowlabel,
	.themed-colors #roomlistjoinpasswordwindow,
	.themed-colors #sm_connectingWindow,
	.themed-colors #newbonklobby_specbox,
	.themed-colors #newbonklobby_playerbox,
	.themed-colors #newbonklobby_chatbox,
	.themed-colors #newbonklobby_settingsbox,
	.themed-colors #newbonklobby_votewindow,
	.themed-colors #newbonklobby_votewindow_maptitle,
	.themed-colors #newbonklobby_votewindow_mapauthor,
	.themed-colors #leaveconfirmwindow,
	.themed-colors #leaveconfirmwindow_text2,
	.themed-colors #hostleaveconfirmwindow,
	.themed-colors #hostleaveconfirmwindow_text2,
	.themed-colors #maploadwindow,
	.themed-colors #maploadwindowgreybar,
	.themed-colors #maploadwindowstatustext,
	.themed-colors #mapeditor_leftbox,
	.themed-colors #mapeditor_midbox,
	.themed-colors #mapeditor_rightbox,
	.themed-colors #mapeditor_save_window,
	.themed-colors #kkleeRoot,
	.themed-colors #skineditor_colorpicker,
	.themed-colors #mapeditor_colorpicker,
	.themed-colors #friendsSendWindow,
	.themed-colors #roomlistfilterwindow,
	.themed-colors #ingamecountdown,
	.themed-colors #mapeditor_save_overwrite_window,
	#theme_container {
		background-color: var(--bonk_theme_primary_background) !important;
		color: var(--bonk_theme_primary_text) !important;
	}
	
	/* Lighter backgrounds for some menus and stuff */
	.themed-colors .bt-secondary-background,
	.themed-colors #bonkioheader,
	.themed-colors .windowTopBar_classic,
	.themed-colors #autoLogin_text,
	.themed-colors .guestOrAccountContainerLabelBox,
	.themed-colors #guest_nametext,
	.themed-colors #loginwindow_username,
	.themed-colors #loginwindow_password,
	.themed-colors #registerwindow_username,
	.themed-colors #registerwindow_password,
	.themed-colors #guest_skinbox,
	.themed-colors #skineditor_propertiesbox_blocker,
	.themed-colors #newswindow_white,
	.themed-colors .quickPlayWindowModeDiv,
	.themed-colors .quickPlayWindowText1,
	.themed-colors .quickPlayWindowText2,
	.themed-colors .quickPlayWindowText3,
	.themed-colors #roomlist,
	.themed-colors #roomlisttableheadercontainer,
	.themed-colors .roomlistcreatewindowinput,
	.themed-colors .whiteInputField,
	.themed-colors #sm_connectingWindow_text,
	.themed-colors #friendsContainer,
	.themed-colors .friends_table,
	.themed-colors .skinmanager_icon,
	.themed-colors #maploadwindowsearchinput,
	.themed-colors .maploadwindowmapdiv,
	.themed-colors #mapeditor_midbox_explain,
	.themed-colors #mapeditor_rightbox_namefield,
	.configuration_value,
	#configuration_json {
		border-color: transparent !important;
		background-color: var(--bonk_theme_secondary_background) !important;
		color: var(--bonk_theme_primary_text) !important;
	}
	
	.themed-colors .mapeditor_field,
	.themed-colors .skineditor_field,
	.themed-colors .mapeditor_rightbox_table_shape_headerfield {
		border-color: transparent;
		background-color: var(--bonk_theme_secondary_background);
		color: var(--bonk_theme_primary_text);
	}

	.themed-colors .newbonklobby_playerentry_name,
	.themed-colors #newbonklobby_modetext,
	.themed-colors #newbonklobby_roundslabel,
	.themed-colors .maploadwindowtext,
	.themed-colors #roomliststatustext, 
	.themed-colors #roomlisttable,
	.themed-colors .mapeditor_rightbox_table_leftcell,
	.themed-colors .mapeditor_rightbox_table_rightcell,
	.themed-colors #ingamecountdown_text,
	.themed-colors #bonkiobugemail {
		color: var(--bonk_theme_primary_text) !important;
	}

	.themed-colors .newbonklobby_mapsuggest_high,
	.themed-colors .maploadwindowtext_picks,
	.themed-colors .newbonklobby_chat_msg_name,
	.themed-colors .newbonklobby_playerentry_balancetext {
		color: var(--bonk_theme_primary_text);
	}

	.themed-colors .newbonklobby_playerentry_level,
	.themed-colors .newbonklobby_playerentry_pingtext, 
	.themed-colors #newbonklobby_chat_lowerinstruction,
	.themed-colors #newbonklobby_chat_lowerline, 
	.themed-colors .newbonklobby_chat_msg_txt,
	.themed-colors #newbonklobby_chat_input,
	.themed-colors #newbonklobby_maptext,
	.themed-colors #newbonklobby_roundsinput,
	.themed-colors #newbonklobby_mapauthortext,
	.themed-colors #newbonklobby_votewindow_maptitle_by,
	.themed-colors #hostleaveconfirmwindow_text1,
	.themed-colors #leaveconfirmwindow_text1,
	.themed-colors .maploadwindowtextmode_picks,
	.themed-colors .maploadwindowtextmode,
	.themed-colors .maploadwindowtextauthor,
	.themed-colors .maploadwindowtextauthor_picks,
	.themed-colors .roomlisttablejoined {
		color: var(--bonk_theme_secondary_text) !important;
	}

	/* Lobby chat */
	.themed-colors .newbonklobby_mapsuggest_low {
		color: var(--bonk_theme_secondary_text);
	}

	/* Copy link */
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(9, 85, 199);"],
	.themed-colors .newbonklobby_chat_link {
		color: var(--bonk_theme_blue_text) !important;
	}

	/* /help, player join */
	.themed-colors .newbonklobby_playerentry_balance_nerf,
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(204, 68, 68);"],
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(204, 51, 51);"],
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(255, 0, 0);"] {
		color: var(--bonk_theme_red_text) !important;
	}

	/* Friend requests */
	.themed-colors .newbonklobby_playerentry_balance_buff,
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(0, 103, 93);"] {
		color: var(--bonk_theme_green_text) !important;
	}

	/* /curate */
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(96, 51, 204);"] {
		color: var(--bonk_theme_purple_text) !important;
	}

	/* "You are now host" */
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(122, 25, 154);"],
	.themed-colors #newbonklobby_chat_content span[style="color: rgb(128, 13, 110);"] {
		color: var(--bonk_theme_magenta_text) !important;
	}

	.themed-colors .newbonklobby_chat_status:not([style]) {
		filter: brightness(var(--bonk_theme_brightness_lighter)) !important;
	}

	.themed-colors #descriptioncontainer,
	.themed-colors #gamethumbbox,
	.themed-colors #bgreplay {
		filter: brightness(var(--bonk_theme_brightness_darker)) !important;
	}

	/* Style sliders and buttons */
	.themed-colors .compactSlider {
		background-color: transparent !important;
		color: var(--bonk_theme_button_text) !important;
	}

	.themed-colors .brownButton_classic,
	.themed-colors .dropdown_classic,
	.themed-colors .skineditor_field_button,
	.themed-colors .mapeditor_field_button,
	#theme_save, #theme_cancel, .theme_reset {
		border-color: transparent !important;
		background-color: var(--bonk_theme_button_color) !important;
		color: var(--bonk_theme_button_text) !important;
	}

	.themed-colors .brownButtonDisabled,
	.themed-colors .brownButton_disabled_classic,
	.themed-colors .dropdown-option-disabled {
		background-color: var(--bonk_theme_disabled_button_color) !important;
		color: var(--bonk_theme_button_text) !important;
	}

	.themed-colors .brownButton_classic:hover,
	.themed-colors .dropdown_classic:hover,
	#theme_save:hover, #theme_cancel:hover, .theme_reset:hover {
		background-color: var(--bonk_theme_hover_button_color) !important;
		color: var(--bonk_theme_button_text) !important;
	}

	.themed-colors .brownButton_classic:active,
	.themed-colors .dropdown_classic:active,
	.themed-colors .newbonklobby_teamlockbutton_warn,
	#theme_save:active, #theme_cancel:active, .theme_reset:active {
		background-color: var(--bonk_theme_active_button_color) !important;
		color: var(--bonk_theme_button_text) !important;
	}

	/* Tables in the game - Skin editor, Map Editor and Custom game list */
	.themed-colors #roomlisttable tr:nth-child(odd),
	.themed-colors .friends_table>tbody:nth-child(odd),
	.themed-colors #skineditor_layerbox_layertable tr:nth-child(odd),
	.themed-colors #mapeditor_leftbox_platformtable tr:nth-child(odd),
	.themed-colors #mapeditor_leftbox_spawntable tr:nth-child(odd),
	.themed-colors #redefineControls_table tr:nth-child(odd),
	.themed-colors .roomlistfilterwindow_a,
	.themed-colors #redefineControls_table th {
		background-color: var(--bonk_theme_table_color) !important;
		color: var(--bonk_theme_primary_text) !important;
	}

	.themed-colors #roomlisttable tr:nth-child(even),
	.themed-colors .friends_table tr:nth-child(even),
	.themed-colors #skineditor_layerbox_layertable tr:nth-child(even),
	.themed-colors #mapeditor_leftbox_platformtable tr:nth-child(even),
	.themed-colors #mapeditor_leftbox_spawntable tr:nth-child(even),
	.themed-colors #redefineControls_table tr:nth-child(even),
	.themed-colors #roomlisttableheadercontainer,
	.themed-colors .roomlistfilterwindow_b {
		background-color: var(--bonk_theme_table_alt_color) !important;
		color: var(--bonk_theme_primary_text) !important;
	}

	.themed-colors #roomlisttable tr.FRIENDSPRESENT,
	.themed-colors .maploadwindowtextaddedby_picks {
		color: var(--bonk_theme_colored_text) !important;
	}

	.themed-colors tr.HOVERUNSELECTED:hover td {
		background-color: var(--bonk_theme_table_hover_color) !important;
	}

	.themed-colors tr.HOVERSELECTED td,
	.themed-colors tr.SELECTED td {
		background-color: var(--bonk_theme_table_active_color) !important;
	}

	/* "popup" color, custom room list top bar */
	.themed-colors .bt-titlebar,
	.themed-colors .windowTopBar_classic, 
	.themed-colors .classicTopBar,
	.themed-colors .newbonklobby_boxtop, 
	.themed-colors .newbonklobby_boxtop_classic,
	.themed-colors .friends_topbar,
	.themed-colors #newbonklobby_votewindow_top,
	.themed-colors #hostleaveconfirmwindow_top,
	.themed-colors #leaveconfirmwindow_top,
	.themed-colors #ingamecountdown_top,
	.themed-colors #roomlisthidepasswordedcheckboxlabel,
	#theme_container_title {
		background-color: var(--bonk_theme_window_color) !important;
		color: var(--bonk_theme_window_text) !important;
	}

	/* Style the top bar */
	.themed-colors #pretty_top_bar {
		background-color: var(--bonk_theme_top_bar_color) !important;
		opacity: var(--bonk_theme_top_bar_opacity) !important;
		color: var(--bonk_theme_top_bar_text) !important;
	}

	.themed-colors .pretty_top_button,
	.themed-colors #pretty_top_name,
	.themed-colors #pretty_top_name,
	.themed-colors #pretty_top_level,
	.themed-colors #pretty_top_playercount {
		background-color: var(--bonk_theme_top_bar_color) !important;
		color: var(--bonk_theme_top_bar_text) !important;
	}

	.themed-colors .pretty_top_button:hover {
		background-color: var(--bonk_theme_top_bar_hover_color) !important;
	}

	/* Invert the Bonk.io description at the bottom of the page */
	.themed-colors #descriptioninner,
	.themed-colors #descriptioninner .descriptionthumbr,
	.themed-colors #descriptioninner .descriptionthumbl {
		filter: invert(var(--bonk_theme_description_invert)) !important;
	}

	/* Change color of borders in settings */
	.themed-colors #redefineControls_table td,
	.themed-colors #redefineControls_table th,
	.themed-colors #redefineControls_table {
		border: 1px solid var(--bonk_theme_border_color) !important;
	}

	/* Players in lobby */
	.themed-colors .newbonklobby_playerentry {
		border-left: 4px solid var(--bonk_theme_primary_background) !important;
		border-top: 4px solid var(--bonk_theme_primary_background) !important;
		border-right: 4px solid var(--bonk_theme_primary_background) !important;
		background-color: var(--bonk_theme_primary_background) !important;
		color: var(--bonk_theme_primary_text) !important;
	}

	.themed-colors .newbonklobby_playerentry:hover {
		border-left: 4px solid var(--bonk_theme_secondary_background) !important;
		border-top: 4px solid var(--bonk_theme_secondary_background) !important;
		border-right: 4px solid var(--bonk_theme_secondary_background) !important;
		background-color: var(--bonk_theme_secondary_background) !important;
	}

	.themed-colors #newbonklobby_chat_lowerline {
		border-top: 1px solid var(--bonk_theme_border_color) !important;
	}

	.themed-colors #newbonklobby_playerbox_middleline {
		border-left: 1px solid var(--bonk_theme_border_color) !important;
	}

	/* Headings in the Map Editor and stuff */
	.themed-colors .friends_titles,
	.themed-colors .mapeditor_table_heading_div {
		background-color: var(--bonk_theme_list_headers) !important;
	}

	/* Change the XP bar to use the colors we want */
	.themed-colors #xpbarfill {
		background-color: var(--bonk_theme_xp_bar_fill) !important;
	}

	/* Below is CSS for our theme editor thing */
	#theme_container {
		border-radius: 3px 0px 3px 3px;
		z-index: 999;
		font-family: "futurept_b1";
		padding: 0px;
		right: 0px;
		top: 0px;
		position: fixed;
		overflow-y: scroll;
		max-height: 100vh;
		width: 20em;
	}

	#configuration_list {
		padding: 1.5em;
		display: flex;
		flex-direction: column;
	}

	#theme_container_title {
		text-align: center;
		margin: 0px;
		font-size: 20px;
		line-height: 32px;
		font-family: "futurept_b1"
	}

	.configuration_container {
		white-space: pre-line;
		display: flex;
		justify-content: space-between;
		align-items: center;
		height: 2.25em;
	}

	.configuration_name {
		text-transform: capitalize;
		float: left;
		width: 70%;
	}

	.configuration_value {
		float: right;
		width: 25%;
	}

	#theme_cancel {
		float: left;
	}

	#theme_save {
		float: right;
	}

	#theme_save,
	#theme_cancel {
		width: 45%;
		height: 30px;
		line-height: 30px;
	}

	#configuration_json {
		align-self: center;
		resize: none;
		width: 100%;
		height: 15em;
	}
	`);
	});
})();