Compare commits

..

No commits in common. "main" and "4.1.0" have entirely different histories.
main ... 4.1.0

41 changed files with 380 additions and 9895 deletions

View file

@ -1,3 +0,0 @@
{
"presets": ["@babel/env"]
}

View file

@ -12,7 +12,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
with:
repository: ${{ github.event.repository.full_name }}
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 0

View file

@ -14,12 +14,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
with:
repository: ${{ github.event.repository.full_name }}
ref: ${{ github.head_ref }}
fetch-depth: 0
- name: Prettify code
uses: creyD/prettier_action@v4.1.1
uses: creyD/prettier_action@v4.0
with:
prettier_options: --write **/*.{js,css}
same_commit: True

View file

@ -28,7 +28,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "15"
node-version: "14"
- name: Build Webpack
run: |

View file

@ -1,21 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days.'
close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 15 days with no activity.'
days-before-issue-stale: 60
days-before-pr-stale: 60
days-before-issue-close: 7
days-before-pr-close: 15
operations-per-run: 100
ascending: true

View file

@ -10,7 +10,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
with:
repository: ${{ github.event.repository.full_name }}
ref: ${{ github.head_ref }}
- name: Get commit SHA
@ -19,7 +18,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "15"
node-version: "14"
- name: Test-Build Webpack
run: |

View file

@ -1,13 +1,23 @@
Added:
- Ability to hide premium features in addition to ads
- Ability to show current artist's genres
- A spinning loader at startup while spotify is not ready (Can be disabled in settings)
- The old search box in the top bar (Can be disabled in settings)
- Ability to disable the progress transition to improve performance `Playbar > Progress Transition` (#118)
- Option to insert custom CSS
- Ability to search Settings
Fixed:
- Custom search input not being focussed after clicking
- When changing songs, the color shifts to blue for a second (#179)
- Checking for update every 10 Minutes not working
- Background album art is cut off (#116)
- Tall context menus not scrollable (#114)
- Default folder images not working
Improved:
- Add `embedWidgetGenerator` modals to custom modal styles. (Things like the [spicetify-marketplace](https://github.com/CharlieS1103/spicetify-marketplace) options)
- Changed custom app tab styles to fit in with the theme
- Changed notification styles to fit in with other elements of the theme
- The visualizer icon when playing a song from a playlist is now colored
- Moved `Hide Ads` into `Main Settings`
- Added hover tooltips on playlist / folder images. Useful for collapsed sidebar
- Now also shows if a new spicetify-cli update is available (Requires spicetify-cli > 2.7.2)
- Popups (like lyrics-plus settings) styles are now consistent with the Dribbblish settings styles
- Only display option to reset setting if it was changed
- `Connect to Device` interface / selection is now more inline with other context menus
- When casting to a device it shows up as info (next to your profile picture)
- Confirmation modal styles are now more inline with other popups
- Made `System` the default theme. Windows users see [this](https://github.com/JulienMaille/dribbblish-dynamic-theme#follow-system-darklight-theme-powershell)

View file

@ -117,12 +117,12 @@ xpui.js_repl_8008=,`${1}58,
Write-Part "APPLYING";
$backupVer = $configFile -match "^version"
if ($backupVer.Length -gt 0) {
$version = ConvertFrom-StringData $backupVer[0]
if ($version.version.Length -gt 0) {
spicetify apply
} else {
spicetify backup apply
}
Write-Done
}
else {
Write-Part "`nYour Powershell version is less than "; Write-Emphasized "$PSMinVersion";

10
manifest.json Normal file
View file

@ -0,0 +1,10 @@
{
"name": "Dribbblish Dynamic (Beta)",
"description": "A mod of Dribbblish theme for Spicetify with support for light/dark modes and album art based colors. (Beta Release)",
"branch": "release-beta",
"preview": "https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/main/showcase-images/preview.gif",
"readme": "https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/main/README.md",
"usercss": "user.css",
"schemes": "color.ini",
"include": ["https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/release-beta/dribbblish-dynamic.js"]
}

View file

@ -1,22 +0,0 @@
[
{
"name": "Dribbblish Dynamic",
"description": "A mod of Dribbblish theme for Spicetify with support for light/dark modes and album art based colors. (Beta Release)",
"branch": "release-stable",
"preview": "https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/main/showcase-images/preview.gif",
"readme": "https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/main/README.md",
"usercss": "user.css",
"schemes": "color.ini",
"include": ["https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/release-stable/dribbblish-dynamic.js"]
},
{
"name": "Dribbblish Dynamic (Beta)",
"description": "A mod of Dribbblish theme for Spicetify with support for light/dark modes and album art based colors. (Beta Release)",
"branch": "release-beta",
"preview": "https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/main/showcase-images/preview.gif",
"readme": "https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/main/README.md",
"usercss": "user.css",
"schemes": "color.ini",
"include": ["https://raw.githubusercontent.com/JulienMaille/dribbblish-dynamic-theme/release-beta/dribbblish-dynamic.js"]
}
]

9109
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,11 @@
{
"name": "dribbblish-dynamic-theme",
"version": "4.1.1",
"version": "4.0.0",
"homepage": "https://github.com/JulienMaille/dribbblish-dynamic-theme",
"bugs": {
"url": "https://github.com/JulienMaille/dribbblish-dynamic-theme/issues"
},
"devDependencies": {
"@babel/core": "^7.17.0",
"@babel/preset-env": "^7.16.5",
"@babel/register": "^7.16.5",
"@material-icons/svg": "^1.0.22",
"clean-webpack-plugin": "^4.0.0",
"sass": "^1.43.5",
@ -17,18 +14,16 @@
"webpack-cli": "^4.9.0"
},
"scripts": {
"build": "webpack"
"build": "webpack --mode=development"
},
"dependencies": {
"chroma-js": "^2.1.2",
"colorthief": "^2.3.2",
"jquery": "^3.6.0",
"lodash.debounce": "^4.0.8",
"lodash.defaultsdeep": "^4.6.1",
"markdown-it": "^12.3.2",
"markdown-it": "^12.2.0",
"markdown-it-attrs": "^4.1.0",
"moment": "^2.29.2",
"moment": "^2.29.1",
"node-vibrant": "^3.1.6",
"svgson": "^5.2.1"
}
}
}

View file

@ -1,20 +1,10 @@
Get-Process -Name Spotify -ErrorAction SilentlyContinue | Stop-Process -Force
Get-Process -Name SpotifyWebHelper -ErrorAction SilentlyContinue | Stop-Process -Force
$sp = spicetify config spotify_path
$sp += "\Spotify.exe"
$sp = "$env:APPDATA\Spotify\Spotify.exe"
Copy-Item $sp ($sp + ".backup")
$bytes = [System.IO.File]::ReadAllBytes($sp);
$toRemove = [System.Text.Encoding]::UTF8.GetBytes("force-dark-mode");
$sw = [System.Diagnostics.Stopwatch]::StartNew()
for ($i = 0; $i -lt $bytes.Length; $i++) {
if ($sw.Elapsed.TotalMilliseconds -ge 1000) {
Write-Progress -Activity "Enabling dark mode in Spotify.exe" -status "Patching binary file $i" -percentComplete ($i / $bytes.Length*100);
$sw.Reset();
$sw.Start();
}
$found = $true
for ($j = 0; $j -lt $toRemove.Length; $j++) {
if ($bytes[$i + $j] -ne $toRemove[$j]) {
@ -31,9 +21,4 @@ for ($i = 0; $i -lt $bytes.Length; $i++) {
}
}
[System.IO.File]::WriteAllBytes($sp, $bytes);
if ( $found ) {
Write-Host "The patch is complete." -ForegroundColor "Green"
} else {
Write-Host "Failed to patch the file." -ForegroundColor "Red"
}
[System.IO.File]::WriteAllBytes($sp, $bytes);

View file

@ -1,131 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 24'>
<defs>
<style>
@keyframes play {
0% {
transform: scaleY(1);
}
3.3% {
transform: scaleY(0.9583);
}
6.6% {
transform: scaleY(0.9166);
}
9.9% {
transform: scaleY(0.8333);
}
13.3% {
transform: scaleY(0.7083);
}
16.6% {
transform: scaleY(0.5416);
}
19.9% {
transform: scaleY(0.4166);
}
23.3% {
transform: scaleY(0.25);
}
26.6% {
transform: scaleY(0.1666);
}
29.9% {
transform: scaleY(0.125);
}
33.3% {
transform: scaleY(0.125);
}
36.6% {
transform: scaleY(0.1666);
}
39.9% {
transform: scaleY(0.1666);
}
43.3% {
transform: scaleY(0.2083);
}
46.6% {
transform: scaleY(0.2916);
}
49.9% {
transform: scaleY(0.375);
}
53.3% {
transform: scaleY(0.5);
}
56.6% {
transform: scaleY(0.5833);
}
59.9% {
transform: scaleY(0.625);
}
63.3% {
transform: scaleY(0.6666);
}
66.6% {
transform: scaleY(0.6666);
}
69.9% {
transform: scaleY(0.6666);
}
73.3% {
transform: scaleY(0.6666);
}
76.6% {
transform: scaleY(0.7083);
}
79.9% {
transform: scaleY(0.75);
}
83.3% {
transform: scaleY(0.8333);
}
86.6% {
transform: scaleY(0.875);
}
89.9% {
transform: scaleY(0.9166);
}
93.3% {
transform: scaleY(0.9583);
}
96.6% {
transform: scaleY(1);
}
}
svg[icon-type="dribbblish"][icon-name="equaliser"] > rect[i="0"] {
transform-origin: bottom;
animation: play 0.9s -0.51s infinite;
}
svg[icon-type="dribbblish"][icon-name="equaliser"] > rect[i="1"] {
transform-origin: bottom;
animation: play 0.9s infinite;
}
svg[icon-type="dribbblish"][icon-name="equaliser"] > rect[i="2"] {
transform-origin: bottom;
animation: play 0.9s -0.15s infinite;
}
svg[icon-type="dribbblish"][icon-name="equaliser"] > rect[i="3"] {
transform-origin: bottom;
animation: play 0.9s -0.75s infinite;
}
</style>
</defs>
<rect fill="currentColor" i="0" class='cls-1' width='4' height='24'/>
<rect fill="currentColor" i="1" class='cls-1' x='6' width='4' height='24'/>
<rect fill="currentColor" i="2" class='cls-1' x='12' width='4' height='24'/>
<rect fill="currentColor" i="3" class='cls-1' x='18' width='4' height='24'/>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -1,42 +0,0 @@
<svg viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
/* From https://codepen.io/mrrocks/pen/EiplA */
@keyframes loader-rotator {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(270deg);
}
}
@keyframes loader-dash {
0% {
stroke-dashoffset: 187;
}
50% {
stroke-dashoffset: 46.75;
transform: rotate(135deg);
}
100% {
stroke-dashoffset: 187;
transform: rotate(450deg);
}
}
svg[icon-type="dribbblish"][icon-name="loading"] {
animation: loader-rotator 1.4s linear infinite;
}
svg[icon-type="dribbblish"][icon-name="loading"] circle {
stroke-dasharray: 187;
stroke-dashoffset: 0;
transform-origin: center;
animation: loader-dash 1.4s ease-in-out infinite;
}
</style>
</defs>
<circle fill="none" stroke="currentColor" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,6 +1,6 @@
import $ from "jquery";
import { renderMD, defaults } from "./Util";
import { renderMD } from "./Util";
import { icons } from "./Icons";
export default class ConfigMenu {
@ -125,7 +125,7 @@ export default class ConfigMenu {
parent: null
};
// Set Defaults
options = defaults(options, defaultOptions);
options = { ...defaultOptions, ...options };
if (typeof options.area == "string") options.area = { name: options.area, order: 0 };
options.description = options.description
.split("\n")

View file

@ -1,8 +1,7 @@
import ConfigMenu from "./ConfigMenu";
import Info from "./Info";
import Loader from "./Loader";
import Overlay from "./Overlay";
import Icon, { icons } from "./Icons";
import { icons } from "./Icons";
export default class Dribbblish {
/**
@ -24,10 +23,7 @@ export default class Dribbblish {
/** @type {Loader} */
loader;
/** @type {Overlay} */
overlay;
/** @type {Icon} */
/** @type {Icons} */
icons;
/** @type {Object.<string, listener[]>} */
@ -40,12 +36,9 @@ export default class Dribbblish {
this.config = new ConfigMenu();
this.info = new Info();
this.loader = new Loader();
this.overlay = new Overlay();
this.icons = icons;
let tries = 0;
const interval = setInterval(() => {
if (++tries > 50) throw new Error("ready timeout");
if (document.querySelector("#main") == null || Spicetify?.showNotification == undefined || !this.info.isReady()) return;
this.#ready = true;
this.emit("ready");

View file

@ -1,13 +1,11 @@
import { defaults } from "./Util";
import { parseSync as parseSVG, stringify as stringifySVG } from "svgson";
export default class Icons {
/** @typedef {"custom" | "material:baseline" | "material:outline" | "material:round" | "material:sharp" | "material:twotone"} IconStyle */
/** @typedef {"baseline" | "outline" | "round" | "sharp" | "twotone"} IconStyle */
/**
* @typedef {Object} IconOptions
* @property {IconStyle} [style="round"]
* @property {String} [className=""]
* @property {Number} [size=16]
* @property {Number} [scale=1]
* @property {String} [fill="currentColor"]
@ -17,45 +15,24 @@ export default class Icons {
/** @type {Object.<String, Object.<IconStyle, String>>} */
#icons;
constructor(icons) {
this.#icons = icons ?? process.env.DRIBBBLISH_ICONS;
constructor() {
this.#icons = process.env.DRIBBBLISH_ICONS;
}
/**
* @param {String} name
* @param {IconStyle} style
* @returns {String}
*/
getRawSVG(name, style) {
if (style == null) style = this.#getDefaultStyle(name);
getRawSVG(name, style = "round") {
if (!this.#icons.hasOwnProperty(name)) throw new Error(`Icon "${name}" does not exist`);
if (!this.#icons[name].hasOwnProperty(style)) {
const styles = Object.keys(this.#icons[name])
.map((s) => `"${s}"`)
.join(", ");
throw new Error(`Icon "${name}" does not have style "${style}". It is available in styles [${styles}].`);
if (typeof this.#icons[name] == "string") {
return this.#icons[name];
} else {
if (!this.#icons[name].hasOwnProperty(style)) {
const styles = Object.keys(this.#icons[name])
.map((s) => `"${s}"`)
.join(", ");
throw new Error(`Icon "${name}" does not have style "${style}". It is available in styles [${styles}].`);
}
return this.#icons[name][style];
}
return this.#icons[name][style];
}
getAvailableStyles(name) {
if (!this.#icons.hasOwnProperty(name)) throw new Error(`Icon "${name}" does not exist`);
return Object.keys(this.#icons[name]);
}
/**
* @param {String} name
* @returns
*/
#getDefaultStyle(name) {
const styles = this.getAvailableStyles(name);
for (const s of ["custom", "material:round"]) {
if (styles.includes(s)) return s;
}
return styles[0];
}
/**
@ -67,43 +44,32 @@ export default class Icons {
get(name, options) {
/** @type {IconOptions} */
const defaultOptions = {
style: this.#getDefaultStyle(name),
className: "",
style: "round",
size: 16,
scale: 1,
fill: "currentColor",
base64: false
};
options = defaults(options, defaultOptions);
options = { ...defaultOptions, ...options };
const svg = parseSVG(this.getRawSVG(name, options.style));
// Add general / required attributes
svg.attributes["icon-type"] = "dribbblish";
svg.attributes["icon-name"] = name;
svg.attributes["icon-style"] = options.style;
svg.attributes.type = "dribbblish-icon";
svg.attributes.fill = options.fill;
svg.attributes.width = options.size;
svg.attributes.height = options.size;
// Add className
if (options.className != "") svg.attributes.class = options.className;
// Add Styles
const styles = {};
// Create CSSStyleDeclaration by creating an element since there is no constructor for it
const styles = document.createElement("a").style;
if (options.scale != 1) {
styles["transform"] = `scale(${options.scale})`;
styles["transform-origin"] = "center";
styles.transform = `scale(${options.scale})`;
styles.transformOrigin = "center";
}
const styleStr = Object.entries(styles)
.map(([prop, val]) => `${prop}: ${val};`)
.join(" ");
svg.children = svg.children.map((child) => {
if (styleStr != "") child.attributes.style = styleStr;
child.attributes.style = styles.cssText;
return child;
});
// Add title
if (options.title != null) {
svg.children.push({
name: "title",

View file

@ -1,18 +1,25 @@
import Overlay from "./Overlay";
export default class Loader {
/** @type {Overlay} */
#overlay;
/** @type {HTMLDivElement} */
#container;
constructor() {
this.#overlay = new Overlay();
this.#container = document.createElement("div");
this.#container.id = "dribbblish-loader";
this.#container.innerHTML = `
<svg width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
</svg>
`;
document.body.appendChild(this.#container);
}
show(text) {
this.#overlay.show({ icon: "loading", text });
this.#container.setAttribute("text", text ?? "");
this.#container.setAttribute("active", "");
}
hide() {
this.#overlay.hide();
this.#container.removeAttribute("active");
}
}

View file

@ -1,68 +0,0 @@
import { defaults } from "./Util";
import { icons } from "./Icons";
export default class Overlay {
/**
* @typedef {Object} DribbblishOverlayOptions
* @property {String} icon
* @property {String} text
* @property {DribbblishOverlayColor} color
*/
/**
* @typedef {Object} DribbblishOverlayColor
* @property {String} [icon]
* @property {String} [text]
*/
/** @type {HTMLDivElement} */
#container;
/** @type {HTMLElement} */
#iconElem;
/** @type {HTMLSpanElement} */
#textElem;
constructor() {
this.#container = document.createElement("div");
this.#container.id = "dribbblish-overlay";
this.#container.innerHTML = /* html */ `
<div>
<i></i>
<span>Loading...</span>
</div>
`;
this.#iconElem = this.#container.querySelector("i");
this.#textElem = this.#container.querySelector("span");
document.body.appendChild(this.#container);
}
/**
*
* @param {DribbblishOverlayOptions} options
*/
show(options) {
const defaultOptions = {
icon: "",
text: "",
color: {
icon: "var(--spice-text)",
text: "var(--spice-subtext)"
}
};
options = defaults(options, defaultOptions);
if (options.icon != "" && !options.icon.startsWith("<svg")) options.icon = icons.get(options.icon, { size: 64 });
this.#iconElem.innerHTML = options.icon;
this.#iconElem.style.setProperty("--color", options.color.icon);
this.#textElem.innerHTML = options.text;
this.#textElem.style.setProperty("--color", options.color.text);
this.#container.setAttribute("active", "");
}
hide() {
this.#container.removeAttribute("active");
}
}

View file

@ -1,13 +0,0 @@
export function lightOffset(n, offset) {
return `calc(${n} + ${offset} * var(--is_light))`;
}
export function spiceColor(key, alpha = 1, _lightOffset = 0) {
if (alpha == 1) {
return `var(--spice-${key})`;
} else if (_lightOffset == 0) {
return `rgba(var(--spice-rgb-${key}), ${alpha})`;
} else {
return `rgba(var(--spice-rgb-${key}), ${lightOffset(alpha, _lightOffset)})`;
}
}

View file

@ -1,7 +1,5 @@
import MarkdownIt from "markdown-it";
import MarkdownItAttrs from "markdown-it-attrs";
import defaultsDeep from "lodash.defaultsdeep";
import { default as _debounce } from "lodash.debounce";
/**
* @callback waitForElCb
@ -71,25 +69,22 @@ export function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* @template T
* @param {T[]} arr
* @returns {T}
*/
export function randomFromArray(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
/** @type {_debounce} */
export function debounce(fn, wait, opts) {
return _debounce(fn, wait, opts);
}
/**
* @param {Object} options
* @param {...Object} defaults
* @returns {Object}
*/
export function defaults(options, ...defaults) {
return defaultsDeep(options, ...defaults);
export function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}

View file

@ -12,7 +12,6 @@ $("html").attr("dribbblish-js-installed", "");
import { waitForElement, copyToClipboard, capitalizeFirstLetter, getClosestToNum, randomFromArray, debounce } from "./Util";
import { default as _Dribbblish } from "./Dribbblish";
import "./Folders";
import { spiceColor } from "./SassUtil";
// To expose to external scripts
const Dribbblish = new _Dribbblish();
@ -48,8 +47,8 @@ Dribbblish.on("ready", () => {
? {
icon: "settings",
color: {
fg: spiceColor("subtext"),
bg: spiceColor("subtext", 0.1, 0.05)
fg: "var(--spice-subtext)",
bg: "rgba(var(--spice-rgb-subtext), calc(0.1 + var(--is_light) * 0.05))"
},
order: 999,
tooltip: "Open Dribbblish Settings",
@ -77,7 +76,7 @@ Dribbblish.on("ready", () => {
input.setAttribute("spellcheck", "false");
input.addEventListener("click", (e) => {
if (!Spicetify.Platform.History.location.pathname.startsWith("/search")) Spicetify.Platform.History.push(`/search/${input.value}`);
waitForElement([`.main-topBar-topbarContent form[role="search"]`], ([defaultSearch]) => {
waitForElement([`[data-testid="search-input"]`], ([defaultSearch]) => {
input.focus();
});
});
@ -216,29 +215,14 @@ Dribbblish.on("ready", () => {
onChange: (val) => $("#main").attr("playbar-album-info", val)
});
Dribbblish.config.register({
area: "Playbar",
type: "checkbox",
key: "showGenreInfoInPlaybar",
name: "Show Genre Info in Playbar",
description: "Show artist's genres in the Playbar",
defaultValue: false,
onChange: (val) => $("#main").attr("playbar-genre-info", val)
});
Dribbblish.config.register({
order: 999,
type: "select",
data: { none: "Hide Nothing", ads: "Hide Ads", premium: "Hide Premium Features", both: "Hide Both" },
key: "hideAdsOrPremium",
name: "Hide Ads / Premium Features",
description: `Hide ads / premium features like Fullscreen and Download Buttons (see: [SpotifyNoPremium](https://github.com/Daksh777/SpotifyNoPremium))`,
defaultValue: "none",
onChange: (val) => {
if (val == "none") return $("#main").attr("hide-ads", null);
if (val == "both") return $("#main").attr("hide-ads", "ads premium");
$("#main").attr("hide-ads", val);
}
type: "checkbox",
key: "hideAds",
name: "Hide Ads",
description: `Hide ads / premium features (see: [SpotifyNoPremium](https://github.com/Daksh777/SpotifyNoPremium))`,
defaultValue: false,
onChange: (val) => $("#main").attr("hide-ads", val)
});
waitForElement([".main-rootlist-rootlist", ".main-rootlist-wrapper > :nth-child(2) > :first-child", "#spicetify-show-list"], ([rootlist]) => {
@ -379,11 +363,11 @@ Dribbblish.on("ready", () => {
key: "aboutDribbblishInfo",
name: "Info",
description: `
OS: \`${capitalizeFirstLetter(Spicetify.Platform.PlatformData.os_name)} v${Spicetify.Platform.PlatformData.os_version}\`
Spotify: \`v${Spicetify.Platform.PlatformData.event_sender_context_information?.client_version_string ?? Spicetify.Platform.PlatformData.client_version_triple}\`
Spicetify: \`${Spicetify.version != null ? `v${Spicetify.version}` : "< v2.7.3"}\`
Dribbblish: \`v${process.env.DRIBBBLISH_VERSION}-${process.env.COMMIT_HASH}\`
`,
OS: \`${capitalizeFirstLetter(Spicetify.Platform.PlatformData.os_name)} v${Spicetify.Platform.PlatformData.os_version}\`
Spotify: \`v${Spicetify.Platform.PlatformData.event_sender_context_information?.client_version_string ?? Spicetify.Platform.PlatformData.client_version_triple}\`
Spicetify: \`${Spicetify.version != null ? `v${Spicetify.version}` : "< v2.7.3"}\`
Dribbblish: \`v${process.env.DRIBBBLISH_VERSION}-${process.env.COMMIT_HASH}\`
`,
data: "Copy",
onChange: function () {
copyToClipboard(this.description.replace(/\`/g, ""));
@ -400,29 +384,30 @@ Dribbblish.on("ready", () => {
data: "Create Report",
onChange: () => {
const reportBody = `
${process.env.BUG_REPORT}
${process.env.BUG_REPORT}
<!-- Leave the lines below as they are -->
---
<!-- Leave the lines below as they are -->
---
<details>
<summary>Info for Contributors</summary>
### Info for Contributors:
**Versions**
${Dribbblish.config.getOptions("aboutDribbblishInfo").description}
**Versions**
${Dribbblish.config.getOptions("aboutDribbblishInfo").description.split("\n").join("\n ")}
**Extensions**
${$(`script[src^="extensions/"]`)
.toArray()
.map((e) => `- ${e.src.split("/").slice(-1)[0]}`)
.join("\n")}
**Extensions**
${$(`script[src^="extensions/"]`)
.toArray()
.map((e) => `- ${e.src.split("/").slice(-1)[0]}`)
.join("\n ")}
**Settings**
\`\`\`json
${JSON.stringify(Dribbblish.config.export(), null, 4).split("\n").join("\n ")}
\`\`\`
</details>
`.replace(/^ {12}/gm, "");
**Settings**
\`\`\`json
${JSON.stringify(Dribbblish.config.export(), null, 4)}
\`\`\`
`
.split("\n")
.map((line) => line.replace(/^ {16}/, ""))
.join("\n");
const reportURL = new URL("https://github.com/JulienMaille/dribbblish-dynamic-theme/issues/new");
reportURL.searchParams.set("labels", "bug");
@ -460,13 +445,8 @@ Dribbblish.on("ready", () => {
/* js */
async function getAlbumRelease(uri) {
const info = await Spicetify.CosmosAsync.get(`https://api.spotify.com/v1/albums/${uri}`);
return { year: info.release_date };
}
async function getGenres(uri) {
const res = await Spicetify.CosmosAsync.get(`https://api.spotify.com/v1/artists/${uri}`);
return res.genres.slice(0, 3);
const info = await Spicetify.CosmosAsync.get(`hm://album/v1/album-app/album/${uri}/desktop`);
return { year: info.year, month: (info.month ?? 1) - 1, day: info.day ?? 1 };
}
function isLight(hex) {
@ -565,10 +545,11 @@ Dribbblish.on("ready", () => {
Algorithm of selecting colors from the albumart
- **Colorthief [(see)](https://lokeshdhakar.com/projects/color-thief/):** Gets more fitting colors
- **Vibrant [(see)](https://jariz.github.io/vibrant.js/):** Gets more vibrant colors *(was the default up to v3.1.1)*
- **Spotify:** Basically Vibrant but internal and without support of local files
- **Static:** Select a static color to be used
{.muted}
`,
data: { colorthief: "Colorthief", vibrant: "Vibrant", static: "Static" },
data: { spotify: "Spotify", colorthief: "Colorthief", vibrant: "Vibrant", static: "Static" },
defaultValue: "colorthief",
onChange: () => updateColors(),
showChildren: (val) => {
@ -752,19 +733,8 @@ Dribbblish.on("ready", () => {
}
const albumInfoSpan = document.getElementById("main-trackInfo-year");
if (!document.getElementById("main-trackInfo-genre")) {
const el = document.createElement("div");
el.classList.add("main-trackInfo-release", "standalone-ellipsis-one-line", "main-type-finale");
el.setAttribute("as", "div");
el.id = "main-trackInfo-genre";
document.querySelector(".main-trackInfo-container").append(el);
}
const genreInfoSpan = document.getElementById("main-trackInfo-genre");
let track = Spicetify.Player.data.track;
let album_uri = track.metadata.album_uri;
let artist_uri = track.metadata.artist_uri;
let bgImage = track.metadata.image_url;
let album_uri = Spicetify.Player.data.track.metadata.album_uri;
let bgImage = Spicetify.Player.data.track.metadata.image_url;
if (bgImage === undefined) {
bgImage = "/images/tracklist-row-song-fallback.svg";
}
@ -775,37 +745,22 @@ Dribbblish.on("ready", () => {
const albumLinkElem = /* html */ `
<span>
<span draggable="true">
<a draggable="false" dir="auto" href="${album_uri}">${track.metadata.album_title}</a>
<a draggable="false" dir="auto" href="${album_uri}">${Spicetify.Player.data.track.metadata.album_title}</a>
</span>
</span>
`;
const albumDateElem = /* html */ `<span> • <span title="${albumDate.format("L")}">${albumDate.format(moment().diff(albumDate, "months") <= 6 ? "MMM YYYY" : "YYYY")}</span></span>`;
albumInfoSpan.innerHTML = `${albumLinkElem}${albumDateElem}`;
let genres = "";
if (!album_uri.includes("spotify:episode")) {
genres = await getGenres(artist_uri.replace("spotify:artist:", ""));
}
genreInfoSpan.innerHTML = /* html */ `
<span>
<span draggable="true">
<span draggable="false" dir="auto">${genres.join(", ")}</span>
</span>
</span>
`;
} else if (track.uri.includes("spotify:episode")) {
} else if (Spicetify.Player.data.track.uri.includes("spotify:episode")) {
// podcast
bgImage = bgImage.replace("spotify:image:", "https://i.scdn.co/image/");
albumInfoSpan.innerHTML = track.metadata.album_title;
genreInfoSpan.innerHTML = "";
} else if (track.metadata.is_local == "true") {
albumInfoSpan.innerHTML = Spicetify.Player.data.track.metadata.album_title;
} else if (Spicetify.Player.data.track.metadata.is_local == "true") {
// local file
albumInfoSpan.innerHTML = track.metadata.album_title;
genreInfoSpan.innerHTML = "";
} else if (track.provider == "ad") {
albumInfoSpan.innerHTML = Spicetify.Player.data.track.metadata.album_title;
} else if (Spicetify.Player.data.track.provider == "ad") {
// ad
albumInfoSpan.innerHTML = "Advertisement";
genreInfoSpan.innerHTML = "";
return;
} else {
// When clicking a song from the homepage, songChange is fired with half empty metadata
@ -825,13 +780,19 @@ Dribbblish.on("ready", () => {
$("html").css("--image-brightness", getImageLightness(img) / 255);
let color = "#509bf5";
if (img.complete) {
let color = "#1ed760";
const colorSelectionAlgorithm = Dribbblish.config.get("colorSelectionAlgorithm");
const colorSelectionMode = Dribbblish.config.get("colorSelectionMode");
let palette = {};
if (colorSelectionAlgorithm == "colorthief") {
if (colorSelectionAlgorithm == "spotify") {
const swatches = await Spicetify.colorExtractor(Spicetify.Player.data.track.uri);
for (const col of ["VIBRANT", "VIBRANT_NON_ALARMING", "PROMINENT", "DARK_VIBRANT", "LIGHT_VIBRANT", "DESATURATED"]) {
const c = chroma(swatches[col]);
palette[c.luminance()] = c;
}
} else if (colorSelectionAlgorithm == "colorthief") {
palette = Object.fromEntries([colorThief.getColor(img), ...colorThief.getPalette(img, 24, 5)].map((c) => chroma(c)).map((c) => [c.luminance(), c]));
} else if (colorSelectionAlgorithm == "vibrant") {
const swatches = await new Promise((resolve, reject) => new Vibrant(img, 5).getPalette().then(resolve).catch(reject));
@ -857,9 +818,9 @@ Dribbblish.on("ready", () => {
const wantedLuminance = $("html").css("--is_light") == "1" ? Dribbblish.config.get("lightModeLuminance") : Dribbblish.config.get("darkModeLuminance");
color = palette[getClosestToNum(Object.keys(palette), wantedLuminance)].hex();
}
updateColors(false, color);
}
updateColors(false, color);
}
var coverListener;
@ -891,7 +852,7 @@ Dribbblish.on("ready", () => {
fetch("https://api.github.com/repos/JulienMaille/dribbblish-dynamic-theme/releases/latest")
.then((response) => response.json())
.then((data) => {
Dribbblish.info.set("dribbblish-update", data.tag_name > process.env.DRIBBBLISH_VERSION ? { text: `v${data.tag_name}`, tooltip: "New Dribbblish version available", icon: "palette", onClick: () => window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme/releases/latest", "_blank") } : null);
Dribbblish.info.set("dribbblish-update", data.tag_name > process.env.DRIBBBLISH_VERSION ? { text: `v${data.tag_name}`, tooltip: "Nev Dribbblish version available", icon: "palette", onClick: () => window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme/releases/latest", "_blank") } : null);
})
.catch(console.error);

View file

@ -3,7 +3,7 @@ $colors: (
subtext: #f0f0f0,
sidebar-text: #ffffff,
main: #000000,
sidebar: #121212,
sidebar: #1ed760,
player: #000000,
card: #000000,
shadow: #202020,
@ -44,3 +44,16 @@ $props-to-transition: ("sidebar", "main", "text", "button");
transition: all var(--song-transition-speed) linear;
transition-property: $props;
}
// $key: the key of the color, e.g. "main"
// $alpha: tha alpha in rgba()
// $light-offset: added when in light mode
@function spiceColor($key, $alpha: 1, $light-offset: 0) {
@if $alpha == 1 {
@return var(--spice-#{$key});
} @else if $light-offset == 0 {
@return rgba(var(--spice-rgb-#{$key}), $alpha);
} @else {
@return rgba(var(--spice-rgb-#{$key}), lightOffset($alpha, $light-offset));
}
}

View file

@ -23,13 +23,16 @@
z-index: 1;
position: relative;
width: clamp(500px, 50%, 650px);
background-color: spiceColor("main", 0.95);
backdrop-filter: blur(3px);
padding: 20px 15px;
border-radius: var(--main-corner-radius);
display: flex;
gap: 8px;
flex-direction: column;
align-items: center;
justify-content: center;
@include spiceGlass();
@include spiceShadow();
> h2 {
font-size: 32px;

View file

@ -1,6 +1,13 @@
.connect-device-list-container {
$border-color: spiceColor("subtext", 0.1, 0.1);
border: 1px solid $border-color;
border-radius: var(--main-corner-radius);
padding: 8px;
@include spiceGlass(lightOffset(0.8, -0.1));
background-color: spiceColor("main", 0.75, -0.1) !important;
backdrop-filter: blur(10px);
border-radius: var(--main-corner-radius);
@include spiceShadow();
&::before {
display: none;

View file

@ -1,7 +1,8 @@
.main-contextMenu-menu {
position: relative;
overflow: visible;
$border-color: spiceColor("subtext", 0.1, 0.1);
background-color: transparent !important;
border: 1px solid $border-color;
border-radius: var(--main-corner-radius);
padding: 8px;
@ -10,7 +11,10 @@
content: "";
position: absolute;
inset: 0px;
@include spiceGlass(lightOffset(0.8, -0.1));
background-color: spiceColor("main", 0.75, -0.1) !important;
backdrop-filter: blur(10px);
border-radius: var(--main-corner-radius);
@include spiceShadow();
}
.main-contextMenu-menuItem {
@ -28,7 +32,7 @@
&::after {
left: 8px;
right: 8px;
border-color: spiceColor("subtext", 0.1, 0.1);
border-color: $border-color;
}
}
}

View file

@ -1,32 +0,0 @@
.main-topBar-topbarContent {
height: 100%;
& > nav[class*="-tabBar"][class*="-tabBar-nav"] {
height: 100%;
& > ul {
display: flex;
gap: 8px;
height: 100%;
li[class*="-tabBar-headerItem"] {
height: 100%;
& > a {
display: flex;
align-items: center;
justify-content: center;
margin: 0px;
height: 100%;
padding: 0px 8px;
border-radius: var(--sidebar-icons-border-radius);
& > span {
line-height: 13px;
@include spiceFont("glue", 14px, "Medium");
}
}
}
}
}
}

View file

@ -1,7 +0,0 @@
svg[icon-type="dribbblish"] {
overflow: visible;
// &[icon-style="custom"] {}
// &[icon-style^="material:"] {}
}

View file

@ -1,4 +1,4 @@
.main-button {
button.main-button {
&-primary {
$color: spiceColor("subtext");
color: $color;
@ -30,8 +30,7 @@
// }
// }
// ? the `:not(...)` is to fix #137
&-tertiary:not(.main-entityHeader-titleButton) {
&-tertiary {
$color: spiceColor("subtext");
color: $color;
background-color: spiceColor("selected-row", 0.2, 0.05) !important;

73
src/styles/Loader.scss Normal file
View file

@ -0,0 +1,73 @@
// From https://codepen.io/mrrocks/pen/EiplA
@use "sass:math";
$offset: 187;
$duration: 1.4s;
#dribbblish-loader {
z-index: 999999;
position: fixed;
inset: 0px;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
opacity: 0;
transition: opacity 1s ease-in;
&[active] {
opacity: 1;
pointer-events: all;
}
&::before {
content: "";
position: absolute;
inset: 0px;
background-color: spiceColor("main", 0.9, -0.1);
backdrop-filter: blur(10px);
}
&::after {
content: attr(text);
position: absolute;
bottom: 40%;
color: spiceColor("subtext");
@include spiceFont("glue", 32px, "Bold");
}
svg {
animation: rotator $duration linear infinite;
circle {
stroke: spiceColor("sidebar");
stroke-dasharray: $offset;
stroke-dashoffset: 0;
transform-origin: center;
animation: dash $duration ease-in-out infinite;
}
}
}
@keyframes rotator {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(270deg);
}
}
@keyframes dash {
0% {
stroke-dashoffset: $offset;
}
50% {
stroke-dashoffset: math.div($offset, 4);
transform: rotate(135deg);
}
100% {
stroke-dashoffset: $offset;
transform: rotate(450deg);
}
}

View file

@ -1 +0,0 @@
// TODO: improve default lyrics styles when the classes are mapped `see: https://github.com/JulienMaille/dribbblish-dynamic-theme/issues/144`

View file

@ -5,12 +5,15 @@
.GenericModal {
z-index: 1;
position: relative;
background-color: spiceColor("main", 0.95);
backdrop-filter: blur(3px);
padding: 24px 16px;
border-radius: var(--main-corner-radius);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
@include spiceGlass();
@include spiceShadow();
// Popups (`Spicetify.PopupModal.display()`)
generic-modal & {
@ -27,40 +30,34 @@
}
}
.main- {
&trackCreditsModal,
&embedWidgetGenerator {
&-container {
display: flex;
gap: 16px;
background-color: transparent;
.main-trackCreditsModal-container {
display: flex;
gap: 16px;
background-color: transparent;
width: 100%;
.main-trackCreditsModal-header {
display: flex;
gap: 16px;
padding: 0px;
width: 100%;
border: none;
h1 {
text-align: center;
width: 100%;
box-shadow: none;
@include spiceFont("glue", 2em, "Bold");
}
&-header {
display: flex;
gap: 16px;
padding: 0px;
width: 100%;
border: none;
h1 {
text-align: center;
width: 100%;
@include spiceFont("glue", 2em, "Bold");
}
button {
position: absolute;
top: 16px;
right: 16px;
}
button {
position: absolute;
top: 16px;
right: 16px;
}
}
&-mainSection {
padding: 0px 26px;
}
.main-trackCreditsModal-mainSection {
padding: 0px 26px;
}
}
}

View file

@ -1,31 +1,17 @@
// Hide ads
#main[hide-ads~="ads"] {
// Remove ad placeholder in main screen
.main-leaderboardComponent-container {
display: none;
}
}
// Hide premium features
#main[hide-ads~="premium"] {
// Remove upgrade button
#main[hide-ads="true"] {
/* Remove upgrade button*/
.main-topBar-UpgradeButton {
display: none;
}
// Remove upgrade to premium button in user menu
// prettier-ignore
.main-contextMenu-menuItemButton[href="https://www.spotify.com/premium/"] {
/* Remove upgrade to premium button in user menu */
.main-contextMenu-menuItemButton[href="https://www.spotify.com/premium/"]
{
display: none;
}
// Hide fullscreen button
.volume-bar + .control-button {
display: none;
}
// Hide download buttons
.main-actionBar-ActionBarRow button[role="switch"] {
/* Remove ad placeholder in main screen */
.main-leaderboardComponent-container {
display: none;
}
}

View file

@ -1,42 +0,0 @@
#dribbblish-overlay {
z-index: 999999;
position: fixed;
inset: 0px;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
opacity: 0;
transition: opacity 1s ease-in;
color: spiceColor("subtext");
@include spiceFont("glue", 32px, "Bold");
&[active] {
opacity: 1;
pointer-events: all;
}
&::before {
z-index: -1;
content: "";
position: absolute;
inset: 0px;
background-color: spiceColor("main", 0.9, -0.1);
backdrop-filter: blur(10px);
}
& > div {
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
& > * {
color: var(--color);
&:empty {
display: none;
}
}
}
}

50
src/styles/Popup.scss Normal file
View file

@ -0,0 +1,50 @@
.GenericModal__overlay {
backdrop-filter: blur(3px) brightness(60%);
background-color: transparent;
.GenericModal {
z-index: 1;
position: relative;
width: clamp(500px, 50%, 650px);
background-color: spiceColor("main", 0.95);
backdrop-filter: blur(3px);
padding: 24px 16px;
border-radius: var(--main-corner-radius);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
@include spiceShadow();
.main-trackCreditsModal-container {
display: flex;
gap: 16px;
background-color: transparent;
width: 100%;
.main-trackCreditsModal-header {
display: flex;
gap: 16px;
padding: 0px;
width: 100%;
border: none;
h1 {
text-align: center;
width: 100%;
@include spiceFont("glue", 2em, "Bold");
}
button {
position: absolute;
top: 16px;
right: 16px;
}
}
.main-trackCreditsModal-mainSection {
padding: 0px 26px;
}
}
}
}

View file

@ -3,14 +3,11 @@
@return #{"invert("}$v#{")"};
}
// returns $n and adds $offset when the theme is light
@function lightOffset($n, $offset) {
@return calc($n + $offset * var(--is_light));
}
@mixin spiceShadow() {
box-shadow: 0px 0px 8px spiceColor("subtext", 0.1, 0.1);
}
@mixin spiceGlass($opacity: 0.95) {
background-color: spiceColor("main", $opacity);
backdrop-filter: blur(10px);
border: 1px solid spiceColor("subtext", 0.1, 0.1);
border-radius: var(--main-corner-radius);
@include spiceShadow();
}

View file

@ -7,12 +7,9 @@
@import "NoAds";
@import "Markdown";
@import "Info";
@import "Loader";
@import "Modals";
@import "ConnectDeviceList";
@import "Lyrics";
@import "Icons";
@import "Overlay";
@import "CustomAppTabBar";
:root {
--bar-height: 70px;
@ -22,7 +19,7 @@
--scrollbar-vertical-size: 8px;
--cover-border-radius: 8px;
--playbar-movement-anim-speed: 0.5s;
--image-radius: 4px;
--image-radius: 10px;
--sidebar-icons-border-radius: 50vh; // 50vh = round / pill
--song-transition-speed: 3s;
--is_dark: calc(1 - var(--is_light));
@ -135,27 +132,13 @@
box-shadow: 0 4px 20px #21212130;
}
.main-trackList-rowMarker {
color: spiceColor("text");
}
.main-trackList-playingIcon {
-webkit-mask-image: url(icon64("equaliser", '{"size": 14}'));
background-color: currentColor;
content-visibility: hidden;
image-rendering: pixelated;
}
.main-trackList-trackListRowGrid {
background-color: var(--spice-main);
}
.main-trackList-active {
background-color: spiceColor("selected-row", 0.25) !important;
filter: grayscale(1);
}
.main-trackList-trackListRow:hover {
background-color: spiceColor("selected-row", 0.15) !important;
background-color: spiceColor("selected-row", 0.2) !important;
}
.main-trackList-trackListRow:focus-within,
@ -170,11 +153,6 @@ span.artist-artistVerifiedBadge-badge svg > path:last-of-type {
fill: spiceColor("text");
}
/* Playlist text color */
.main-entityHeader-subtitle.main-entityHeader-gra {
color: spiceColor("subtext");
}
/* Full window artist background */
.main-entityHeader-background.main-entityHeader-gradient {
opacity: 0.3;
@ -576,7 +554,6 @@ html.sidebar-hide-text .GlueDropTarget span {
.progress-bar {
--progress-bar-height: 2px;
--is-active-fg-color: #{spiceColor("button-active")};
--fg-color: #{spiceColor("button")};
--bg-color: #{spiceColor("text", 0.2)};
}
@ -614,10 +591,14 @@ html.sidebar-hide-text .GlueDropTarget span {
transform: translateX(calc(-50%));
padding: 0 5px;
text-align: center;
white-space: nowrap;
pointer-events: none;
border: 1px solid spiceColor("subtext", 0.1, 0.1);
border-radius: var(--main-corner-radius);
color: spiceColor("subtext");
@include spiceGlass();
background-color: spiceColor("main", 0.75, -0.1) !important;
backdrop-filter: blur(10px);
border-radius: var(--main-corner-radius);
@include spiceShadow();
}
.minimal-player .player-controls__buttons {
@ -672,13 +653,9 @@ html.sidebar-hide-text .GlueDropTarget span {
}
#main-trackInfo-year {
#main[playbar-album-info="false"] & {
display: none;
}
}
display: block;
#main-trackInfo-genre {
#main[playbar-genre-info="false"] & {
#main[playbar-album-info="false"] & {
display: none;
}
}
@ -821,10 +798,6 @@ li.GlueDropTarget {
gap: 8px;
max-width: unset;
padding: 16px 32px !important;
& > * {
height: 100%;
}
}
/** Custom elements */
@ -1293,14 +1266,13 @@ html.right-expanded-cover.buddyfeed-visible .main-coverSlotExpanded-container {
}
}
// Fix main-view-containers having a scrollbar when top-bar is `solid` or `transparent`
// Fix lyrics-plus having a scrollbar when top-bar is `solid` or `transparent`
.lyrics-lyricsContainer-LyricsContainer {
height: 100% !important;
}
.main-view-container__scroll-node-child {
height: 100%;
padding-bottom: 0px;
& > * {
height: 100% !important;
}
}
// Hide default Spotify "Offline" notice
@ -1308,12 +1280,8 @@ html.right-expanded-cover.buddyfeed-visible .main-coverSlotExpanded-container {
display: none;
}
// Notifications
.main-notificationBubbleContainer-NotificationBubbleContainer {
.main-notificationBubble-NotificationBubble {
color: spiceColor("subtext");
@include spiceGlass();
}
svg[type="dribbblish-icon"] {
overflow: visible;
}
// ! WORKAROUNDS / TEMP FIXES
@ -1333,20 +1301,3 @@ canvas[width="250"][height="250"] {
display: flex;
justify-content: center;
}
// fix albumn name font size (#171)
.main-trackInfo-release {
font-size: 10px;
}
// fix play buttons color going green (#174)
.encore-bright-accent-set {
--background-highlight: var(--spice-button) !important;
--background-press: var(--spice-button-active) !important;
color: var(--spice-sidebar-text);
}
// hide the triangle under the connect button
.control-button--active::after {
display: none;
}

View file

@ -1,4 +1,4 @@
spicetify config current_theme "SpicetifyDefault" extensions dribbblish-dynamic.js-
spicetify config current_theme " " extensions dribbblish-dynamic.js-
$spicePath = spicetify -c | Split-Path
$configFile = Get-Content "$spicePath\config-xpui.ini"

View file

@ -6,7 +6,7 @@ set -e
echo "UN-INSTALLING"
cd "$(dirname "$(spicetify -c)")"
spicetify config current_theme "SpicetifyDefault" extensions dribbblish-dynamic.js-
spicetify config current_theme " " extensions dribbblish-dynamic.js-
echo "UN-PATCHING"
if cat config-xpui.ini | grep -o '\[Patch\]'; then

View file

@ -1,36 +1,26 @@
import webpack from "webpack";
import sass from "sass";
import { CleanWebpackPlugin } from "clean-webpack-plugin";
import path from "path";
import fs from "fs";
const webpack = require("webpack");
const sass = require("sass");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const path = require("path");
const fs = require("fs");
import Icons from "./src/js/Icons";
import { lightOffset, spiceColor } from "./src/js/SassUtil";
const _icons = {};
function addIcon(name, style, path) {
name = name.replace(/_/g, "-");
if (!_icons.hasOwnProperty(name)) _icons[name] = {};
_icons[name][style] = fs.readFileSync(path, { encoding: "utf8" });
}
const icons = {};
// Add Material Icons
let iconDir = path.resolve(__dirname, "node_modules/@material-icons/svg/svg");
for (const dir of fs.readdirSync(iconDir)) {
icons[dir.replace("_", "-")] = {};
for (const file of fs.readdirSync(path.resolve(iconDir, dir))) {
addIcon(dir, `material:${file.replace(/\..*?$/, "")}`, path.resolve(iconDir, dir, file));
icons[dir.replace("_", "-")][file.replace(/\..*?$/, "")] = fs.readFileSync(path.resolve(iconDir, dir, file), { encoding: "utf8" });
}
}
// Add Custom Icons
iconDir = path.resolve(__dirname, "src/icons");
for (const icon of fs.readdirSync(iconDir)) {
addIcon(icon.replace(/\..*?$/, ""), "custom", path.resolve(iconDir, icon));
icons[icon.replace(/\..*?$/, "")] = fs.readFileSync(path.resolve(iconDir, icon), { encoding: "utf8" });
}
const icons = new Icons(_icons);
/** @type {import('webpack').Configuration} */
const config = {
mode: "development",
module.exports = {
entry: [path.resolve(__dirname, "src/js/main.js"), path.resolve(__dirname, "src/styles/main.scss"), path.resolve(__dirname, "src/styles/Colors.scss")],
output: {
path: path.resolve(__dirname, "dist"),
@ -56,20 +46,9 @@ const config = {
sourceMap: true,
sassOptions: {
functions: {
"lightOffset($n, $offset)": (n, offset) => {
return new sass.types.String(lightOffset(n.getValue(), offset.getValue()));
},
"spiceColor($key, $alpha: 1, $light-offset: 0)": (key, alpha, _lightOffset) => {
return new sass.types.String(spiceColor(key.getValue(), alpha.getValue(), _lightOffset.getValue()));
},
"font64($font)": (font) => {
const file = path.resolve(__dirname, "src/fonts", font.getValue());
return new sass.types.String(`"data:font/truetype;charset=utf-8;base64,${fs.readFileSync(file, { encoding: "base64" })}"`);
},
'icon64($name, $options: "{}")': (name, options) => {
name = name.getValue();
options = JSON.parse(options.getValue());
return new sass.types.String(icons.get(name, { ...options, base64: true }));
}
}
}
@ -96,7 +75,7 @@ const config = {
.replace(/^---(.*?\r?\n)+---(.*?\r?\n)*?(?=\S)/, "") // Replace GitHub header
.replace(/\*\*Desktop Setup\*\*\r?\n(- .*?\r?\n)+\r?\n/, "") // Replace **Desktop Setup** block
),
"process.env.DRIBBBLISH_ICONS": JSON.stringify(_icons),
"process.env.DRIBBBLISH_ICONS": JSON.stringify(icons),
"process.env.DRIBBBLISH_VERSION": JSON.stringify(process.env.DRIBBBLISH_VERSION ?? "Dev"),
"process.env.COMMIT_HASH": JSON.stringify(process.env.COMMIT_HASH ?? "local")
}),
@ -106,5 +85,3 @@ const config = {
})
]
};
export default config;