diff --git a/dribbblish-dynamic.js b/dribbblish-dynamic.js
index 64a10c1..06bc43e 100644
--- a/dribbblish-dynamic.js
+++ b/dribbblish-dynamic.js
@@ -176,17 +176,25 @@ function toggleDark(setDark) {
/* Init with current system light/dark mode */
let systemDark = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--system_is_dark")) == 1;
-DribbblishShared.config.registerSelect("Theme", "theme", ["System", "Dark", "Light"], 0, (val) => {
- switch (val) {
- case 0:
- toggleDark(systemDark);
- break;
- case 1:
- toggleDark(true);
- break;
- case 2:
- toggleDark(false);
- break;
+DribbblishShared.config.register({
+ type: "select",
+ options: ["System", "Dark", "Light"],
+ key: "theme",
+ name: "Theme",
+ description: "Select Dark / Bright mode",
+ defaultValue: 0,
+ onChange: (val) => {
+ switch (val) {
+ case 0:
+ toggleDark(systemDark);
+ break;
+ case 1:
+ toggleDark(true);
+ break;
+ case 2:
+ toggleDark(false);
+ break;
+ }
}
});
@@ -321,11 +329,15 @@ hookCoverChange(false);
document.querySelector(".main-userWidget-box").append(upd)
upd.append(`Theme UPD v${data.tag_name} avail.`)
upd.setAttribute("title", `Changes: ${data.name}`)
- DribbblishShared.configButton.addItem(
- new Spicetify.Menu.Item("Update", false, (self) => {
+ DribbblishShared.config.register({
+ insertOnTop: true,
+ type: "button",
+ name: "Update",
+ description: "Open the GitHub Page with Installation instructions / Commands.",
+ onChange: () => {
window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme#install", "_blank");
- })
- );
+ }
+ });
}
}).catch(err => {
// Do something for an error here
diff --git a/dribbblish.js b/dribbblish.js
index 411f0de..dfa24f4 100644
--- a/dribbblish.js
+++ b/dribbblish.js
@@ -1,133 +1,213 @@
// Hide popover message
// document.getElementById("popover-container").style.height = 0;
-const DribbblishShared = {
- configButton: new Spicetify.Menu.SubMenu("Dribbblish", []),
- config: {
- register: (name, key, defaultValue, update) => {
- const menuItem = new Spicetify.Menu.Item(name, defaultValue, (self) => {
- self.setState(!self.isEnabled);
- DribbblishShared.config.toggle(key);
- });
- DribbblishShared.configButton.addItem(menuItem);
+class ConfigMenu {
+ constructor() {
+ this.config = {};
+ this.configButton = new Spicetify.Menu.Item("Dribbblish config", false, () => DribbblishShared.config.open());
+ this.configButton.register();
- if (localStorage.getItem(`dribbblish:config:${key}`) == null) localStorage.setItem(`dribbblish:config:${key}`, defaultValue);
+ const container = document.createElement("div");
+ container.id = "dribbblish-config";
+ container.innerHTML = /* html */ `
+
+
+
Dribbblish Settings
+
+
+ `;
- DribbblishShared.configData[key] = {
- menuItem,
- update
- };
+ document.body.appendChild(container);
+ document.querySelector(".dribbblish-config-close").addEventListener("click", () => DribbblishShared.config.close());
+ document.querySelector(".dribbblish-config-backdrop").addEventListener("click", () => DribbblishShared.config.close());
+ }
- DribbblishShared.config.update(key);
- },
- registerSelect: (name, key, choices, defaultChoice, update) => {
- const menuItem = new Spicetify.Menu.SubMenu(name, []);
- const menuItems = choices.map((choice, i) => {
- const subItem = new Spicetify.Menu.Item(choice, i == defaultChoice, (self) => {
- self.setState(!self.isEnabled);
- DribbblishShared.config.set(key, i);
- });
- menuItem.addItem(subItem);
- return subItem;
- });
- DribbblishShared.configButton.addItem(menuItem);
- menuItem.register();
+ open() {
+ document.getElementById("dribbblish-config").setAttribute("active", "");
+ }
- if (localStorage.getItem(`dribbblish:config:${key}`) == null) localStorage.setItem(`dribbblish:config:${key}`, defaultChoice);
+ close() {
+ document.getElementById("dribbblish-config").removeAttribute("active");
+ }
- DribbblishShared.configData[key] = {
- subItems: menuItems,
- menuItem,
- update
- };
-
- DribbblishShared.config.update(key);
- },
- get: (key) => {
- const val = localStorage.getItem(`dribbblish:config:${key}`);
- if (val == "true" || val == "false") return val == "true";
- if (!isNaN(val) && !isNaN(parseInt(val))) return parseInt(val);
- },
- set: (key, val) => {
- if (DribbblishShared.configData[key].hasOwnProperty("subItems")) {
- DribbblishShared.configData[key].subItems.forEach((item, i) => {
- item.setState(val == i);
- });
- } else {
- DribbblishShared.configData[key].menuItem.setState(val);
- }
- localStorage.setItem(`dribbblish:config:${key}`, val);
- DribbblishShared.config.update(key);
- },
- toggle: (key) => {
- DribbblishShared.config.set(key, !DribbblishShared.config.get(key));
-
- if (DribbblishShared.configData[key].hasOwnProperty("subItems")) {
- // Can't toggle lists
- } else {
- DribbblishShared.configData[key].menuItem.setState(DribbblishShared.config.get(key));
- }
- },
- update: (key) => {
- const val = DribbblishShared.config.get(key);
- if (DribbblishShared.configData[key].hasOwnProperty("subItems")) {
- DribbblishShared.configData[key].subItems.forEach((item, i) => {
- item.setState(val == i);
- });
- } else {
- DribbblishShared.configData[key].menuItem.setState(val);
- }
- DribbblishShared.configData[key].update(val);
+ /** @private */
+ addInputHTML({ type, key, name, description, input, insertOnTop }) {
+ const elem = document.createElement("div");
+ elem.classList.add("dribbblish-config-item");
+ elem.setAttribute("key", `dribbblish:config:${key}`);
+ elem.setAttribute("type", type);
+ elem.innerHTML = /* html */ `
+ ${name}
+
+
+ `;
+ if (insertOnTop && document.querySelector(".dribbblish-config-item")) {
+ console.log("before");
+ document.querySelector(".dribbblish-config-container").insertBefore(elem, document.querySelector(".dribbblish-config-item:first-of-type"));
+ } else {
+ document.querySelector(".dribbblish-config-container").appendChild(elem);
}
- },
- configData: {}
-};
-DribbblishShared.configButton.register();
+ }
-// Initialize Config
-DribbblishShared.config.register("Right expanded cover", "rightBigCover", true, (value) => {
- if (value) {
- document.documentElement.classList.add("right-expanded-cover");
- } else {
- document.documentElement.classList.remove("right-expanded-cover");
+ register({ type, options, key, name, description, defaultValue, insertOnTop, onChange }) {
+ if (!key) key = cyrb53Hash(name);
+ var fireChange = true;
+
+ if (type == "checkbox") {
+ const input = /* html */ `
+
+
+
+
+ `;
+ this.addInputHTML({ type, key, name, description, input, insertOnTop });
+
+ document.getElementById(`dribbblish-config-input-${key}`).addEventListener("change", (e) => {
+ this.set(key, e.target.checked);
+ onChange(this.get(key));
+ });
+ } else if (type == "select") {
+ const input = /* html */ `
+
+
+
+ `;
+ this.addInputHTML({ type, key, name, description, input, insertOnTop });
+
+ document.getElementById(`dribbblish-config-input-${key}`).addEventListener("change", (e) => {
+ this.set(key, e.target.value);
+ onChange(this.get(key));
+ });
+ } else if (type == "button") {
+ const input = /* html */ `
+
+
+
+ `;
+ this.addInputHTML({ type, key, name, description, input, insertOnTop });
+
+ document.getElementById(`dribbblish-config-input-${key}`).addEventListener("click", (e) => {
+ onChange(true);
+ });
+ fireChange = false;
+ } else {
+ throw new Error(`Config Type "${type}" invalid`);
+ }
+
+ if (fireChange) onChange(this.get(key, defaultValue));
+ }
+
+ get(key, defaultValue) {
+ const val = localStorage.getItem(`dribbblish:config:${key}`);
+ if (val == null) return defaultValue;
+
+ if (val == "true" || val == "false") return val == "true"; // Boolean
+ if (!isNaN(val) && !isNaN(parseInt(val))) return parseInt(val); // Number
+ return val; // String
+ }
+
+ set(key, val) {
+ localStorage.setItem(`dribbblish:config:${key}`, val);
+ }
+}
+
+class _DribbblishShared {
+ constructor() {
+ this.config = new ConfigMenu();
+ }
+}
+const DribbblishShared = new _DribbblishShared();
+
+DribbblishShared.config.register({
+ type: "checkbox",
+ key: "rightBigCover",
+ name: "Right expanded cover",
+ description: "Have the expanded cover Image on the right instead of onn the left.",
+ defaultValue: true,
+ onChange: (val) => {
+ if (val) {
+ document.documentElement.classList.add("right-expanded-cover");
+ } else {
+ document.documentElement.classList.remove("right-expanded-cover");
+ }
}
});
-DribbblishShared.config.register("Round Sidebar Icons", "roundSidebarIcons", false, (value) => {
- if (value) {
- document.documentElement.style.setProperty("--sidebar-icons-border-radius", "50%");
- } else {
- document.documentElement.style.setProperty("--sidebar-icons-border-radius", "var(--image-radius)");
+DribbblishShared.config.register({
+ type: "checkbox",
+ key: "roundSidebarIcons",
+ name: "Round Sidebar Icons",
+ description: "If the Sidebar Iconns should be round instead of square",
+ defaultValue: false,
+ onChange: (val) => {
+ if (val) {
+ document.documentElement.style.setProperty("--sidebar-icons-border-radius", "50%");
+ } else {
+ document.documentElement.style.setProperty("--sidebar-icons-border-radius", "var(--image-radius)");
+ }
}
});
+DribbblishShared.config.open();
+
waitForElement(["#main"], () => {
- DribbblishShared.config.registerSelect("Windows Top Bar", "winTopBar", ["None", "None (With Top Padding)", "Solid", "Transparent"], 0, (value) => {
- switch (value) {
- case 0:
- document.getElementById("main").setAttribute("top-bar", "none");
- break;
- case 1:
- document.getElementById("main").setAttribute("top-bar", "none-padding");
- break;
- case 2:
- document.getElementById("main").setAttribute("top-bar", "solid");
- break;
- case 3:
- document.getElementById("main").setAttribute("top-bar", "transparent");
- break;
+ DribbblishShared.config.register({
+ type: "select",
+ options: ["None", "None (With Top Padding)", "Solid", "Transparent"],
+ key: "winTopBar",
+ name: "Windows Top Bar",
+ description: "Have differennt top Bars (Ore none at all)",
+ defaultValue: 0,
+ onChange: (val) => {
+ switch (val) {
+ case 0:
+ document.getElementById("main").setAttribute("top-bar", "none");
+ break;
+ case 1:
+ document.getElementById("main").setAttribute("top-bar", "none-padding");
+ break;
+ case 2:
+ document.getElementById("main").setAttribute("top-bar", "solid");
+ break;
+ case 3:
+ document.getElementById("main").setAttribute("top-bar", "transparent");
+ break;
+ }
}
});
});
function waitForElement(els, func, timeout = 100) {
- const queries = els.map(el => document.querySelector(el));
- if (queries.every(a => a)) {
+ const queries = els.map((el) => document.querySelector(el));
+ if (queries.every((a) => a)) {
func(queries);
} else if (timeout > 0) {
setTimeout(waitForElement, 300, els, func, --timeout);
}
}
+function cyrb53Hash(str, seed = 0) {
+ let h1 = 0xdeadbeef ^ seed,
+ h2 = 0x41c6ce57 ^ seed;
+ for (let i = 0, ch; i < str.length; i++) {
+ ch = str.charCodeAt(i);
+ h1 = Math.imul(h1 ^ ch, 2654435761);
+ h2 = Math.imul(h2 ^ ch, 1597334677);
+ }
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0);
+}
+
waitForElement([
`.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"]`,
`.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"] li`
diff --git a/user.css b/user.css
index 188704d..368c0ce 100644
--- a/user.css
+++ b/user.css
@@ -274,14 +274,14 @@ span.artist-artistVerifiedBadge-badge svg > path:last-of-type {
}
.cover-art,
+.main-userWidget-box,
.view-homeShortcutsGrid-shortcut,
:not(.view-homeShortcutsGrid-imageWrapper) > .main-image-image:not(.main-avatar-image) {
border-radius: var(--image-radius) !important;
}
-.main-userWidget-box,
-.main-avatar-userIcon,
.main-avatar-image,
+.main-avatar-userIcon,
.view-homeShortcutsGrid-shortcutLink {
border-radius: var(--sidebar-icons-border-radius) !important;
}
@@ -699,6 +699,73 @@ li.GlueDropTarget {
pointer-events: none;
}
+#dribbblish-config {
+ display: none;
+ z-index: 99999;
+ position: absolute;
+ inset: 0px;
+ align-items: center;
+ justify-content: center;
+ color: var(--spice-text);
+}
+
+#dribbblish-config[active] {
+ display: flex;
+}
+
+#dribbblish-config .dribbblish-config-container {
+ z-index: 1;
+ position: relative;
+ max-width: 80%;
+ background-color: rgba(var(--spice-rgb-main), 0.85);
+ backdrop-filter: blur(3px);
+ padding: 20px 15px;
+ border-radius: var(--main-corner-radius);
+ display: flex;
+ gap: 5px;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+#dribbblish-config .dribbblish-config-item {
+ position: relative;
+ width: 100%;
+ padding: 0px 50px;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ grid-template-rows: 1fr 1fr;
+ gap: 0px 10px;
+ grid-template-areas:
+ "header input"
+ "description input";
+}
+
+#dribbblish-config .dribbblish-config-item > .x-settings-title {
+ grid-area: header;
+}
+
+#dribbblish-config .dribbblish-config-item > .main-type-mesto {
+ grid-area: description;
+}
+
+#dribbblish-config .dribbblish-config-item > .x-settings-secondColumn {
+ grid-area: input;
+}
+
+#dribbblish-config .dribbblish-config-close {
+ position: absolute;
+ top: 15px;
+ right: 15px;
+}
+
+#dribbblish-config .dribbblish-config-backdrop {
+ position: absolute;
+ content: "";
+ inset: 0px;
+ backdrop-filter: blur(3px) brightness(60%);
+}
+
/** Rearrange player bar */
.main-nowPlayingBar-left {
order: 1;