Merge branch 'main' into patch-1

This commit is contained in:
Julien 2021-10-08 17:07:03 +02:00 committed by GitHub
commit 3766db4ab4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 1507 additions and 1192 deletions

View file

@ -23,7 +23,7 @@ Right click at folder and choose images for your playlist folder. Every image fo
### Left/Right expanded cover ### Left/Right expanded cover
In profile menu, toggle option "Right expanded cover" to change expaned current track cover image to left or right side, whereever you prefer. In profile menu, toggle option "Right expanded cover" to change expaned current track cover image to left or right side, whereever you prefer.
## Install ## Install / Update
Make sure you are using spicetify >= v2.6.0 and Spotify >= v1.1.67. Make sure you are using spicetify >= v2.6.0 and Spotify >= v1.1.67.
Run these commands: Run these commands:

View file

@ -1,4 +1,4 @@
let current = '2.3' let current = '2.4.3'
/* css is injected so this works with untouched user.css from Dribbblish */ /* css is injected so this works with untouched user.css from Dribbblish */
/* dark theme */ /* dark theme */
@ -71,6 +71,25 @@ document.styleSheets[0].insertRule(`
color: var(--spice-sidebar-text) !important; color: var(--spice-sidebar-text) !important;
}`) }`)
/* Config settings */
DribbblishShared.config.register({
type: "slider",
data: {
"min": 0,
"max": 10,
"step": 0.1,
"suffix": "s"
},
key: "fadeDuration",
name: "Color Fade Duration",
description: "Select the duration of the color fading transition",
defaultValue: 0.5,
onChange: (val) => {
document.documentElement.style.setProperty("--song-transition-speed", val+"s");
}
});
/* js */ /* js */
function getAlbumInfo(uri) { function getAlbumInfo(uri) {
return Spicetify.CosmosAsync.get(`hm://album/v1/album-app/album/${uri}/desktop`) return Spicetify.CosmosAsync.get(`hm://album/v1/album-app/album/${uri}/desktop`)
@ -170,7 +189,7 @@ function toggleDark(setDark) {
setRootColor('subtext', setDark ? "#EAEAEA" : "#3D3D3D") setRootColor('subtext', setDark ? "#EAEAEA" : "#3D3D3D")
setRootColor('notification', setDark ? "#303030" : "#DDDDDD") setRootColor('notification', setDark ? "#303030" : "#DDDDDD")
updateColors(textColor, sidebarColor) updateColors(textColor, sidebarColor, false)
} }
/* Init with current system light/dark mode */ /* Init with current system light/dark mode */
@ -178,7 +197,7 @@ let systemDark = parseInt(getComputedStyle(document.documentElement).getProperty
DribbblishShared.config.register({ DribbblishShared.config.register({
type: "select", type: "select",
options: ["System", "Dark", "Light"], data: ["System", "Dark", "Light"],
key: "theme", key: "theme",
name: "Theme", name: "Theme",
description: "Select Dark / Bright mode", description: "Select Dark / Bright mode",
@ -198,20 +217,69 @@ DribbblishShared.config.register({
} }
}); });
function updateColors(textColHex, sideColHex) { var currentColor;
let isLightBg = isLight(textColorBg) var currentSideColor;
if (isLightBg) textColHex = LightenDarkenColor(textColHex, -15) // vibrant color is always too bright for white bg mode var colorFadeInterval = false;
let darkColHex = LightenDarkenColor(textColHex, isLightBg ? 12 : -20) function updateColors(textColHex, sideColHex, animate=false) {
let darkerColHex = LightenDarkenColor(textColHex, isLightBg ? 30 : -40) let update = (textColHex, sideColHex) => {
let buttonBgColHex = setLightness(textColHex, isLightBg ? 0.90 : 0.14) currentColor = textColHex;
setRootColor('text', textColHex) currentSideColor = sideColHex;
setRootColor('button', darkerColHex)
setRootColor('button-active', darkColHex) let isLightBg = isLight(textColorBg)
setRootColor('selected-row', darkerColHex) if (isLightBg) textColHex = LightenDarkenColor(textColHex, -15) // vibrant color is always too bright for white bg mode
setRootColor('tab-active', buttonBgColHex)
setRootColor('button-disabled', buttonBgColHex) let darkColHex = LightenDarkenColor(textColHex, isLightBg ? 12 : -20)
setRootColor('sidebar', sideColHex) let darkerColHex = LightenDarkenColor(textColHex, isLightBg ? 30 : -40)
let buttonBgColHex = setLightness(textColHex, isLightBg ? 0.90 : 0.14)
setRootColor('text', textColHex)
setRootColor('button', darkerColHex)
setRootColor('button-active', darkColHex)
setRootColor('selected-row', darkerColHex)
setRootColor('tab-active', buttonBgColHex)
setRootColor('button-disabled', buttonBgColHex)
setRootColor('sidebar', sideColHex)
};
clearInterval(colorFadeInterval); // clear any interval that might be running
if(!animate) {
update(textColHex, sideColHex);
return;
}
let clamp = (num,min,max) => Math.min(Math.max(num, min), max);
let lerp = (a,b,u) => (1-u) * a + u * b;
let toMS = s => parseFloat(s) * (/\ds$/.test(s) ? 1000 : 1);
let interval = 10; // 10 ms between each call
var duration = toMS(getComputedStyle(document.documentElement).getPropertyValue("--song-transition-speed"));
if(duration < 1) duration = 1;
let startC1 = hexToRgb(currentColor);
let startC2 = hexToRgb(currentSideColor);
let endC1 = hexToRgb(textColHex);
let endC2 = hexToRgb(sideColHex);
var start;
colorFadeInterval = setInterval(function(){
if(!start) { start = performance.now() }
let elapsed = performance.now()-start;
let ratio = clamp(elapsed/duration, 0, 1)
let currentC1 = [];
let currentC2 = [];
for(var i = 0; i < 3; i++){
currentC1[i] = lerp(startC1[i], endC1[i], ratio);
currentC2[i] = lerp(startC2[i], endC2[i], ratio);
}
update(rgbToHex(currentC1), rgbToHex(currentC2));
if (elapsed>duration){ clearInterval(colorFadeInterval) }
}, interval);
} }
let nearArtistSpanText = "" let nearArtistSpanText = ""
@ -244,7 +312,7 @@ async function songchange() {
recent_date.setMonth(recent_date.getMonth() - 6) recent_date.setMonth(recent_date.getMonth() - 6)
album_date = album_date.toLocaleString('default', album_date>recent_date ? { year: 'numeric', month: 'short' } : { year: 'numeric' }) album_date = album_date.toLocaleString('default', album_date>recent_date ? { year: 'numeric', month: 'short' } : { year: 'numeric' })
album_link = "<a title=\""+Spicetify.Player.data.track.metadata.album_title+"\" href=\""+album_uri+"\" data-uri=\""+album_uri+"\" data-interaction-target=\"album-name\" class=\"tl-cell__content\">"+Spicetify.Player.data.track.metadata.album_title+"</a>" album_link = "<a title=\""+Spicetify.Player.data.track.metadata.album_title+"\" href=\""+album_uri+"\" data-uri=\""+album_uri+"\" data-interaction-target=\"album-name\" class=\"tl-cell__content\">"+Spicetify.Player.data.track.metadata.album_title+"</a>"
nearArtistSpanText = album_link + " • " + album_date nearArtistSpanText = album_link + " • " + album_date
} else if (Spicetify.Player.data.track.uri.includes('spotify:episode')) { } else if (Spicetify.Player.data.track.uri.includes('spotify:episode')) {
// podcast // podcast
@ -294,7 +362,7 @@ function pickCoverColor(img) {
sidebarColor = swatches[lightCols[col]].getHex() sidebarColor = swatches[lightCols[col]].getHex()
break break
} }
updateColors(textColor, sidebarColor) updateColors(textColor, sidebarColor, true)
} }
function hookCoverChange(pick) { function hookCoverChange(pick) {
@ -325,19 +393,13 @@ hookCoverChange(false);
}).then(data => { }).then(data => {
if (data.tag_name > current) { if (data.tag_name > current) {
upd = document.createElement("div") upd = document.createElement("div")
upd.innerText = `Theme UPD v${data.tag_name} avail.`
upd.classList.add("ellipsis-one-line", "main-type-finale") upd.classList.add("ellipsis-one-line", "main-type-finale")
document.querySelector(".main-userWidget-box").append(upd)
upd.append(`Theme UPD v${data.tag_name} avail.`)
upd.setAttribute("title", `Changes: ${data.name}`) upd.setAttribute("title", `Changes: ${data.name}`)
DribbblishShared.config.register({ upd.style.setProperty("color", "var(--spice-button-active)");
insertOnTop: true, document.querySelector(".main-userWidget-box").append(upd)
type: "button", document.querySelector(".main-userWidget-box").classList.add("update-avail")
name: "Update", new Spicetify.Menu.Item("Update Dribbblish", false, () => window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme/blob/main/README.md#install--update", "_blank")).register();
description: "Open the GitHub Page with Installation instructions / Commands.",
onChange: () => {
window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme#install", "_blank");
}
});
} }
}).catch(err => { }).catch(err => {
// Do something for an error here // Do something for an error here
@ -346,23 +408,26 @@ hookCoverChange(false);
})() })()
/* translucid background cover */ /* translucid background cover */
document.styleSheets[0].addRule('.Root__top-container::before', document.styleSheets[0].insertRule(`
` content: ''; .Root__top-container::before {
background-image: var(--image_url); content: '';
background-repeat: no-repeat; background-image: var(--image_url);
background-size: cover; background-repeat: no-repeat;
background-position: center center; background-size: cover;
position: fixed; background-position: center center;
display: block; position: fixed;
top: 0; display: block;
left: 0; top: 0;
right: 0; left: 0;
bottom: 0; right: 0;
filter: blur(15px); bottom: 0;
pointer-events: none; filter: blur(15px);
backface-visibility: hidden; pointer-events: none;
will-change: transform; backface-visibility: hidden;
opacity: calc(0.07 + 0.03 * var(--is_light, 0)); will-change: transform;
z-index: +3;`) opacity: calc(0.07 + 0.03 * var(--is_light, 0));
z-index: +3;
transition: background-image var(--song-transition-speed) linear;
}`)
document.documentElement.style.setProperty('--warning_message', ' '); document.documentElement.style.setProperty('--warning_message', ' ');

View file

@ -1,6 +1,20 @@
// Hide popover message // Hide popover message
// document.getElementById("popover-container").style.height = 0; // document.getElementById("popover-container").style.height = 0;
class ConfigMenu { class ConfigMenu {
/**
* @typedef {Object} DribbblishConfigOptions
* @property {"checkbox" | "select" | "button" | "slider" | "number" | "text"} type
* @property {String?} area
* @property {any?} data
* @property {String?} key
* @property {String?} name
* @property {String?} description
* @property {any?} defaultValue
* @property {Boolean?} insertOnTop
* @property {Function?} onAppended
* @property {Function?} onChange
*/
constructor() { constructor() {
this.config = {}; this.config = {};
this.configButton = new Spicetify.Menu.Item("Dribbblish config", false, () => DribbblishShared.config.open()); this.configButton = new Spicetify.Menu.Item("Dribbblish config", false, () => DribbblishShared.config.open());
@ -14,6 +28,7 @@ class ConfigMenu {
<svg width="18" height="18" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M31.098 29.794L16.955 15.65 31.097 1.51 29.683.093 15.54 14.237 1.4.094-.016 1.508 14.126 15.65-.016 29.795l1.414 1.414L15.54 17.065l14.144 14.143" fill="currentColor" fill-rule="evenodd"></path></svg> <svg width="18" height="18" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M31.098 29.794L16.955 15.65 31.097 1.51 29.683.093 15.54 14.237 1.4.094-.016 1.508 14.126 15.65-.016 29.795l1.414 1.414L15.54 17.065l14.144 14.143" fill="currentColor" fill-rule="evenodd"></path></svg>
</button> </button>
<h1>Dribbblish Settings</h1> <h1>Dribbblish Settings</h1>
<div class="dribbblish-config-items"></div>
</div> </div>
<div class="dribbblish-config-backdrop"></div> <div class="dribbblish-config-backdrop"></div>
`; `;
@ -31,79 +46,164 @@ class ConfigMenu {
document.getElementById("dribbblish-config").removeAttribute("active"); document.getElementById("dribbblish-config").removeAttribute("active");
} }
/** @private */ /**
addInputHTML({ type, key, name, description, input, insertOnTop }) { * @private
* @param {DribbblishConfigOptions} options
*/
addInputHTML(options) {
let parent;
if (options.area != null) {
if (!document.querySelector(`.dribbblish-config-area[name="${options.area}"]`)) {
const areaElem = document.createElement("div");
areaElem.classList.add("dribbblish-config-area");
areaElem.setAttribute("name", options.area);
areaElem.innerHTML = `<h2>${options.area}</h2>`;
document.querySelector(".dribbblish-config-items").appendChild(areaElem);
}
parent = document.querySelector(`.dribbblish-config-area[name="${options.area}"]`);
} else {
parent = document.querySelector(".dribbblish-config-items");
}
const elem = document.createElement("div"); const elem = document.createElement("div");
elem.classList.add("dribbblish-config-item"); elem.classList.add("dribbblish-config-item");
elem.setAttribute("key", `dribbblish:config:${key}`); elem.setAttribute("key", `dribbblish:config:${options.key}`);
elem.setAttribute("type", type); elem.setAttribute("type", options.type);
elem.innerHTML = /* html */ ` elem.innerHTML = /* html */ `
<h2 class="x-settings-title main-type-cello" as="h2">${name}</h2> <h2 class="x-settings-title main-type-cello${!options.description ? " no-desc" : ""}" as="h2">${options.name}</h2>
<label class="main-type-mesto" as="label" for="dribbblish-config-input-${key}" style="color: var(--spice-subtext);">${description}</label> <label class="main-type-mesto" as="label" for="dribbblish-config-input-${options.key}" style="color: var(--spice-subtext);">${options.description}</label>
<label class="x-toggle-wrapper x-settings-secondColumn"> <label class="x-toggle-wrapper x-settings-secondColumn">
${input} ${options.input}
</label> </label>
`; `;
if (insertOnTop && document.querySelector(".dribbblish-config-item")) {
console.log("before"); if (options.insertOnTop && parent.children.length > 0) {
document.querySelector(".dribbblish-config-container").insertBefore(elem, document.querySelector(".dribbblish-config-item:first-of-type")); parent.insertBefore(elem, parent.children[0]);
} else { } else {
document.querySelector(".dribbblish-config-container").appendChild(elem); parent.appendChild(elem);
} }
} }
register({ type, options, key, name, description, defaultValue, insertOnTop, onChange }) { /**
if (!key) key = cyrb53Hash(name); * @param {DribbblishConfigOptions} options
*/
register(options) {
options = {
...{
area: "Main Settings",
data: {},
key: cyrb53Hash(options.name ?? ""),
name: "",
description: "",
insertOnTop: false,
onAppended: () => {},
onChange: () => {}
},
...options
};
var fireChange = true; var fireChange = true;
if (type == "checkbox") { if (options.type == "checkbox") {
const input = /* html */ ` const input = /* html */ `
<input id="dribbblish-config-input-${key}" class="x-toggle-input" type="checkbox"${this.get(key, defaultValue) ? " checked" : ""}> <input id="dribbblish-config-input-${options.key}" class="x-toggle-input" type="checkbox"${this.get(options.key, options.defaultValue) ? " checked" : ""}>
<span class="x-toggle-indicatorWrapper"> <span class="x-toggle-indicatorWrapper">
<span class="x-toggle-indicator"></span> <span class="x-toggle-indicator"></span>
</span> </span>
`; `;
this.addInputHTML({ type, key, name, description, input, insertOnTop }); this.addInputHTML({ ...options, input });
document.getElementById(`dribbblish-config-input-${key}`).addEventListener("change", (e) => { document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("change", (e) => {
this.set(key, e.target.checked); this.set(options.key, e.target.checked);
onChange(this.get(key)); options.onChange(this.get(options.key));
}); });
} else if (type == "select") { } else if (options.type == "select") {
const input = /* html */ ` const input = /* html */ `
<span class="x-settings-secondColumn"> <select class="main-dropDown-dropDown" id="dribbblish-config-input-${options.key}">
<select class="main-dropDown-dropDown" id="dribbblish-config-input-${key}"> ${options.data.map((option, i) => `<option value="${i}"${this.get(options.key, options.defaultValue) == i ? " selected" : ""}>${option}</option>`).join("")}
${options.map((option, i) => `<option value="${i}"${this.get(key, defaultValue) == i ? " selected" : ""}>${option}</option>`).join("")} </select>
</select>
</span>
`; `;
this.addInputHTML({ type, key, name, description, input, insertOnTop }); this.addInputHTML({ ...options, input });
document.getElementById(`dribbblish-config-input-${key}`).addEventListener("change", (e) => { document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("change", (e) => {
this.set(key, e.target.value); this.set(options.key, e.target.value);
onChange(this.get(key)); options.onChange(this.get(options.key));
}); });
} else if (type == "button") { } else if (options.type == "button") {
const input = /* html */ ` const input = /* html */ `
<span class="x-settings-secondColumn"> <button class="main-buttons-button main-button-primary" type="button" id="dribbblish-config-input-${options.key}">
<button class="main-buttons-button main-button-primary" type="button" id="dribbblish-config-input-${key}"> <div class="x-settings-buttonContainer">
<div class="x-settings-buttonContainer"> <span>${options.name}</span>
<span>${name}</span> </div>
</div> </button>
</button>
</span>
`; `;
this.addInputHTML({ type, key, name, description, input, insertOnTop }); this.addInputHTML({ ...options, input });
document.getElementById(`dribbblish-config-input-${key}`).addEventListener("click", (e) => { document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("click", (e) => {
onChange(true); options.onChange(true);
}); });
fireChange = false; fireChange = false;
} else if (options.type == "number") {
if (options.defaultValue == null) options.defaultValue = 0;
const input = /* html */ `
<input type="number" id="dribbblish-config-input-${options.key}" value="${this.get(options.key, options.defaultValue)}">
`;
this.addInputHTML({ ...options, input });
// Prevent inputting +, - and e. Why is it even possible in the first place?
document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("keypress", (e) => {
if (["+", "-", "e"].includes(e.key)) e.preventDefault();
});
document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {
if (options.data.min != null && e.target.value < options.data.min) e.target.value = options.data.min;
if (options.data.max != null && e.target.value > options.data.max) e.target.value = options.data.max;
this.set(options.key, e.target.value);
options.onChange(this.get(options.key));
});
} else if (options.type == "text") {
if (options.defaultValue == null) options.defaultValue = "";
const input = /* html */ `
<input type="text" id="dribbblish-config-input-${options.key}" value="${this.get(options.key, options.defaultValue)}">
`;
this.addInputHTML({ ...options, input });
document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {
// TODO: maybe add an validation function via `data.validate`
this.set(options.key, e.target.value);
options.onChange(this.get(options.key));
});
} else if (options.type == "slider") {
if (options.defaultValue == null) options.defaultValue = 0;
const input = /* html */ `
<input
type="range"
id="dribbblish-config-input-${options.key}"
name="${options.name}"
min="${options.data?.min ?? "0"}"
max="${options.data?.max ?? "100"}"
step="${options.data?.step ?? "1"}"
value="${this.get(options.key, options.defaultValue)}"
tooltip="${this.get(options.key, options.defaultValue)}${options.data?.suffix ?? ""}"
>
`;
this.addInputHTML({ ...options, input });
document.getElementById(`dribbblish-config-input-${options.key}`).addEventListener("input", (e) => {
document.getElementById(`dribbblish-config-input-${options.key}`).setAttribute("tooltip", `${e.target.value}${options.data?.suffix ?? ""}`);
document.getElementById(`dribbblish-config-input-${options.key}`).setAttribute("value", e.target.value);
this.set(options.key, e.target.value);
options.onChange(e.target.value);
});
} else { } else {
throw new Error(`Config Type "${type}" invalid`); throw new Error(`Config Type "${options.type}" invalid`);
} }
if (fireChange) onChange(this.get(key, defaultValue)); options.onAppended();
if (fireChange) options.onChange(this.get(options.key, options.defaultValue));
} }
get(key, defaultValue) { get(key, defaultValue) {
@ -111,7 +211,8 @@ class ConfigMenu {
if (val == null) return defaultValue; if (val == null) return defaultValue;
if (val == "true" || val == "false") return val == "true"; // Boolean if (val == "true" || val == "false") return val == "true"; // Boolean
if (!isNaN(val) && !isNaN(parseInt(val))) return parseInt(val); // Number if (!isNaN(val) && /\d+\.\d+/.test(val) && !isNaN(parseFloat(val))) return parseFloat(val); // Float
if (!isNaN(val) && /\d+/.test(val) && !isNaN(parseInt(val))) return parseInt(val); // Int
return val; // String return val; // String
} }
@ -131,7 +232,7 @@ DribbblishShared.config.register({
type: "checkbox", type: "checkbox",
key: "rightBigCover", key: "rightBigCover",
name: "Right expanded cover", name: "Right expanded cover",
description: "Have the expanded cover Image on the right instead of onn the left.", description: "Have the expanded cover Image on the right instead of on the left",
defaultValue: true, defaultValue: true,
onChange: (val) => { onChange: (val) => {
if (val) { if (val) {
@ -146,7 +247,7 @@ DribbblishShared.config.register({
type: "checkbox", type: "checkbox",
key: "roundSidebarIcons", key: "roundSidebarIcons",
name: "Round Sidebar Icons", name: "Round Sidebar Icons",
description: "If the Sidebar Iconns should be round instead of square", description: "If the Sidebar Icons should be round instead of square",
defaultValue: false, defaultValue: false,
onChange: (val) => { onChange: (val) => {
if (val) { if (val) {
@ -160,10 +261,10 @@ DribbblishShared.config.register({
waitForElement(["#main"], () => { waitForElement(["#main"], () => {
DribbblishShared.config.register({ DribbblishShared.config.register({
type: "select", type: "select",
options: ["None", "None (With Top Padding)", "Solid", "Transparent"], data: ["None", "None (With Top Padding)", "Solid", "Transparent"],
key: "winTopBar", key: "winTopBar",
name: "Windows Top Bar", name: "Windows Top Bar",
description: "Have differennt top Bars (Ore none at all)", description: "Have different top Bars (or none at all)",
defaultValue: 0, defaultValue: 0,
onChange: (val) => { onChange: (val) => {
switch (val) { switch (val) {
@ -206,10 +307,7 @@ function cyrb53Hash(str, seed = 0) {
return 4294967296 * (2097151 & h2) + (h1 >>> 0); return 4294967296 * (2097151 & h2) + (h1 >>> 0);
} }
waitForElement([ waitForElement([`.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"]`, `.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"] li`], ([root, firstItem]) => {
`.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"]`,
`.main-rootlist-rootlistPlaylistsScrollNode ul[tabindex="0"] li`
], ([root, firstItem]) => {
const listElem = firstItem.parentElement; const listElem = firstItem.parentElement;
root.classList.add("dribs-playlist-list"); root.classList.add("dribs-playlist-list");
@ -219,7 +317,7 @@ waitForElement([
let link = item.querySelector("a"); let link = item.querySelector("a");
if (!link) continue; if (!link) continue;
let [_, app, uid ] = link.pathname.split("/"); let [_, app, uid] = link.pathname.split("/");
let uri; let uri;
if (app === "playlist") { if (app === "playlist") {
uri = Spicetify.URI.playlistV2URI(uid); uri = Spicetify.URI.playlistV2URI(uid);
@ -231,14 +329,11 @@ waitForElement([
img.classList.add("playlist-picture"); img.classList.add("playlist-picture");
link.prepend(img); link.prepend(img);
} }
img.src = base64 || "/images/tracklist-row-song-fallback.svg"; img.src = base64 || "/images/tracklist-row-song-fallback.svg";
continue; continue;
} }
Spicetify.CosmosAsync.get( Spicetify.CosmosAsync.get(`sp://core-playlist/v1/playlist/${uri.toURI()}/metadata`, { policy: { picture: true } }).then((res) => {
`sp://core-playlist/v1/playlist/${uri.toURI()}/metadata`,
{ policy: { picture: true } }
).then(res => {
const meta = res.metadata; const meta = res.metadata;
let img = link.querySelector("img"); let img = link.querySelector("img");
if (!img) { if (!img) {
@ -254,13 +349,12 @@ waitForElement([
DribbblishShared.loadPlaylistImage = loadPlaylistImage; DribbblishShared.loadPlaylistImage = loadPlaylistImage;
loadPlaylistImage(); loadPlaylistImage();
new MutationObserver(loadPlaylistImage) new MutationObserver(loadPlaylistImage).observe(listElem, { childList: true });
.observe(listElem, {childList: true});
}); });
waitForElement([".main-rootlist-rootlist", ".main-rootlist-wrapper > :nth-child(2) > :first-child"], ([rootlist]) => { waitForElement([".main-rootlist-rootlist", ".main-rootlist-wrapper > :nth-child(2) > :first-child", "#spicetify-show-list"], ([rootlist]) => {
function checkSidebarPlaylistScroll() { function checkSidebarPlaylistScroll() {
const topDist = rootlist.getBoundingClientRect().top - document.querySelector(".main-rootlist-wrapper > :nth-child(2) > :first-child").getBoundingClientRect().top; const topDist = rootlist.getBoundingClientRect().top - document.querySelector("#spicetify-show-list:not(:empty), .main-rootlist-wrapper > :nth-child(2) > :first-child").getBoundingClientRect().top;
const bottomDist = document.querySelector(".main-rootlist-wrapper > :nth-child(2) > :last-child").getBoundingClientRect().bottom - rootlist.getBoundingClientRect().bottom; const bottomDist = document.querySelector(".main-rootlist-wrapper > :nth-child(2) > :last-child").getBoundingClientRect().bottom - rootlist.getBoundingClientRect().bottom;
rootlist.classList.remove("no-top-shadow", "no-bottom-shadow"); rootlist.classList.remove("no-top-shadow", "no-bottom-shadow");

2322
user.css

File diff suppressed because it is too large Load diff