diff --git a/CHANGELOG.md b/CHANGELOG.md index 541b013..737c58a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ Added: - 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) Fixed: - Checking for update every 10 Minutes not working diff --git a/src/icons/times.svg b/src/icons/times.svg new file mode 100644 index 0000000..5c47bc2 --- /dev/null +++ b/src/icons/times.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/js/Util.js b/src/js/Util.js index f5b04d7..4fdb087 100644 --- a/src/js/Util.js +++ b/src/js/Util.js @@ -74,3 +74,19 @@ export function getRandomInt(min, max) { export function randomFromArray(arr) { return arr[Math.floor(Math.random() * arr.length)]; } + +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); + }; +} diff --git a/src/js/main.js b/src/js/main.js index 95fea15..d32ad23 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -4,7 +4,7 @@ import chroma from "chroma-js"; import $ from "jquery"; import moment from "moment"; -import { waitForElement, copyToClipboard, capitalizeFirstLetter, getClosestToNum, randomFromArray } from "./Util"; +import { waitForElement, copyToClipboard, capitalizeFirstLetter, getClosestToNum, randomFromArray, debounce } from "./Util"; import { default as _Dribbblish } from "./Dribbblish"; import "./Folders"; @@ -53,6 +53,44 @@ Dribbblish.on("ready", () => { }) }); + Dribbblish.config.register({ + type: "checkbox", + key: "showSearchBox", + name: "Show Search Box", + description: "Show an search box in the top bar. Just like the good old times", + defaultValue: true, + onChange: (val) => $("#main").attr("search-box", val ? "" : null), + onAppended: () => { + const input = document.createElement("input"); + input.id = "dribbblish-search-box"; + input.type = "search"; + input.placeholder = "Search"; + input.setAttribute("maxlength", "80"); + input.setAttribute("autocorrect", "off"); + input.setAttribute("autocapitalize", "off"); + input.setAttribute("spellcheck", "false"); + input.addEventListener("click", (e) => { + if (!Spicetify.Platform.History.location.pathname.startsWith("/search")) Spicetify.Platform.History.push(`/search/${input.value}`); + waitForElement([`[data-testid="search-input"]`], ([defaultSearch]) => { + input.focus(); + }); + }); + input.addEventListener( + "input", + debounce((e) => { + if (Spicetify.Platform.History.location.pathname.startsWith("/search")) { + Spicetify.Platform.History.replace(`/search/${input.value}`); + } else { + Spicetify.Platform.History.push(`/search/${input.value}`); + } + input.focus(); + }, 250) + ); + + $(".main-topBar-historyButtons").append(input); + } + }); + Dribbblish.config.register({ type: "checkbox", key: "rightBigCover", @@ -224,17 +262,7 @@ Dribbblish.on("ready", () => { } }); - (function Dribbblish() { - const progBar = document.querySelector(".playback-bar"); - const root = document.querySelector(".Root"); - - if (!Spicetify.Player.origin || !progBar || !root) { - setTimeout(Dribbblish, 300); - return; - } - - const progKnob = progBar.querySelector(".progress-bar__slider"); - + waitForElement([".Root", ".playback-bar .progress-bar__slider"], ([root, progKnob]) => { const tooltip = document.createElement("div"); tooltip.className = "prog-tooltip"; progKnob.append(tooltip); @@ -263,7 +291,7 @@ Dribbblish.on("ready", () => { root.classList.remove("is-connectBarVisible"); } }); - })(); + }); /* Config settings */ @@ -477,10 +505,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 - **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) => { @@ -504,8 +533,9 @@ Dribbblish.on("ready", () => { name: "Color Selection Mode", description: ` Method of selecting colors from the albumart - - **Default:** Choose closest matching{.muted} - - **Luminance:** Choose matching current theme (lighter/darker){.muted} + - **Default:** Choose closest matching + - **Luminance:** Choose matching current theme (lighter/darker) + {.muted} `, data: { default: "Default", luminance: "Luminance" }, defaultValue: "default", @@ -698,11 +728,17 @@ Dribbblish.on("ready", () => { 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)); - for (var col of ["Vibrant", "DarkVibrant", "Muted", "LightVibrant"]) { + for (const col of ["Vibrant", "DarkVibrant", "Muted", "LightVibrant"]) { if (swatches[col]) { const c = chroma(swatches[col].getHex()); palette[c.luminance()] = c; diff --git a/src/styles/Info.scss b/src/styles/Info.scss index 16a5a68..bfe3284 100644 --- a/src/styles/Info.scss +++ b/src/styles/Info.scss @@ -1,7 +1,6 @@ #dribbblish-info-container { display: flex; gap: 8px; - margin-right: 8px; height: 100%; &:empty { diff --git a/src/styles/Inputs.scss b/src/styles/Inputs.scss index 2a0bed7..b6be4db 100644 --- a/src/styles/Inputs.scss +++ b/src/styles/Inputs.scss @@ -52,6 +52,11 @@ input { &:active { background-color: spiceColor("selected-row", 0.6) !important; } + + &::placeholder { + color: spiceColor("subtext"); + opacity: lightOffset(0.8, 0.1); + } } textarea { @@ -128,12 +133,32 @@ input { &[type="number"], &[type="text"], + &[type="search"], &[type="time"] { height: 32px; border-radius: 4px !important; padding: 0px 10px; } + &[type="search"] { + &::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 1em; + width: 1em; + border-radius: 50em; + background: url(icon64("times")) no-repeat 50% 50%; + background-size: contain; + opacity: 0; + pointer-events: none; + filter: invert(lightOffset(1, -1)); + } + + &:focus::-webkit-search-cancel-button { + opacity: lightOffset(0.8, 0.1); + pointer-events: all; + } + } + &[type="time"] { &::-webkit-calendar-picker-indicator { filter: invert(var(--is_dark)); diff --git a/src/styles/main.scss b/src/styles/main.scss index d829d41..4770dbc 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -70,6 +70,24 @@ color: spiceColor("text") !important; } +#dribbblish-search-box { + display: none; + order: 99; + height: 28px; + border-radius: var(--sidebar-icons-border-radius) !important; + @include spiceFont("info", 14px, "Regular"); +} + +#main[search-box] { + #dribbblish-search-box { + display: block; + } + + [data-testid="search-input"] { + display: none; + } +} + .main-home-homeHeader, .x-entityHeader-overlay, .x-actionBarBackground-background, @@ -468,11 +486,17 @@ html.sidebar-hide-text .GlueDropTarget span { display: none; } -/** */ -.main-topBar-historyButtons .main-topBar-button { - background-color: unset; - width: 24px; - height: 24px; +.main-topBar-historyButtons { + display: flex; + align-items: center; + gap: 8px; + + .main-topBar-button { + margin: 0px; + background-color: unset; + width: 24px; + height: 24px; + } } .main-topBar-historyButtons svg { @@ -743,6 +767,8 @@ li.GlueDropTarget { } .main-topBar-container { + display: flex; + gap: 8px; max-width: unset; padding: 16px 32px !important; } @@ -992,7 +1018,7 @@ span.main-userWidget-displayName, left: -200%; opacity: 0.4; background-color: black; - border-radius: var(--image-radius); + border-radius: var(--sidebar-icons-border-radius); pointer-events: none; transition: all calc(var(--sidebar-icons-hover-animation) * 0.2s) ease; transition-property: left, opacity; diff --git a/webpack.config.js b/webpack.config.js index af818de..1fcf501 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,6 +3,7 @@ const sass = require("sass"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const path = require("path"); const fs = require("fs"); +const iconLoader = require("./src/loaders/icon-loader"); /** @type {import('webpack').Configuration} */ module.exports = { @@ -37,6 +38,12 @@ module.exports = { sourceMap: true, sassOptions: { functions: { + 'icon64($icon, $query: "")': (icon, query) => { + query = new URLSearchParams(query); + query.set("base64", ""); + const content = fs.readFileSync(path.resolve(__dirname, "./src/icons", `${icon.getValue()}.svg`), "utf8"); + return new sass.types.String(`"${iconLoader.call({ resourceQuery: query.toString() }, content)}"`); + }, "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" })}"`);