🔧 chore(spicetify): update custom app

This commit is contained in:
Sergio Laín 2024-09-14 13:59:02 +02:00
parent 78b5bc7880
commit 9a9148c5f7
No known key found for this signature in database
GPG key ID: 8429B2EE312F8150
6 changed files with 1172 additions and 848 deletions

View file

@ -8,6 +8,7 @@ var library = (() => {
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
@ -21,6 +22,10 @@ var library = (() => {
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// src/extensions/collections_wrapper.ts
var collections_wrapper_exports = {};
@ -77,7 +82,7 @@ var library = (() => {
var v4_default = v4;
// src/extensions/collections_wrapper.ts
var CollectionWrapper = class extends EventTarget {
var _CollectionsWrapper = class extends EventTarget {
_collections;
constructor() {
super();
@ -90,115 +95,189 @@ var library = (() => {
getCollection(uri) {
return this._collections.find((collection) => collection.uri === uri);
}
async requestAlbums({ sortOrder, textFilter }) {
async getCollectionContents(uri) {
const collection = this.getCollection(uri);
if (!collection)
throw new Error("Collection not found");
const items = this._collections.filter((collection2) => collection2.parentCollection === uri);
const albums = await Spicetify.Platform.LibraryAPI.getContents({
filters: ["0"],
sortOrder,
textFilter,
offset: 0,
limit: 9999
});
return albums;
items.push(...albums.items.filter((album) => collection.items.includes(album.uri)));
return items;
}
async getCollectionItems(props) {
const { collectionUri, textFilter, sortOrder, rootlist, limit = 9999, offset = 0 } = props;
let collectionItems = this._collections;
let albumItems = [];
let unfilteredLength = this._collections.length;
let openedCollection = "";
if (collectionUri) {
const collection = this.getCollection(collectionUri);
const res = await this.requestAlbums({ sortOrder, textFilter });
const collectionSet = new Set(collection.items);
const commonElements = res.items.filter((item) => collectionSet.has(item.uri));
const collections = this._collections.filter((collection2) => collection2.parentCollection === collectionUri);
openedCollection = collection.name;
collectionItems = collections;
albumItems = commonElements;
unfilteredLength = collection.totalLength;
}
async getContents(props) {
const { collectionUri, offset, limit, textFilter } = props;
let items = collectionUri ? await this.getCollectionContents(collectionUri) : this._collections;
const openedCollectionName = collectionUri ? this.getCollection(collectionUri)?.name : void 0;
if (textFilter) {
let regex = new RegExp("\\b" + textFilter, "i");
collectionItems = collectionItems.filter((item) => {
return regex.test(item.name);
});
const regex = new RegExp(`\\b${textFilter}`, "i");
items = items.filter((collection) => regex.test(collection.name));
}
if (rootlist && !collectionUri) {
const res = await this.requestAlbums({ sortOrder, textFilter });
albumItems = res.items;
if (!textFilter) {
const collectionSet = new Set(this._collections.map((collection) => collection.items).flat());
const uncommonElements = res.items.filter((item) => !collectionSet.has(item.uri));
collectionItems = this._collections.filter((collection) => !collection.parentCollection);
albumItems = uncommonElements;
unfilteredLength = this._collections.length + uncommonElements.length;
items = items.slice(offset, offset + limit);
return { items, totalLength: this._collections.length, offset, openedCollectionName };
}
async cleanCollections() {
for (const collection of this._collections) {
const boolArray = await Spicetify.Platform.LibraryAPI.contains(...collection.items);
if (boolArray.includes(false)) {
collection.items = collection.items.filter((_, i) => boolArray[i]);
this.saveCollections();
Spicetify.showNotification("Album removed from collection");
this.syncCollection(collection.uri);
}
}
if (offset > 0)
collectionItems = [];
return {
openedCollection,
items: [...collectionItems, ...albumItems.slice(offset, offset + limit)],
totalLength: albumItems.length + collectionItems.length,
unfilteredLength
};
}
async syncCollection(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
const { PlaylistAPI } = Spicetify.Platform;
if (!collection.syncedPlaylistUri)
return;
const playlist = await PlaylistAPI.getPlaylist(collection.syncedPlaylistUri);
const playlistTracks = playlist.contents.items.filter((t) => t.type === "track").map((t) => t.uri);
const collectionTracks = await this.getTracklist(uri);
const wanted = collectionTracks.filter((track) => !playlistTracks.includes(track));
const unwanted = playlistTracks.filter((track) => !collectionTracks.includes(track)).map((uri2) => ({ uri: uri2, uid: [] }));
if (wanted.length)
await PlaylistAPI.add(collection.syncedPlaylistUri, wanted, { before: "end" });
if (unwanted.length)
await PlaylistAPI.remove(collection.syncedPlaylistUri, unwanted);
}
unsyncCollection(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.syncedPlaylistUri = void 0;
this.saveCollections();
}
async getTracklist(collectionUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return [];
return Promise.all(
collection.items.map(async (uri) => {
const album = await Spicetify.Platform.LibraryAPI.getAlbum(uri);
return album.items.map((t) => t.uri);
})
).then((tracks) => tracks.flat());
}
async convertToPlaylist(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
const { Platform, showNotification } = Spicetify;
const { RootlistAPI, PlaylistAPI } = Platform;
if (collection.syncedPlaylistUri) {
showNotification("Synced Playlist already exists", true);
return;
}
try {
const playlistUri = await RootlistAPI.createPlaylist(collection.name, { before: "start" });
const items = await this.getTracklist(uri);
await PlaylistAPI.add(playlistUri, items, { before: "start" });
collection.syncedPlaylistUri = playlistUri;
} catch (error) {
console.error(error);
showNotification("Failed to create playlist", true);
}
}
async createCollectionFromDiscog(artistUri) {
const [raw, info] = await Promise.all([
Spicetify.GraphQL.Request(Spicetify.GraphQL.Definitions.queryArtistDiscographyAlbums, {
uri: artistUri,
offset: 0,
limit: 50
}),
Spicetify.GraphQL.Request(Spicetify.GraphQL.Definitions.queryArtistOverview, {
uri: artistUri,
locale: Spicetify.Locale.getLocale(),
includePrerelease: false
})
]);
const items = raw?.data?.artistUnion.discography.albums?.items;
const name = info?.data?.artistUnion.profile.name;
const image = info?.data?.artistUnion.visuals.avatarImage?.sources?.[0]?.url;
if (!name || !items?.length) {
Spicetify.showNotification("Artist not found or has no albums");
return;
}
const collectionUri = this.createCollection(`${name} Albums`);
if (image)
this.setCollectionImage(collectionUri, image);
for (const album of items) {
this.addAlbumToCollection(collectionUri, album.releases.items[0].uri);
}
}
createCollection(name, parentCollection = "") {
const uri = v4_default();
const collection = {
const id = v4_default();
this._collections.push({
type: "collection",
uri,
uri: id,
name,
items: [],
totalLength: 0,
imgUrl: "",
addedAt: new Date(),
lastPlayedAt: new Date(),
parentCollection
};
this._collections.push(collection);
});
this.saveCollections();
Spicetify.showNotification("Collection created");
return id;
}
deleteCollection(uri) {
this._collections = this._collections.filter((collection) => collection.uri !== uri);
this.saveCollections();
Spicetify.showNotification("Collection deleted");
}
deleteCollectionAndAlbums(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
for (const album of collection.items) {
Spicetify.Platform.LibraryAPI.remove({ uris: [album] });
}
this.deleteCollection(uri);
}
async addAlbumToCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
await Spicetify.Platform.LibraryAPI.add({ uris: [albumUri] });
collection.items.push(albumUri);
collection.totalLength++;
this.saveCollections();
Spicetify.showNotification("Album added to collection");
this.syncCollection(collectionUri);
}
removeAlbumFromCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
collection.items = collection.items.filter((item) => item !== albumUri);
collection.totalLength--;
this.saveCollections();
Spicetify.showNotification("Album removed from collection");
this.syncCollection(collectionUri);
}
getCollectionsWithAlbum(albumUri) {
return this._collections.filter((collection) => {
return collection.items.some((item) => item === albumUri);
});
}
renameCollection(uri, newName) {
renameCollection(uri, name) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.name = newName;
collection.name = name;
this.saveCollections();
Spicetify.showNotification("Collection renamed");
}
setCollectionImage(uri, imgUrl) {
setCollectionImage(uri, url) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = imgUrl;
collection.image = url;
this.saveCollections();
Spicetify.showNotification("Collection image set");
}
@ -206,12 +285,15 @@ var library = (() => {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = "";
collection.image = void 0;
this.saveCollections();
Spicetify.showNotification("Collection image removed");
}
};
var collections_wrapper_default = CollectionWrapper;
var CollectionsWrapper = _CollectionsWrapper;
__publicField(CollectionsWrapper, "INSTANCE", new _CollectionsWrapper());
window.CollectionsWrapper = CollectionsWrapper.INSTANCE;
var collections_wrapper_default = CollectionsWrapper;
return __toCommonJS(collections_wrapper_exports);
})();

View file

@ -285,7 +285,9 @@ var library = (() => {
});
};
var ToggleFiltersButton = () => {
const [direction, setDirection] = import_react3.default.useState(document.body.classList.contains("show-ylx-filters") ? "up" : "down");
const [direction, setDirection] = import_react3.default.useState(
document.body.classList.contains("show-ylx-filters") ? "up" : "down"
);
const { ButtonTertiary } = Spicetify.ReactComponent;
const toggleDirection = () => {
if (direction === "down") {
@ -332,31 +334,207 @@ var library = (() => {
};
var collapse_button_default = CollapseButton;
// src/components/expand_button.tsx
// src/components/album_menu_item.tsx
var import_react8 = __toESM(require_react());
// src/components/leading_icon.tsx
var import_react5 = __toESM(require_react());
var expandLibrary = () => {
Spicetify.Platform.LocalStorageAPI.setItem("ylx-sidebar-state", 0);
};
var ExpandIcon = () => {
const { IconComponent } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react5.default.createElement(IconComponent, {
var LeadingIcon = ({ path }) => {
return /* @__PURE__ */ import_react5.default.createElement(Spicetify.ReactComponent.IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" ><path d="M7.19 1A.749.749 0 0 1 8.47.47L16 7.99l-7.53 7.521a.75.75 0 0 1-1.234-.815.75.75 0 0 1 .174-.243l5.72-5.714H.75a.75.75 0 1 1 0-1.498h12.38L7.41 1.529a.749.749 0 0 1-.22-.53z"></path></svg>'
__html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">${path}</svg>`
},
iconSize: 16
});
};
var ExpandButton = () => {
const { ButtonTertiary } = Spicetify.ReactComponent;
return /* @__PURE__ */ import_react5.default.createElement(ButtonTertiary, {
buttonSize: "sm",
"aria-label": "Show Filters",
iconOnly: ExpandIcon,
onClick: expandLibrary
var leading_icon_default = LeadingIcon;
// src/components/text_input_dialog.tsx
var import_react6 = __toESM(require_react());
var TextInputDialog = (props) => {
const { def, placeholder, onSave } = props;
const [value, setValue] = import_react6.default.useState(def || "");
const onSubmit = (e) => {
e.preventDefault();
Spicetify.PopupModal.hide();
onSave(value);
};
return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, /* @__PURE__ */ import_react6.default.createElement("form", {
className: "text-input-form",
onSubmit
}, /* @__PURE__ */ import_react6.default.createElement("label", {
className: "text-input-wrapper"
}, /* @__PURE__ */ import_react6.default.createElement("input", {
className: "text-input",
type: "text",
value,
placeholder,
onChange: (e) => setValue(e.target.value)
})), /* @__PURE__ */ import_react6.default.createElement("button", {
type: "submit",
"data-encore-id": "buttonPrimary",
className: "Button-sc-qlcn5g-0 Button-small-buttonPrimary"
}, /* @__PURE__ */ import_react6.default.createElement("span", {
className: "ButtonInner-sc-14ud5tc-0 ButtonInner-small encore-bright-accent-set"
}, "Save"))));
};
var text_input_dialog_default = TextInputDialog;
// src/components/searchbar.tsx
var import_react7 = __toESM(require_react());
var SearchBar = (props) => {
const { setSearch, placeholder } = props;
const handleChange = (e) => {
setSearch(e.target.value);
};
return /* @__PURE__ */ import_react7.default.createElement("div", {
className: "x-filterBox-filterInputContainer x-filterBox-expandedOrHasFilter",
role: "search"
}, /* @__PURE__ */ import_react7.default.createElement("input", {
type: "text",
className: "x-filterBox-filterInput",
role: "searchbox",
maxLength: 80,
autoCorrect: "off",
autoCapitalize: "off",
spellCheck: "false",
placeholder: `Search ${placeholder}`,
"aria-hidden": "false",
onChange: handleChange
}), /* @__PURE__ */ import_react7.default.createElement("div", {
className: "x-filterBox-overlay"
}, /* @__PURE__ */ import_react7.default.createElement("span", {
className: "x-filterBox-searchIconContainer"
}, /* @__PURE__ */ import_react7.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-small x-filterBox-searchIcon",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react7.default.createElement("path", {
d: "M7 1.75a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5zM.25 7a6.75 6.75 0 1 1 12.096 4.12l3.184 3.185a.75.75 0 1 1-1.06 1.06L11.304 12.2A6.75 6.75 0 0 1 .25 7z"
})))), /* @__PURE__ */ import_react7.default.createElement("button", {
className: "x-filterBox-expandButton",
"aria-hidden": "false",
"aria-label": "Search Playlists",
type: "button"
}, /* @__PURE__ */ import_react7.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-small x-filterBox-searchIcon",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react7.default.createElement("path", {
d: "M7 1.75a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5zM.25 7a6.75 6.75 0 1 1 12.096 4.12l3.184 3.185a.75.75 0 1 1-1.06 1.06L11.304 12.2A6.75 6.75 0 0 1 .25 7z"
}))));
};
var searchbar_default = SearchBar;
// src/components/album_menu_item.tsx
var createCollection = () => {
const onSave = (value) => {
CollectionsWrapper.createCollection(value);
};
Spicetify.PopupModal.display({
title: "Create Collection",
content: /* @__PURE__ */ import_react8.default.createElement(text_input_dialog_default, {
def: "New Collection",
placeholder: "Collection Name",
onSave
})
});
};
var expand_button_default = ExpandButton;
var CollectionSearchMenu = () => {
const { MenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
const [textFilter, setTextFilter] = import_react8.default.useState("");
const [collections, setCollections] = import_react8.default.useState(null);
const context = import_react8.default.useContext(Spicetify.ContextMenuV2._context);
const uri = context?.props?.uri;
import_react8.default.useEffect(() => {
const fetchCollections = async () => {
setCollections(await CollectionsWrapper.getContents({ textFilter, limit: 20, offset: 0 }));
};
fetchCollections();
}, [textFilter]);
if (!collections)
return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, null);
const addToCollection = (collectionUri) => {
CollectionsWrapper.addAlbumToCollection(collectionUri, uri);
};
const activeCollections = CollectionsWrapper.getCollectionsWithAlbum(uri);
const hasCollections = activeCollections.length > 0;
const removeFromCollections = () => {
for (const collection of activeCollections) {
CollectionsWrapper.removeAlbumFromCollection(collection.uri, uri);
}
};
const allCollectionsLength = collections.totalLength;
const menuItems = collections.items.map((collection, index) => {
return /* @__PURE__ */ import_react8.default.createElement(MenuItem, {
key: collection.uri,
onClick: () => {
addToCollection(collection.uri);
},
divider: index === 0 ? "before" : void 0
}, collection.name);
});
const menuLength = allCollectionsLength + (hasCollections ? 1 : 0);
return /* @__PURE__ */ import_react8.default.createElement("div", {
className: "main-contextMenu-filterPlaylistSearchContainer",
style: { "--context-menu-submenu-length": `${menuLength}` }
}, /* @__PURE__ */ import_react8.default.createElement("li", {
role: "presentation",
className: "main-contextMenu-filterPlaylistSearch"
}, /* @__PURE__ */ import_react8.default.createElement("div", {
role: "menuitem"
}, /* @__PURE__ */ import_react8.default.createElement(searchbar_default, {
setSearch: setTextFilter,
placeholder: "collections"
}))), /* @__PURE__ */ import_react8.default.createElement(MenuItem, {
key: "new-collection",
leadingIcon: /* @__PURE__ */ import_react8.default.createElement(leading_icon_default, {
path: SVGIcons.plus2px
}),
onClick: createCollection
}, "Create collection"), hasCollections && /* @__PURE__ */ import_react8.default.createElement(MenuItem, {
key: "remove-collection",
leadingIcon: /* @__PURE__ */ import_react8.default.createElement(leading_icon_default, {
path: SVGIcons.minus
}),
onClick: removeFromCollections
}, "Remove from all"), menuItems);
};
var AlbumMenuItem = () => {
const { MenuSubMenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
return /* @__PURE__ */ import_react8.default.createElement(MenuSubMenuItem, {
displayText: "Add to collection",
divider: "after",
leadingIcon: /* @__PURE__ */ import_react8.default.createElement(leading_icon_default, {
path: SVGIcons.plus2px
})
}, /* @__PURE__ */ import_react8.default.createElement(CollectionSearchMenu, null));
};
var album_menu_item_default = AlbumMenuItem;
// src/components/artist_menu_item.tsx
var import_react9 = __toESM(require_react());
var ArtistMenuItem = () => {
const { MenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
const context = import_react9.default.useContext(Spicetify.ContextMenuV2._context);
const uri = context?.props?.uri;
return /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
divider: "after",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons.plus2px
}),
onClick: () => CollectionsWrapper.createCollectionFromDiscog(uri)
}, "Create Discog Collection");
};
var artist_menu_item_default = ArtistMenuItem;
// ../node_modules/uuid/dist/esm-browser/rng.js
var getRandomValues;
@ -407,7 +585,7 @@ var library = (() => {
var v4_default = v4;
// src/extensions/collections_wrapper.ts
var CollectionWrapper = class extends EventTarget {
var _CollectionsWrapper = class extends EventTarget {
_collections;
constructor() {
super();
@ -420,115 +598,189 @@ var library = (() => {
getCollection(uri) {
return this._collections.find((collection) => collection.uri === uri);
}
async requestAlbums({ sortOrder, textFilter }) {
async getCollectionContents(uri) {
const collection = this.getCollection(uri);
if (!collection)
throw new Error("Collection not found");
const items = this._collections.filter((collection2) => collection2.parentCollection === uri);
const albums = await Spicetify.Platform.LibraryAPI.getContents({
filters: ["0"],
sortOrder,
textFilter,
offset: 0,
limit: 9999
});
return albums;
items.push(...albums.items.filter((album) => collection.items.includes(album.uri)));
return items;
}
async getCollectionItems(props) {
const { collectionUri, textFilter, sortOrder, rootlist, limit = 9999, offset = 0 } = props;
let collectionItems = this._collections;
let albumItems = [];
let unfilteredLength = this._collections.length;
let openedCollection = "";
if (collectionUri) {
const collection = this.getCollection(collectionUri);
const res = await this.requestAlbums({ sortOrder, textFilter });
const collectionSet = new Set(collection.items);
const commonElements = res.items.filter((item) => collectionSet.has(item.uri));
const collections = this._collections.filter((collection2) => collection2.parentCollection === collectionUri);
openedCollection = collection.name;
collectionItems = collections;
albumItems = commonElements;
unfilteredLength = collection.totalLength;
}
async getContents(props) {
const { collectionUri, offset, limit, textFilter } = props;
let items = collectionUri ? await this.getCollectionContents(collectionUri) : this._collections;
const openedCollectionName = collectionUri ? this.getCollection(collectionUri)?.name : void 0;
if (textFilter) {
let regex = new RegExp("\\b" + textFilter, "i");
collectionItems = collectionItems.filter((item) => {
return regex.test(item.name);
});
const regex = new RegExp(`\\b${textFilter}`, "i");
items = items.filter((collection) => regex.test(collection.name));
}
if (rootlist && !collectionUri) {
const res = await this.requestAlbums({ sortOrder, textFilter });
albumItems = res.items;
if (!textFilter) {
const collectionSet = new Set(this._collections.map((collection) => collection.items).flat());
const uncommonElements = res.items.filter((item) => !collectionSet.has(item.uri));
collectionItems = this._collections.filter((collection) => !collection.parentCollection);
albumItems = uncommonElements;
unfilteredLength = this._collections.length + uncommonElements.length;
items = items.slice(offset, offset + limit);
return { items, totalLength: this._collections.length, offset, openedCollectionName };
}
async cleanCollections() {
for (const collection of this._collections) {
const boolArray = await Spicetify.Platform.LibraryAPI.contains(...collection.items);
if (boolArray.includes(false)) {
collection.items = collection.items.filter((_, i) => boolArray[i]);
this.saveCollections();
Spicetify.showNotification("Album removed from collection");
this.syncCollection(collection.uri);
}
}
if (offset > 0)
collectionItems = [];
return {
openedCollection,
items: [...collectionItems, ...albumItems.slice(offset, offset + limit)],
totalLength: albumItems.length + collectionItems.length,
unfilteredLength
};
}
async syncCollection(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
const { PlaylistAPI } = Spicetify.Platform;
if (!collection.syncedPlaylistUri)
return;
const playlist = await PlaylistAPI.getPlaylist(collection.syncedPlaylistUri);
const playlistTracks = playlist.contents.items.filter((t) => t.type === "track").map((t) => t.uri);
const collectionTracks = await this.getTracklist(uri);
const wanted = collectionTracks.filter((track) => !playlistTracks.includes(track));
const unwanted = playlistTracks.filter((track) => !collectionTracks.includes(track)).map((uri2) => ({ uri: uri2, uid: [] }));
if (wanted.length)
await PlaylistAPI.add(collection.syncedPlaylistUri, wanted, { before: "end" });
if (unwanted.length)
await PlaylistAPI.remove(collection.syncedPlaylistUri, unwanted);
}
unsyncCollection(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.syncedPlaylistUri = void 0;
this.saveCollections();
}
async getTracklist(collectionUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return [];
return Promise.all(
collection.items.map(async (uri) => {
const album = await Spicetify.Platform.LibraryAPI.getAlbum(uri);
return album.items.map((t) => t.uri);
})
).then((tracks) => tracks.flat());
}
async convertToPlaylist(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
const { Platform, showNotification } = Spicetify;
const { RootlistAPI, PlaylistAPI } = Platform;
if (collection.syncedPlaylistUri) {
showNotification("Synced Playlist already exists", true);
return;
}
try {
const playlistUri = await RootlistAPI.createPlaylist(collection.name, { before: "start" });
const items = await this.getTracklist(uri);
await PlaylistAPI.add(playlistUri, items, { before: "start" });
collection.syncedPlaylistUri = playlistUri;
} catch (error) {
console.error(error);
showNotification("Failed to create playlist", true);
}
}
async createCollectionFromDiscog(artistUri) {
const [raw, info] = await Promise.all([
Spicetify.GraphQL.Request(Spicetify.GraphQL.Definitions.queryArtistDiscographyAlbums, {
uri: artistUri,
offset: 0,
limit: 50
}),
Spicetify.GraphQL.Request(Spicetify.GraphQL.Definitions.queryArtistOverview, {
uri: artistUri,
locale: Spicetify.Locale.getLocale(),
includePrerelease: false
})
]);
const items = raw?.data?.artistUnion.discography.albums?.items;
const name = info?.data?.artistUnion.profile.name;
const image = info?.data?.artistUnion.visuals.avatarImage?.sources?.[0]?.url;
if (!name || !items?.length) {
Spicetify.showNotification("Artist not found or has no albums");
return;
}
const collectionUri = this.createCollection(`${name} Albums`);
if (image)
this.setCollectionImage(collectionUri, image);
for (const album of items) {
this.addAlbumToCollection(collectionUri, album.releases.items[0].uri);
}
}
createCollection(name, parentCollection = "") {
const uri = v4_default();
const collection = {
const id = v4_default();
this._collections.push({
type: "collection",
uri,
uri: id,
name,
items: [],
totalLength: 0,
imgUrl: "",
addedAt: new Date(),
lastPlayedAt: new Date(),
parentCollection
};
this._collections.push(collection);
});
this.saveCollections();
Spicetify.showNotification("Collection created");
return id;
}
deleteCollection(uri) {
this._collections = this._collections.filter((collection) => collection.uri !== uri);
this.saveCollections();
Spicetify.showNotification("Collection deleted");
}
deleteCollectionAndAlbums(uri) {
const collection = this.getCollection(uri);
if (!collection)
return;
for (const album of collection.items) {
Spicetify.Platform.LibraryAPI.remove({ uris: [album] });
}
this.deleteCollection(uri);
}
async addAlbumToCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
await Spicetify.Platform.LibraryAPI.add({ uris: [albumUri] });
collection.items.push(albumUri);
collection.totalLength++;
this.saveCollections();
Spicetify.showNotification("Album added to collection");
this.syncCollection(collectionUri);
}
removeAlbumFromCollection(collectionUri, albumUri) {
const collection = this.getCollection(collectionUri);
if (!collection)
return;
collection.items = collection.items.filter((item) => item !== albumUri);
collection.totalLength--;
this.saveCollections();
Spicetify.showNotification("Album removed from collection");
this.syncCollection(collectionUri);
}
getCollectionsWithAlbum(albumUri) {
return this._collections.filter((collection) => {
return collection.items.some((item) => item === albumUri);
});
}
renameCollection(uri, newName) {
renameCollection(uri, name) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.name = newName;
collection.name = name;
this.saveCollections();
Spicetify.showNotification("Collection renamed");
}
setCollectionImage(uri, imgUrl) {
setCollectionImage(uri, url) {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = imgUrl;
collection.image = url;
this.saveCollections();
Spicetify.showNotification("Collection image set");
}
@ -536,201 +788,17 @@ var library = (() => {
const collection = this.getCollection(uri);
if (!collection)
return;
collection.imgUrl = "";
collection.image = void 0;
this.saveCollections();
Spicetify.showNotification("Collection image removed");
}
};
var collections_wrapper_default = CollectionWrapper;
// src/components/album_menu_item.tsx
var import_react9 = __toESM(require_react());
// src/components/leading_icon.tsx
var import_react6 = __toESM(require_react());
var LeadingIcon = ({ path }) => {
return /* @__PURE__ */ import_react6.default.createElement(Spicetify.ReactComponent.IconComponent, {
semanticColor: "textSubdued",
dangerouslySetInnerHTML: {
__html: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">${path}</svg>`
},
iconSize: 16
});
};
var leading_icon_default = LeadingIcon;
// src/components/text_input_dialog.tsx
var import_react7 = __toESM(require_react());
var TextInputDialog = (props) => {
const { ButtonPrimary } = Spicetify.ReactComponent;
const { def, placeholder, onSave } = props;
const [value, setValue] = import_react7.default.useState(def);
const onSubmit = (e) => {
e.preventDefault();
Spicetify.PopupModal.hide();
onSave(value);
};
return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement("form", {
className: "text-input-form",
onSubmit
}, /* @__PURE__ */ import_react7.default.createElement("label", {
className: "text-input-wrapper"
}, /* @__PURE__ */ import_react7.default.createElement("input", {
className: "text-input",
type: "text",
value,
placeholder,
onChange: (e) => setValue(e.target.value)
})), /* @__PURE__ */ import_react7.default.createElement("button", {
type: "submit",
"data-encore-id": "buttonPrimary",
className: "Button-sc-qlcn5g-0 Button-small-buttonPrimary"
}, /* @__PURE__ */ import_react7.default.createElement("span", {
className: "ButtonInner-sc-14ud5tc-0 ButtonInner-small encore-bright-accent-set"
}, "Save"))));
};
var text_input_dialog_default = TextInputDialog;
// src/components/searchbar.tsx
var import_react8 = __toESM(require_react());
var SearchBar = (props) => {
const { setSearch, placeholder } = props;
const handleChange = (e) => {
setSearch(e.target.value);
};
return /* @__PURE__ */ import_react8.default.createElement("div", {
className: "x-filterBox-filterInputContainer x-filterBox-expandedOrHasFilter",
role: "search"
}, /* @__PURE__ */ import_react8.default.createElement("input", {
type: "text",
className: "x-filterBox-filterInput",
role: "searchbox",
maxLength: 80,
autoCorrect: "off",
autoCapitalize: "off",
spellCheck: "false",
placeholder: `Search ${placeholder}`,
"aria-hidden": "false",
onChange: handleChange
}), /* @__PURE__ */ import_react8.default.createElement("div", {
className: "x-filterBox-overlay"
}, /* @__PURE__ */ import_react8.default.createElement("span", {
className: "x-filterBox-searchIconContainer"
}, /* @__PURE__ */ import_react8.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-small x-filterBox-searchIcon",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react8.default.createElement("path", {
d: "M7 1.75a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5zM.25 7a6.75 6.75 0 1 1 12.096 4.12l3.184 3.185a.75.75 0 1 1-1.06 1.06L11.304 12.2A6.75 6.75 0 0 1 .25 7z"
})))), /* @__PURE__ */ import_react8.default.createElement("button", {
className: "x-filterBox-expandButton",
"aria-hidden": "false",
"aria-label": "Search Playlists"
}, /* @__PURE__ */ import_react8.default.createElement("svg", {
"data-encore-id": "icon",
role: "img",
"aria-hidden": "true",
className: "Svg-sc-ytk21e-0 Svg-img-icon-small x-filterBox-searchIcon",
viewBox: "0 0 16 16"
}, /* @__PURE__ */ import_react8.default.createElement("path", {
d: "M7 1.75a5.25 5.25 0 1 0 0 10.5 5.25 5.25 0 0 0 0-10.5zM.25 7a6.75 6.75 0 1 1 12.096 4.12l3.184 3.185a.75.75 0 1 1-1.06 1.06L11.304 12.2A6.75 6.75 0 0 1 .25 7z"
}))));
};
var searchbar_default = SearchBar;
// src/components/album_menu_item.tsx
var createCollection = () => {
const onSave = (value) => {
SpicetifyLibrary.CollectionWrapper.createCollection(value);
};
Spicetify.PopupModal.display({
title: "Create Collection",
content: /* @__PURE__ */ import_react9.default.createElement(text_input_dialog_default, {
def: "New Collection",
placeholder: "Collection Name",
onSave
})
});
};
var CollectionSearchMenu = () => {
const { MenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
const [textFilter, setTextFilter] = import_react9.default.useState("");
const [collections, setCollections] = import_react9.default.useState(null);
const context = import_react9.default.useContext(Spicetify.ContextMenuV2._context);
const uri = context?.props?.uri;
import_react9.default.useEffect(() => {
const fetchCollections = async () => {
const res = await SpicetifyLibrary.CollectionWrapper.getCollectionItems({ textFilter });
setCollections(res);
};
fetchCollections();
}, [textFilter]);
if (!collections)
return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null);
const addToCollection = (collectionUri) => {
SpicetifyLibrary.CollectionWrapper.addAlbumToCollection(collectionUri, uri);
};
const activeCollections = SpicetifyLibrary.CollectionWrapper.getCollectionsWithAlbum(uri);
const hasCollections = activeCollections.length > 0;
const removeFromCollections = () => {
activeCollections.forEach((collection) => {
SpicetifyLibrary.CollectionWrapper.removeAlbumFromCollection(collection.uri, uri);
});
};
const allCollectionsLength = collections.unfilteredLength;
const menuItems = collections.items.map((collection, index) => {
return /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
key: collection.uri,
onClick: () => {
addToCollection(collection.uri);
},
divider: index === 0 ? "before" : void 0
}, collection.name);
});
const menuLength = allCollectionsLength + (hasCollections ? 1 : 0);
return /* @__PURE__ */ import_react9.default.createElement("div", {
className: "main-contextMenu-filterPlaylistSearchContainer",
style: { "--context-menu-submenu-length": `${menuLength}` }
}, /* @__PURE__ */ import_react9.default.createElement("li", {
role: "presentation",
className: "main-contextMenu-filterPlaylistSearch"
}, /* @__PURE__ */ import_react9.default.createElement("div", {
role: "menuitem"
}, /* @__PURE__ */ import_react9.default.createElement(searchbar_default, {
setSearch: setTextFilter,
placeholder: "collections"
}))), /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
key: "new-collection",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons["plus2px"]
}),
onClick: createCollection
}, "Create collection"), hasCollections && /* @__PURE__ */ import_react9.default.createElement(MenuItem, {
key: "remove-collection",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons["minus"]
}),
onClick: removeFromCollections
}, "Remove from all"), menuItems);
};
var AlbumMenuItem = () => {
const { MenuSubMenuItem } = Spicetify.ReactComponent;
const { SVGIcons } = Spicetify;
return /* @__PURE__ */ import_react9.default.createElement(MenuSubMenuItem, {
displayText: "Add to collection",
divider: "after",
leadingIcon: /* @__PURE__ */ import_react9.default.createElement(leading_icon_default, {
path: SVGIcons["plus2px"]
})
}, /* @__PURE__ */ import_react9.default.createElement(CollectionSearchMenu, null));
};
var album_menu_item_default = AlbumMenuItem;
var CollectionsWrapper2 = _CollectionsWrapper;
__publicField(CollectionsWrapper2, "INSTANCE", new _CollectionsWrapper());
window.CollectionsWrapper = CollectionsWrapper2.INSTANCE;
// src/extensions/folder_image_wrapper.ts
var FolderImageWrapper = class extends EventTarget {
var _FolderImageWrapper = class extends EventTarget {
_folderImages;
constructor() {
super();
@ -757,7 +825,9 @@ var library = (() => {
localStorage.setItem("library:folderImages", JSON.stringify(this._folderImages));
}
};
var folder_image_wrapper_default = FolderImageWrapper;
var FolderImageWrapper2 = _FolderImageWrapper;
__publicField(FolderImageWrapper2, "INSTANCE", new _FolderImageWrapper());
window.FolderImageWrapper = FolderImageWrapper2.INSTANCE;
// src/extensions/extension.tsx
var styleLink = document.createElement("link");
@ -773,6 +843,7 @@ var library = (() => {
};
var FolderImage = ({ url }) => {
return /* @__PURE__ */ import_react10.default.createElement("img", {
alt: "Folder Image",
"aria-hidden": "true",
draggable: "false",
loading: "eager",
@ -793,7 +864,7 @@ var library = (() => {
d: "M1 4a2 2 0 0 1 2-2h5.155a3 3 0 0 1 2.598 1.5l.866 1.5H21a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4zm7.155 0H3v16h18V7H10.464L9.021 4.5a1 1 0 0 0-.866-.5z"
})));
};
var SpicetifyLibrary2 = class {
var SpicetifyLibrary = class {
ConfigWrapper = new config_wrapper_default(
[
{
@ -812,16 +883,25 @@ var library = (() => {
type: "toggle",
def: false,
callback: setSearchBarSize
}
},
{
name: "Playlists Page",
key: "show-playlists",
type: "toggle",
def: true,
sectionHeader: "Pages"
},
{ name: "Albums Page", key: "show-albums", type: "toggle", def: true },
{ name: "Collections Page", key: "show-collections", type: "toggle", def: true },
{ name: "Artists Page", key: "show-artists", type: "toggle", def: true },
{ name: "Shows Page", key: "show-shows", type: "toggle", def: true }
],
"library"
);
CollectionWrapper = new collections_wrapper_default();
FolderImageWrapper = new folder_image_wrapper_default();
};
window.SpicetifyLibrary = new SpicetifyLibrary2();
window.SpicetifyLibrary = new SpicetifyLibrary();
(function wait() {
const { LocalStorageAPI } = Spicetify?.Platform;
const { LocalStorageAPI } = Spicetify.Platform;
if (!LocalStorageAPI) {
setTimeout(wait, 100);
return;
@ -829,22 +909,23 @@ var library = (() => {
main(LocalStorageAPI);
})();
function main(LocalStorageAPI) {
const isAlbum = (props) => {
return props.uri?.includes("album");
};
const isAlbum = (props) => props.uri?.includes("album");
const isArtist = (props) => props.uri?.includes("artist");
Spicetify.ContextMenuV2.registerItem(/* @__PURE__ */ import_react10.default.createElement(album_menu_item_default, null), isAlbum);
Spicetify.ContextMenuV2.registerItem(/* @__PURE__ */ import_react10.default.createElement(artist_menu_item_default, null), isArtist);
Spicetify.Platform.LibraryAPI.getEvents()._emitter.addListener("update", () => CollectionsWrapper.cleanCollections());
function injectFolderImages() {
const rootlist = document.querySelector(".main-rootlist-wrapper > div:nth-child(2)");
if (!rootlist)
return setTimeout(injectFolderImages, 100);
setTimeout(() => {
Array.from(rootlist.children).forEach((el) => {
const uri = el.querySelector(".main-yourLibraryX-listItemGroup")?.getAttribute("aria-labelledby")?.slice(14);
for (const el of Array.from(rootlist.children)) {
const uri = el.querySelector("[aria-labelledby]")?.getAttribute("aria-labelledby")?.slice(14);
if (uri?.includes("folder")) {
const imageBox = el.querySelector(".x-entityImage-imageContainer");
if (!imageBox)
return;
const imageUrl = window.SpicetifyLibrary.FolderImageWrapper.getFolderImage(uri);
const imageUrl = FolderImageWrapper.getFolderImage(uri);
if (!imageUrl)
import_react_dom.default.render(/* @__PURE__ */ import_react10.default.createElement(FolderPlaceholder, null), imageBox);
else
@ -852,17 +933,13 @@ var library = (() => {
url: imageUrl
}), imageBox);
}
});
}
}, 500);
}
injectFolderImages();
window.SpicetifyLibrary.FolderImageWrapper.addEventListener("update", () => {
injectFolderImages();
});
FolderImageWrapper.addEventListener("update", injectFolderImages);
function injectYLXButtons() {
const ylx_filter = document.querySelector(
".main-yourLibraryX-libraryRootlist > .main-yourLibraryX-libraryFilter"
);
const ylx_filter = document.querySelector(".main-yourLibraryX-libraryRootlist > .main-yourLibraryX-libraryFilter");
if (!ylx_filter) {
return setTimeout(injectYLXButtons, 100);
}
@ -887,37 +964,17 @@ var library = (() => {
collapseButton
);
}
function injectExpandButton() {
const sidebarHeader = document.querySelector("li.main-yourLibraryX-navItem[data-id='/library']");
if (!sidebarHeader) {
return setTimeout(injectExpandButton, 100);
}
const expandButton = document.createElement("span");
expandButton.classList.add("expand-button");
sidebarHeader.appendChild(expandButton);
import_react_dom.default.render(/* @__PURE__ */ import_react10.default.createElement(expand_button_default, null), expandButton);
}
function removeExpandButton() {
const expandButton = document.querySelector(".expand-button");
if (expandButton)
expandButton.remove();
}
const state = LocalStorageAPI.getItem("ylx-sidebar-state");
if (state === 0) {
if (state === 0)
injectYLXButtons();
} else if (state === 1) {
injectExpandButton();
}
LocalStorageAPI.getEvents()._emitter.addListener("update", (e) => {
const { key, value } = e.data;
if (key === "ylx-sidebar-state" && value === 0) {
injectFolderImages();
injectYLXButtons();
removeExpandButton();
}
if (key === "ylx-sidebar-state" && value === 1) {
injectFolderImages();
injectExpandButton();
}
});
}

View file

@ -8,6 +8,7 @@ var library = (() => {
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
@ -21,13 +22,17 @@ var library = (() => {
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
// src/extensions/folder_image_wrapper.ts
var folder_image_wrapper_exports = {};
__export(folder_image_wrapper_exports, {
default: () => folder_image_wrapper_default
});
var FolderImageWrapper = class extends EventTarget {
var _FolderImageWrapper = class extends EventTarget {
_folderImages;
constructor() {
super();
@ -54,6 +59,9 @@ var library = (() => {
localStorage.setItem("library:folderImages", JSON.stringify(this._folderImages));
}
};
var FolderImageWrapper = _FolderImageWrapper;
__publicField(FolderImageWrapper, "INSTANCE", new _FolderImageWrapper());
window.FolderImageWrapper = FolderImageWrapper.INSTANCE;
var folder_image_wrapper_default = FolderImageWrapper;
return __toCommonJS(folder_image_wrapper_exports);
})();

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc8b4/navBar.module.css */
/* ../../../AppData/Local/Temp/tmp-4464-wbZ6O1BKhuot/19178f16f304/navBar.module.css */
.navBar-module__topBarHeaderItem___piw4C_library {
-webkit-app-region: no-drag;
display: inline-block;
@ -46,7 +46,7 @@ div.navBar-module__topBarHeaderItemLink___xA4uv_library {
padding: 0;
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8baff0/app.css */
/* ../../../AppData/Local/Temp/tmp-4464-wbZ6O1BKhuot/19178f16dc80/app.css */
:root {
--library-card-size: 180px;
--library-searchbar-size: 200px;
@ -57,6 +57,10 @@ div.navBar-module__topBarHeaderItemLink___xA4uv_library {
#library-app .grid {
grid-template-columns: repeat(auto-fill, minmax(var(--library-card-size), 1fr)) !important;
}
#library-app .main-card-cardContainer {
width: 100%;
height: 100%;
}
#library-app .load-more-card {
display: flex;
gap: 10px;
@ -100,7 +104,7 @@ div.navBar-module__topBarHeaderItemLink___xA4uv_library {
align-self: end;
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc361/external.css */
/* ../../../AppData/Local/Temp/tmp-4464-wbZ6O1BKhuot/19178f16ee21/external.css */
body:not(.show-ylx-filters) .main-yourLibraryX-filterArea:not(:has(> .main-yourLibraryX-libraryFilter)),
.main-yourLibraryX-header:not(:has(> .main-yourLibraryX-headerContent > .main-yourLibraryX-collapseButton > button:nth-child(2))),
.main-yourLibraryX-collapseButton > button:first-child,
@ -113,10 +117,12 @@ body:not(.show-ylx-filters) .main-yourLibraryX-filterArea:not(:has(> .main-yourL
.main-yourLibraryX-header {
margin-top: -8px;
}
.main-yourLibraryX-librarySortWrapper button span:first-child {
.main-yourLibraryX-libraryFilter .main-yourLibraryX-librarySortWrapper button span:first-child,
.main-yourLibraryX-libraryFilter span[role=presentation] span[role=presentation] button span:first-child {
display: none;
}
.main-yourLibraryX-librarySortWrapper {
.main-yourLibraryX-libraryFilter .main-yourLibraryX-librarySortWrapper,
.main-yourLibraryX-libraryFilter span[role=presentation] {
margin-left: auto;
}
.toggle-filters-button > button:after,
@ -140,21 +146,24 @@ li.main-yourLibraryX-navItem[data-id="/library"] {
li.main-yourLibraryX-navItem[data-id="/library"] > a {
flex-grow: 1;
}
.toggle-filters-button > button,
.collapse-button > button,
.main-yourLibraryX-librarySortWrapper > button {
.main-yourLibraryX-libraryFilter .toggle-filters-button > button,
.main-yourLibraryX-libraryFilter .collapse-button > button,
.main-yourLibraryX-libraryFilter .main-yourLibraryX-librarySortWrapper > button,
.main-yourLibraryX-libraryFilter span[role=presentation] span[role=presentation] > button {
padding: 0;
}
.toggle-filters-button,
.collapse-button,
.main-yourLibraryX-librarySortWrapper {
.main-yourLibraryX-libraryFilter .toggle-filters-button,
.main-yourLibraryX-libraryFilter .collapse-button,
.main-yourLibraryX-libraryFilter .main-yourLibraryX-librarySortWrapper,
.main-yourLibraryX-libraryFilter span[role=presentation] span[role=presentation] {
display: flex;
flex-basis: 32px;
justify-content: center;
min-width: 24px;
flex-shrink: 10;
}
.main-yourLibraryX-librarySortWrapper > button > span:nth-child(2) {
.main-yourLibraryX-libraryFilter .main-yourLibraryX-librarySortWrapper > button > span:nth-child(2),
.main-yourLibraryX-libraryFilter span[role=presentation] span[role=presentation] > button > span:nth-child(2) {
margin: 0;
}
.LayoutResizer__resize-bar {
@ -227,7 +236,7 @@ li.main-yourLibraryX-navItem[data-id="/library"] > a {
transform: scale(1.04);
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc5f2/config_modal.css */
/* ../../../AppData/Local/Temp/tmp-4464-wbZ6O1BKhuot/19178f16f0a2/config_modal.css */
.config-container {
gap: 10px;
display: flex;
@ -304,7 +313,7 @@ li.main-yourLibraryX-navItem[data-id="/library"] > a {
width: 200px;
}
/* ../../../AppData/Local/Temp/tmp-5152-1Ex7Bvh4Tu6u/18f3cb8bc713/shared.css */
/* ../../../AppData/Local/Temp/tmp-4464-wbZ6O1BKhuot/19178f16f163/shared.css */
.grid {
--grid-gap: 24px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)) !important;
@ -327,6 +336,20 @@ li.main-yourLibraryX-navItem[data-id="/library"] > a {
flex-direction: column;
gap: 24px;
}
.badge {
position: absolute;
top: 3%;
left: 3%;
height: 30px;
width: 30px;
border-radius: 50%;
background-color: rgb(65, 110, 170);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
.page-header {
align-content: space-between;
align-items: center;

View file

@ -29,4 +29,4 @@ custom_apps = library|lyrics-plus|marketplace|stats
; DO NOT CHANGE!
[Backup]
version = 1.2.42.290.g242057a2
with = 2.37.1
with = 2.38.3