forked from SpoTUI/SpoTUI
refactor(theme): refactor theme.js
- Added runPlayerAction wrapper function to eliminate duplicate Spicetify player command try/catches. - Added runGlitchByDist helper function to dry up distance calculation logic across glitch animations (burstGlitch, pulseGlitch, implosionGlitch, fuzzWaveGlitch).
This commit is contained in:
@@ -43,7 +43,10 @@ const style = `
|
|||||||
transition: top 0.5s cubic-bezier(0.4, 0, 0.2, 1), transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: top 0.5s cubic-bezier(0.4, 0, 0.2, 1), transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.spotui-lyrics-panel #spotui-logo {
|
body.spotui-lyrics-panel #spotui-logo,
|
||||||
|
body.spotui-playlist-panel #spotui-logo,
|
||||||
|
body.spotui-help-panel #spotui-logo,
|
||||||
|
body.spotui-about-panel #spotui-logo {
|
||||||
top: 12px;
|
top: 12px;
|
||||||
transform: translate(-50%, 0) scale(0.6);
|
transform: translate(-50%, 0) scale(0.6);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
@@ -103,21 +106,13 @@ body.spotui-lyrics-panel #spotui-top-fade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
.spotui-ascii-grid {
|
.spotui-ascii-grid, .spotui-ascii-char {
|
||||||
font-size: clamp(5px, 1.1vw, 11px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.spotui-ascii-char {
|
|
||||||
font-size: clamp(5px, 1.1vw, 11px);
|
font-size: clamp(5px, 1.1vw, 11px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 450px) {
|
@media (max-width: 450px) {
|
||||||
.spotui-ascii-grid {
|
.spotui-ascii-grid, .spotui-ascii-char {
|
||||||
font-size: clamp(3.5px, 1.4vw, 7px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.spotui-ascii-char {
|
|
||||||
font-size: clamp(3.5px, 1.4vw, 7px);
|
font-size: clamp(3.5px, 1.4vw, 7px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +132,11 @@ body.spotui-lyrics-panel #spotui-top-fade {
|
|||||||
transition: opacity 260ms ease, transform 260ms ease;
|
transition: opacity 260ms ease, transform 260ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.spotui-command-mode #spotui-output {
|
body.spotui-command-mode #spotui-output,
|
||||||
|
body.spotui-playlist-panel #spotui-output,
|
||||||
|
body.spotui-help-panel #spotui-output,
|
||||||
|
body.spotui-about-panel #spotui-output,
|
||||||
|
body.spotui-lyrics-panel #spotui-output {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +144,12 @@ body.spotui-cli-mode #spotui-output {
|
|||||||
display: flex !important;
|
display: flex !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#spotui-output::-webkit-scrollbar {
|
#spotui-output::-webkit-scrollbar,
|
||||||
|
#spotui-help-panel::-webkit-scrollbar,
|
||||||
|
#spotui-about-panel::-webkit-scrollbar,
|
||||||
|
#spotui-playlist-list::-webkit-scrollbar,
|
||||||
|
#spotui-song-list::-webkit-scrollbar,
|
||||||
|
.spotui-lyrics-lines::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
@@ -178,10 +182,6 @@ body.spotui-cli-mode #spotui-output {
|
|||||||
.result { padding: 5px; }
|
.result { padding: 5px; }
|
||||||
.selected { background: #ff8c42; color: #000; }
|
.selected { background: #ff8c42; color: #000; }
|
||||||
|
|
||||||
body.spotui-lyrics-panel #spotui-output {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.spotui-lyrics-panel #spotui-logo {
|
body.spotui-lyrics-panel #spotui-logo {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
}
|
}
|
||||||
@@ -257,11 +257,6 @@ body.spotui-lyrics-panel #spotui-lyrics.spotui-lyrics-active {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spotui-lyrics-lines::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spotui-lyrics-fade {
|
.spotui-lyrics-fade {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -333,19 +328,7 @@ body.spotui-playlist-panel #spotui-playlist-panel {
|
|||||||
transition-delay: 0.6s;
|
transition-delay: 0.6s;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.spotui-playlist-panel #spotui-logo {
|
#spotui-help-panel, #spotui-about-panel {
|
||||||
top: 12px;
|
|
||||||
transform: translate(-50%, 0) scale(0.6);
|
|
||||||
opacity: 0.8;
|
|
||||||
z-index: 2;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.spotui-playlist-panel #spotui-output, body.spotui-help-panel #spotui-output, body.spotui-about-panel #spotui-output {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#spotui-help-panel {
|
|
||||||
display: none;
|
display: none;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -360,56 +343,11 @@ body.spotui-playlist-panel #spotui-output, body.spotui-help-panel #spotui-output
|
|||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#spotui-help-panel::-webkit-scrollbar {
|
body.spotui-help-panel #spotui-help-panel,
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.spotui-help-panel #spotui-help-panel {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.spotui-help-panel #spotui-logo {
|
|
||||||
top: 12px;
|
|
||||||
transform: translate(-50%, 0) scale(0.6);
|
|
||||||
opacity: 0.8;
|
|
||||||
z-index: 2;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#spotui-about-panel {
|
|
||||||
display: none;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 20px;
|
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
margin: 33vh 5vw 8px;
|
|
||||||
height: 60vh;
|
|
||||||
border: 1px solid #ff8c42;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#spotui-about-panel::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.spotui-about-panel #spotui-about-panel {
|
body.spotui-about-panel #spotui-about-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.spotui-about-panel #spotui-logo {
|
|
||||||
top: 12px;
|
|
||||||
transform: translate(-50%, 0) scale(0.6);
|
|
||||||
opacity: 0.8;
|
|
||||||
z-index: 2;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.help-item {
|
.help-item {
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -426,8 +364,6 @@ body.spotui-about-panel #spotui-logo {
|
|||||||
color: #b3b3b3;
|
color: #b3b3b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#spotui-playlist-list, #spotui-song-list {
|
#spotui-playlist-list, #spotui-song-list {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -443,11 +379,6 @@ body.spotui-about-panel #spotui-logo {
|
|||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#spotui-playlist-list::-webkit-scrollbar, #spotui-song-list::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-item, .song-item {
|
.playlist-item, .song-item {
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -458,7 +389,6 @@ body.spotui-about-panel #spotui-logo {
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.spotui-lyrics-lines.unsynced .spotui-lyrics-line {
|
.spotui-lyrics-lines.unsynced .spotui-lyrics-line {
|
||||||
color: #b3b3b3;
|
color: #b3b3b3;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
@@ -679,7 +609,7 @@ function initAsciiAnimation() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function burstGlitch(duration = 800) {
|
async function runGlitchByDist(duration, logic) {
|
||||||
const centerRow = Math.floor(rows / 2);
|
const centerRow = Math.floor(rows / 2);
|
||||||
const centerCol = Math.floor(cols / 2);
|
const centerCol = Math.floor(cols / 2);
|
||||||
const withDist = charData.map((entry) => {
|
const withDist = charData.map((entry) => {
|
||||||
@@ -688,8 +618,14 @@ function initAsciiAnimation() {
|
|||||||
return { ...entry, dist: Math.sqrt(dr * dr + dc * dc) };
|
return { ...entry, dist: Math.sqrt(dr * dr + dc * dc) };
|
||||||
});
|
});
|
||||||
const maxDist = Math.max(...withDist.map((entry) => entry.dist), 1);
|
const maxDist = Math.max(...withDist.map((entry) => entry.dist), 1);
|
||||||
|
await logic(withDist, maxDist);
|
||||||
|
resetGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function burstGlitch(duration = 800) {
|
||||||
const chars = [...GLITCH_CHARS];
|
const chars = [...GLITCH_CHARS];
|
||||||
const steps = 8;
|
const steps = 8;
|
||||||
|
await runGlitchByDist(duration, async (withDist, maxDist) => {
|
||||||
for (let step = 0; step < steps; step += 1) {
|
for (let step = 0; step < steps; step += 1) {
|
||||||
const progress = step / steps;
|
const progress = step / steps;
|
||||||
withDist.forEach(({ el, original, color, dist }) => {
|
withDist.forEach(({ el, original, color, dist }) => {
|
||||||
@@ -697,8 +633,7 @@ function initAsciiAnimation() {
|
|||||||
const threshold = progress * 1.1;
|
const threshold = progress * 1.1;
|
||||||
if (norm < threshold + 0.12 && norm > threshold - 0.12) {
|
if (norm < threshold + 0.12 && norm > threshold - 0.12) {
|
||||||
if (Math.random() < 0.75) {
|
if (Math.random() < 0.75) {
|
||||||
const randIdx = Math.floor(Math.random() * chars.length);
|
el.textContent = chars[Math.floor(Math.random() * chars.length)];
|
||||||
el.textContent = chars[randIdx];
|
|
||||||
el.style.color = `hsl(${20 + Math.random() * 35}, 100%, ${50 + Math.random() * 30}%)`;
|
el.style.color = `hsl(${20 + Math.random() * 35}, 100%, ${50 + Math.random() * 30}%)`;
|
||||||
}
|
}
|
||||||
} else if (norm < threshold - 0.12) {
|
} else if (norm < threshold - 0.12) {
|
||||||
@@ -708,22 +643,14 @@ function initAsciiAnimation() {
|
|||||||
});
|
});
|
||||||
await sleep(Math.floor(duration / steps));
|
await sleep(Math.floor(duration / steps));
|
||||||
}
|
}
|
||||||
resetGrid();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pulseGlitch(duration = 1200) {
|
async function pulseGlitch(duration = 1200) {
|
||||||
const centerRow = Math.floor(rows / 2);
|
|
||||||
const centerCol = Math.floor(cols / 2);
|
|
||||||
const withDist = charData.map((entry) => {
|
|
||||||
const dr = entry.row - centerRow;
|
|
||||||
const dc = entry.col - centerCol;
|
|
||||||
return { ...entry, dist: Math.sqrt(dr * dr + dc * dc) };
|
|
||||||
});
|
|
||||||
const maxDist = Math.max(...withDist.map((entry) => entry.dist), 1);
|
|
||||||
const chars = [...GLITCH_CHARS];
|
const chars = [...GLITCH_CHARS];
|
||||||
const waves = 3;
|
const waves = 3;
|
||||||
const stepsPerWave = 10;
|
const stepsPerWave = 10;
|
||||||
|
await runGlitchByDist(duration, async (withDist, maxDist) => {
|
||||||
for (let wave = 0; wave < waves; wave += 1) {
|
for (let wave = 0; wave < waves; wave += 1) {
|
||||||
for (let step = 0; step < stepsPerWave; step += 1) {
|
for (let step = 0; step < stepsPerWave; step += 1) {
|
||||||
const progress = step / stepsPerWave;
|
const progress = step / stepsPerWave;
|
||||||
@@ -745,27 +672,18 @@ function initAsciiAnimation() {
|
|||||||
}
|
}
|
||||||
await sleep(40);
|
await sleep(40);
|
||||||
}
|
}
|
||||||
resetGrid();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function implosionGlitch(duration = 900) {
|
async function implosionGlitch(duration = 900) {
|
||||||
const centerRow = Math.floor(rows / 2);
|
|
||||||
const centerCol = Math.floor(cols / 2);
|
|
||||||
const withDist = charData.map((entry) => {
|
|
||||||
const dr = entry.row - centerRow;
|
|
||||||
const dc = entry.col - centerCol;
|
|
||||||
return { ...entry, dist: Math.sqrt(dr * dr + dc * dc) };
|
|
||||||
});
|
|
||||||
const maxDist = Math.max(...withDist.map((entry) => entry.dist), 1);
|
|
||||||
const chars = [...GLITCH_CHARS];
|
const chars = [...GLITCH_CHARS];
|
||||||
const steps = 10;
|
const steps = 10;
|
||||||
|
await runGlitchByDist(duration, async (withDist, maxDist) => {
|
||||||
withDist.forEach(({ el }) => {
|
withDist.forEach(({ el }) => {
|
||||||
const randIdx = Math.floor(Math.random() * chars.length);
|
const randIdx = Math.floor(Math.random() * chars.length);
|
||||||
el.textContent = chars[randIdx];
|
el.textContent = chars[randIdx];
|
||||||
el.style.color = `hsl(${20 + Math.random() * 35}, 100%, ${45 + Math.random() * 35}%)`;
|
el.style.color = `hsl(${20 + Math.random() * 35}, 100%, ${45 + Math.random() * 35}%)`;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let step = 0; step < steps; step += 1) {
|
for (let step = 0; step < steps; step += 1) {
|
||||||
const progress = step / steps;
|
const progress = step / steps;
|
||||||
const threshold = 1.0 - progress * 1.1;
|
const threshold = 1.0 - progress * 1.1;
|
||||||
@@ -778,7 +696,7 @@ function initAsciiAnimation() {
|
|||||||
});
|
});
|
||||||
await sleep(Math.floor(duration / steps));
|
await sleep(Math.floor(duration / steps));
|
||||||
}
|
}
|
||||||
resetGrid();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function spiralGlitch(duration = 1000) {
|
async function spiralGlitch(duration = 1000) {
|
||||||
@@ -815,18 +733,10 @@ function initAsciiAnimation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fuzzWaveGlitch(duration = 1000) {
|
async function fuzzWaveGlitch(duration = 1000) {
|
||||||
const centerRow = Math.floor(rows / 2);
|
|
||||||
const centerCol = Math.floor(cols / 2);
|
|
||||||
const withDist = charData.map((entry) => {
|
|
||||||
const dr = entry.row - centerRow;
|
|
||||||
const dc = entry.col - centerCol;
|
|
||||||
return { ...entry, dist: Math.sqrt(dr * dr + dc * dc) };
|
|
||||||
});
|
|
||||||
const maxDist = Math.max(...withDist.map((entry) => entry.dist), 1);
|
|
||||||
const chars = [...GLITCH_CHARS];
|
const chars = [...GLITCH_CHARS];
|
||||||
const steps = 20;
|
const steps = 20;
|
||||||
const bandWidth = 0.25;
|
const bandWidth = 0.25;
|
||||||
|
await runGlitchByDist(duration, async (withDist, maxDist) => {
|
||||||
for (let step = 0; step < steps; step += 1) {
|
for (let step = 0; step < steps; step += 1) {
|
||||||
const progress = step / steps;
|
const progress = step / steps;
|
||||||
const targetNorm = progress * 1.0;
|
const targetNorm = progress * 1.0;
|
||||||
@@ -844,7 +754,7 @@ function initAsciiAnimation() {
|
|||||||
});
|
});
|
||||||
await sleep(Math.floor(duration / steps));
|
await sleep(Math.floor(duration / steps));
|
||||||
}
|
}
|
||||||
resetGrid();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function staticGlitch(duration = 600) {
|
async function staticGlitch(duration = 600) {
|
||||||
@@ -928,45 +838,16 @@ function initAsciiAnimation() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stageBurst() {
|
async function stageBurst() { await burstGlitch(900); }
|
||||||
await burstGlitch(900);
|
async function stagePulse() { await pulseGlitch(1200); }
|
||||||
}
|
async function stageImplosion() { await implosionGlitch(900); }
|
||||||
|
async function stageSpiral() { await spiralGlitch(1000); }
|
||||||
async function stagePulse() {
|
async function stageFuzzWave() { await fuzzWaveGlitch(1000); }
|
||||||
await pulseGlitch(1200);
|
async function stageStatic() { await staticGlitch(700); }
|
||||||
}
|
async function stageHSlashDown() { await horizontalBand(1, 800); }
|
||||||
|
async function stageHSlashUp() { await horizontalBand(-1, 800); }
|
||||||
async function stageImplosion() {
|
async function stageVSlashRight() { await verticalSlice(1, 800); }
|
||||||
await implosionGlitch(900);
|
async function stageVSlashLeft() { await verticalSlice(-1, 800); }
|
||||||
}
|
|
||||||
|
|
||||||
async function stageSpiral() {
|
|
||||||
await spiralGlitch(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stageFuzzWave() {
|
|
||||||
await fuzzWaveGlitch(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stageStatic() {
|
|
||||||
await staticGlitch(700);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stageHSlashDown() {
|
|
||||||
await horizontalBand(1, 800);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stageHSlashUp() {
|
|
||||||
await horizontalBand(-1, 800);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stageVSlashRight() {
|
|
||||||
await verticalSlice(1, 800);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stageVSlashLeft() {
|
|
||||||
await verticalSlice(-1, 800);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stageFunctions = [
|
const stageFunctions = [
|
||||||
stageWaveDown,
|
stageWaveDown,
|
||||||
@@ -1008,7 +889,6 @@ function initAsciiAnimation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let tuiMode = "command";
|
let tuiMode = "command";
|
||||||
|
|
||||||
let results = [];
|
let results = [];
|
||||||
let selected = 0;
|
let selected = 0;
|
||||||
let lyricsObserver = null;
|
let lyricsObserver = null;
|
||||||
@@ -1036,8 +916,7 @@ function createCopyButton() {
|
|||||||
copyBtn.addEventListener("click", () => {
|
copyBtn.addEventListener("click", () => {
|
||||||
const output = document.getElementById("spotui-output");
|
const output = document.getElementById("spotui-output");
|
||||||
if (output) {
|
if (output) {
|
||||||
const text = output.innerText;
|
navigator.clipboard.writeText(output.innerText).then(() => {
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
|
||||||
copyBtn.textContent = "Copied!";
|
copyBtn.textContent = "Copied!";
|
||||||
setTimeout(() => { copyBtn.textContent = "Copy log"; }, 1500);
|
setTimeout(() => { copyBtn.textContent = "Copy log"; }, 1500);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
@@ -1076,17 +955,14 @@ function createCopyButton() {
|
|||||||
controls.appendChild(copyBtn);
|
controls.appendChild(copyBtn);
|
||||||
controls.appendChild(hideBtn);
|
controls.appendChild(hideBtn);
|
||||||
controls.appendChild(spotifyBtn);
|
controls.appendChild(spotifyBtn);
|
||||||
const footer = document.getElementById("spotui-footer");
|
(document.getElementById("spotui-footer") || document.body).appendChild(controls);
|
||||||
(footer || document.body).appendChild(controls);
|
|
||||||
|
|
||||||
const backBtn = document.createElement("button");
|
const backBtn = document.createElement("button");
|
||||||
backBtn.id = "spotui-back-btn";
|
backBtn.id = "spotui-back-btn";
|
||||||
backBtn.className = "spotui-control-btn";
|
backBtn.className = "spotui-control-btn";
|
||||||
backBtn.textContent = "Back";
|
backBtn.textContent = "Back";
|
||||||
backBtn.addEventListener("click", () => {
|
backBtn.addEventListener("click", () => {
|
||||||
document.body.classList.remove("spotui-search-mode");
|
document.body.classList.remove("spotui-search-mode", "spotui-spotify-enabled", "spotui-tui-hidden");
|
||||||
document.body.classList.remove("spotui-spotify-enabled");
|
|
||||||
document.body.classList.remove("spotui-tui-hidden");
|
|
||||||
spotifyBtn.textContent = "Enable Spotify";
|
spotifyBtn.textContent = "Enable Spotify";
|
||||||
hideBtn.textContent = "Hide TUI";
|
hideBtn.textContent = "Hide TUI";
|
||||||
syncLyricsState();
|
syncLyricsState();
|
||||||
@@ -1103,8 +979,9 @@ function detectLyricsSurface() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncLyricsState() {
|
function syncLyricsState() {
|
||||||
if (!document.body) return;
|
if (document.body) {
|
||||||
document.body.classList.toggle("spotui-lyrics-open", detectLyricsSurface());
|
document.body.classList.toggle("spotui-lyrics-open", detectLyricsSurface());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hookLyricsButton() {
|
function hookLyricsButton() {
|
||||||
@@ -1127,14 +1004,11 @@ function initLyricsBridge() {
|
|||||||
setTimeout(initLyricsBridge, 250);
|
setTimeout(initLyricsBridge, 250);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
hookLyricsButton();
|
hookLyricsButton();
|
||||||
syncLyricsState();
|
syncLyricsState();
|
||||||
};
|
};
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
if (!lyricsObserver) {
|
if (!lyricsObserver) {
|
||||||
lyricsObserver = new MutationObserver(refresh);
|
lyricsObserver = new MutationObserver(refresh);
|
||||||
lyricsObserver.observe(document.body, {
|
lyricsObserver.observe(document.body, {
|
||||||
@@ -1145,9 +1019,7 @@ function initLyricsBridge() {
|
|||||||
});
|
});
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"beforeunload",
|
"beforeunload",
|
||||||
() => {
|
() => { lyricsObserver?.disconnect(); },
|
||||||
lyricsObserver?.disconnect();
|
|
||||||
},
|
|
||||||
{ once: true }
|
{ once: true }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1206,7 +1078,6 @@ function createTerminal() {
|
|||||||
renderResults();
|
renderResults();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function print(text) {
|
function print(text) {
|
||||||
@@ -1219,6 +1090,15 @@ function print(text) {
|
|||||||
output.scrollTop = 0;
|
output.scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runPlayerAction(actionName, promiseFn, successMsg) {
|
||||||
|
try {
|
||||||
|
const res = await promiseFn();
|
||||||
|
print(successMsg ?? `Executed ${actionName}`);
|
||||||
|
} catch (err) {
|
||||||
|
print(`${actionName} error: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function execute(cmd) {
|
async function execute(cmd) {
|
||||||
const rawCmd = cmd.trim();
|
const rawCmd = cmd.trim();
|
||||||
const cleanedCmd = rawCmd.startsWith("/") || rawCmd.startsWith(".") ? rawCmd.slice(1).trim() : rawCmd;
|
const cleanedCmd = rawCmd.startsWith("/") || rawCmd.startsWith(".") ? rawCmd.slice(1).trim() : rawCmd;
|
||||||
@@ -1240,167 +1120,91 @@ async function execute(cmd) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "help") {
|
if (command === "help") { openHelpPanel(); return; }
|
||||||
openHelpPanel();
|
if (command === "about") { openAboutPanel(); return; }
|
||||||
return;
|
if (command === "clear") { document.getElementById("spotui-output").textContent = ""; return; }
|
||||||
}
|
if (command === "playlist" || command === "list") { openPlaylistPanel(); return; }
|
||||||
if (command === "about") {
|
|
||||||
openAboutPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (command === "clear") {
|
|
||||||
document.getElementById("spotui-output").textContent = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command === "playlist") {
|
|
||||||
openPlaylistPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command === "list") {
|
|
||||||
openPlaylistPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command === "play") {
|
if (command === "play") {
|
||||||
try {
|
await runPlayerAction("Play", async () => {
|
||||||
const wasPlaying = Spicetify.Player.isPlaying();
|
if (!Spicetify.Player.isPlaying()) Spicetify.Player.togglePlay();
|
||||||
if (!wasPlaying) {
|
}, "Playing");
|
||||||
Spicetify.Player.togglePlay();
|
|
||||||
}
|
|
||||||
print("Playing");
|
|
||||||
} catch (err) {
|
|
||||||
print("Play error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "pause") {
|
if (command === "pause") {
|
||||||
try {
|
await runPlayerAction("Pause", async () => {
|
||||||
const wasPlaying = Spicetify.Player.isPlaying();
|
if (Spicetify.Player.isPlaying()) Spicetify.Player.togglePlay();
|
||||||
if (wasPlaying) {
|
}, "Paused");
|
||||||
Spicetify.Player.togglePlay();
|
|
||||||
}
|
|
||||||
print("Paused");
|
|
||||||
} catch (err) {
|
|
||||||
print("Pause error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "p") {
|
if (command === "p") {
|
||||||
try {
|
await runPlayerAction("Play/PauseToggle", async () => {
|
||||||
const wasPlaying = Spicetify.Player.isPlaying();
|
const wasPlaying = Spicetify.Player.isPlaying();
|
||||||
Spicetify.Player.togglePlay();
|
Spicetify.Player.togglePlay();
|
||||||
print(wasPlaying ? "Paused" : "Playing");
|
return wasPlaying;
|
||||||
} catch (err) {
|
}, Spicetify.Player.isPlaying() ? "Paused" : "Playing");
|
||||||
print("Play/pause error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "search") {
|
if (command === "search") {
|
||||||
document.body.classList.add("spotui-search-mode");
|
document.body.classList.add("spotui-search-mode", "spotui-tui-hidden");
|
||||||
document.body.classList.add("spotui-tui-hidden");
|
|
||||||
syncLyricsState();
|
syncLyricsState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "skip") {
|
if (command === "skip") {
|
||||||
try {
|
await runPlayerAction("Skip", () => Spicetify.Player.next(), "Skipped to next track");
|
||||||
Spicetify.Player.next();
|
|
||||||
print("Skipped to next track");
|
|
||||||
} catch (err) {
|
|
||||||
print("Skip error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (command === "back") {
|
if (command === "back") {
|
||||||
try {
|
await runPlayerAction("Back", () => Spicetify.Player.back(), "Went back to previous track");
|
||||||
Spicetify.Player.back();
|
|
||||||
print("Went back to previous track");
|
|
||||||
} catch (err) {
|
|
||||||
print("Back error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "seek" || command === "s") {
|
if (command === "seek" || command === "s") {
|
||||||
try {
|
await runPlayerAction("Seek", () => {
|
||||||
if (!argText) {
|
if (!argText) throw new Error("Usage: seek <mm:ss>");
|
||||||
print("Usage: seek <mm:ss>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const parts = argText.split(':').map(Number);
|
const parts = argText.split(':').map(Number);
|
||||||
if (parts.length !== 2 || parts.some(isNaN)) {
|
if (parts.length !== 2 || parts.some(isNaN)) throw new Error("Invalid time format. Use mm:ss.");
|
||||||
print("Invalid time format. Use mm:ss.");
|
Spicetify.Player.seek((parts[0] * 60 + parts[1]) * 1000);
|
||||||
return;
|
}, `Seeked to ${argText}`);
|
||||||
}
|
|
||||||
const seekMs = (parts[0] * 60 + parts[1]) * 1000;
|
|
||||||
Spicetify.Player.seek(seekMs);
|
|
||||||
print(`Seeked to ${argText}`);
|
|
||||||
} catch (err) {
|
|
||||||
print("Seek error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "volume" || command === "v") {
|
if (command === "volume" || command === "v") {
|
||||||
try {
|
await runPlayerAction("Volume", () => {
|
||||||
if (!argText) {
|
if (!argText) throw new Error("Usage: volume <0-100>");
|
||||||
print("Usage: volume <0-100>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const percent = Number(argText);
|
const percent = Number(argText);
|
||||||
if (!Number.isFinite(percent) || percent < 0 || percent > 100) {
|
if (!Number.isFinite(percent) || percent < 0 || percent > 100) throw new Error("Volume must be between 0 and 100.");
|
||||||
print("Volume must be between 0 and 100.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Spicetify.Player.setVolume(percent / 100);
|
Spicetify.Player.setVolume(percent / 100);
|
||||||
print(`Volume is ${Math.round(percent)}%`);
|
}, `Volume is ${Math.round(Number(argText))}%`);
|
||||||
} catch (err) {
|
|
||||||
print("Volume error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "shuffle") {
|
if (command === "shuffle") {
|
||||||
try {
|
await runPlayerAction("Shuffle", () => {
|
||||||
const current = Spicetify.Player.getShuffle();
|
const current = Spicetify.Player.getShuffle();
|
||||||
Spicetify.Player.setShuffle(!current);
|
Spicetify.Player.setShuffle(!current);
|
||||||
print("Shuffle: " + (!current ? "ON" : "OFF"));
|
return current;
|
||||||
} catch (err) {
|
}, "Shuffle: " + (!Spicetify.Player.getShuffle() ? "ON" : "OFF"));
|
||||||
print("Shuffle error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "loop") {
|
if (command === "loop") { handleRepeatCommand("loop", argText); return; }
|
||||||
handleRepeatCommand("loop", argText);
|
if (command === "superloop") { handleRepeatCommand("superloop", argText); return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command === "superloop") {
|
|
||||||
handleRepeatCommand("superloop", argText);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command === "like") {
|
if (command === "like") {
|
||||||
try {
|
await runPlayerAction("Like", async () => {
|
||||||
const isLiked = await Spicetify.Player.getHeart();
|
const isLiked = await Spicetify.Player.getHeart();
|
||||||
await Spicetify.Player.toggleHeart();
|
await Spicetify.Player.toggleHeart();
|
||||||
print(!isLiked ? "Liked song" : "Unliked song");
|
return isLiked;
|
||||||
} catch (err) {
|
}, Spicetify.Player.getHeart() ? "Unliked song" : "Liked song");
|
||||||
print("Like error: " + err.message);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "lyrics") {
|
if (command === "lyrics") { handleLyricsCommand(argText); return; }
|
||||||
handleLyricsCommand(argText);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Unknown command. Type /help");
|
print("Unknown command. Type /help");
|
||||||
}
|
}
|
||||||
@@ -1452,70 +1256,52 @@ function handleGlobalEsc(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeHelpPanel() {
|
function setPanelState(panelId, className, openVarName, targetState) {
|
||||||
if (!helpPanelOpen) return;
|
const panels = {
|
||||||
helpPanelOpen = false;
|
'helpPanelOpen': () => helpPanelOpen = targetState,
|
||||||
document.body.classList.remove("spotui-help-panel");
|
'aboutPanelOpen': () => aboutPanelOpen = targetState
|
||||||
const panel = document.getElementById("spotui-help-panel");
|
};
|
||||||
if (panel) {
|
if (panels[openVarName]) panels[openVarName]();
|
||||||
panel.hidden = true;
|
document.body.classList.toggle(className, targetState);
|
||||||
}
|
const panel = document.getElementById(panelId);
|
||||||
|
if (panel) panel.hidden = !targetState;
|
||||||
const input = document.getElementById("spotui-input");
|
const input = document.getElementById("spotui-input");
|
||||||
if (input) input.focus();
|
if (input) {
|
||||||
document.removeEventListener("keydown", handleGlobalEsc);
|
if (targetState) input.blur();
|
||||||
|
else input.focus();
|
||||||
|
}
|
||||||
|
if (targetState) document.addEventListener("keydown", handleGlobalEsc);
|
||||||
|
else document.removeEventListener("keydown", handleGlobalEsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeHelpPanel() { if (helpPanelOpen) setPanelState("spotui-help-panel", "spotui-help-panel", "helpPanelOpen", false); }
|
||||||
|
|
||||||
function openHelpPanel() {
|
function openHelpPanel() {
|
||||||
if (helpPanelOpen) {
|
if (helpPanelOpen) { closeHelpPanel(); return; }
|
||||||
closeHelpPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (lyricsPanelOpen) closeLyricsPanel();
|
if (lyricsPanelOpen) closeLyricsPanel();
|
||||||
if (playlistPanelOpen) closePlaylistPanel();
|
if (playlistPanelOpen) closePlaylistPanel();
|
||||||
if (aboutPanelOpen) closeAboutPanel();
|
if (aboutPanelOpen) closeAboutPanel();
|
||||||
|
|
||||||
helpPanelOpen = true;
|
setPanelState("spotui-help-panel", "spotui-help-panel", "helpPanelOpen", true);
|
||||||
document.body.classList.add("spotui-help-panel");
|
|
||||||
document.addEventListener("keydown", handleGlobalEsc);
|
|
||||||
const panel = document.getElementById("spotui-help-panel");
|
const panel = document.getElementById("spotui-help-panel");
|
||||||
if (panel) {
|
if (panel) {
|
||||||
panel.hidden = false;
|
|
||||||
panel.innerHTML = COMMAND_LIST.map(
|
panel.innerHTML = COMMAND_LIST.map(
|
||||||
item => `<div class="help-item"><span class="command">${item.cmd}</span><span class="description">${item.desc}</span></div>`
|
item => `<div class="help-item"><span class="command">${item.cmd}</span><span class="description">${item.desc}</span></div>`
|
||||||
).join('');
|
).join('');
|
||||||
}
|
}
|
||||||
const input = document.getElementById("spotui-input");
|
|
||||||
if (input) input.blur();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeAboutPanel() {
|
function closeAboutPanel() { if (aboutPanelOpen) setPanelState("spotui-about-panel", "spotui-about-panel", "aboutPanelOpen", false); }
|
||||||
if (!aboutPanelOpen) return;
|
|
||||||
aboutPanelOpen = false;
|
|
||||||
document.body.classList.remove("spotui-about-panel");
|
|
||||||
const panel = document.getElementById("spotui-about-panel");
|
|
||||||
if (panel) {
|
|
||||||
panel.hidden = true;
|
|
||||||
}
|
|
||||||
const input = document.getElementById("spotui-input");
|
|
||||||
if (input) input.focus();
|
|
||||||
document.removeEventListener("keydown", handleGlobalEsc);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openAboutPanel() {
|
function openAboutPanel() {
|
||||||
if (aboutPanelOpen) {
|
if (aboutPanelOpen) { closeAboutPanel(); return; }
|
||||||
closeAboutPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (lyricsPanelOpen) closeLyricsPanel();
|
if (lyricsPanelOpen) closeLyricsPanel();
|
||||||
if (playlistPanelOpen) closePlaylistPanel();
|
if (playlistPanelOpen) closePlaylistPanel();
|
||||||
if (helpPanelOpen) closeHelpPanel();
|
if (helpPanelOpen) closeHelpPanel();
|
||||||
|
|
||||||
aboutPanelOpen = true;
|
setPanelState("spotui-about-panel", "spotui-about-panel", "aboutPanelOpen", true);
|
||||||
document.body.classList.add("spotui-about-panel");
|
|
||||||
document.addEventListener("keydown", handleGlobalEsc);
|
|
||||||
const panel = document.getElementById("spotui-about-panel");
|
const panel = document.getElementById("spotui-about-panel");
|
||||||
if (panel) {
|
if (panel) {
|
||||||
panel.hidden = false;
|
|
||||||
panel.innerHTML = `
|
panel.innerHTML = `
|
||||||
<div class="help-item"><span class="command">Developer</span><span class="description">SkenS</span></div>
|
<div class="help-item"><span class="command">Developer</span><span class="description">SkenS</span></div>
|
||||||
<div class="help-item"><span class="command">Repository</span><span class="description"><a href="https://github.com/SkenSMasteR/SpoTUI">https://github.com/SkenSMasteR/SpoTUI</a></span></div>
|
<div class="help-item"><span class="command">Repository</span><span class="description"><a href="https://github.com/SkenSMasteR/SpoTUI">https://github.com/SkenSMasteR/SpoTUI</a></span></div>
|
||||||
@@ -1523,29 +1309,20 @@ function openAboutPanel() {
|
|||||||
<div class="help-item"><span class="command">Contact</span><span class="description"><a href="mailto:receive@gmx.us">receive@gmx.us</a></span></div>
|
<div class="help-item"><span class="command">Contact</span><span class="description"><a href="mailto:receive@gmx.us">receive@gmx.us</a></span></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
const input = document.getElementById("spotui-input");
|
|
||||||
if (input) input.blur();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function closePlaylistPanel() {
|
function closePlaylistPanel() {
|
||||||
playlistPanelOpen = false;
|
playlistPanelOpen = false;
|
||||||
document.body.classList.remove("spotui-playlist-panel");
|
document.body.classList.remove("spotui-playlist-panel");
|
||||||
const panel = document.getElementById("spotui-playlist-panel");
|
const panel = document.getElementById("spotui-playlist-panel");
|
||||||
if (panel) {
|
if (panel) panel.hidden = true;
|
||||||
panel.hidden = true;
|
|
||||||
}
|
|
||||||
const input = document.getElementById("spotui-input");
|
const input = document.getElementById("spotui-input");
|
||||||
if (input) input.focus();
|
if (input) input.focus();
|
||||||
document.removeEventListener("keydown", handlePlaylistPanelKeydown);
|
document.removeEventListener("keydown", handlePlaylistPanelKeydown);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openPlaylistPanel() {
|
async function openPlaylistPanel() {
|
||||||
if (playlistPanelOpen) {
|
if (playlistPanelOpen) { closePlaylistPanel(); return; }
|
||||||
closePlaylistPanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (lyricsPanelOpen) closeLyricsPanel();
|
if (lyricsPanelOpen) closeLyricsPanel();
|
||||||
if (helpPanelOpen) closeHelpPanel();
|
if (helpPanelOpen) closeHelpPanel();
|
||||||
|
|
||||||
@@ -1559,9 +1336,7 @@ async function openPlaylistPanel() {
|
|||||||
playlistPanelOpen = true;
|
playlistPanelOpen = true;
|
||||||
document.body.classList.add("spotui-playlist-panel");
|
document.body.classList.add("spotui-playlist-panel");
|
||||||
const panel = document.getElementById("spotui-playlist-panel");
|
const panel = document.getElementById("spotui-playlist-panel");
|
||||||
if (panel) {
|
if (panel) panel.hidden = false;
|
||||||
panel.hidden = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const input = document.getElementById("spotui-input");
|
const input = document.getElementById("spotui-input");
|
||||||
if (input) input.blur();
|
if (input) input.blur();
|
||||||
@@ -1577,16 +1352,12 @@ async function openPlaylistPanel() {
|
|||||||
async function renderPlaylistPanel() {
|
async function renderPlaylistPanel() {
|
||||||
const playlistList = document.getElementById("spotui-playlist-list");
|
const playlistList = document.getElementById("spotui-playlist-list");
|
||||||
const songList = document.getElementById("spotui-song-list");
|
const songList = document.getElementById("spotui-song-list");
|
||||||
|
|
||||||
if (!playlistList || !songList) return;
|
if (!playlistList || !songList) return;
|
||||||
|
|
||||||
playlistList.innerHTML = "";
|
playlistList.innerHTML = "";
|
||||||
playlists.forEach((p, idx) => {
|
playlists.forEach((p, idx) => {
|
||||||
const item = document.createElement("div");
|
const item = document.createElement("div");
|
||||||
item.className = "playlist-item";
|
item.className = "playlist-item" + (idx === selectedPlaylist ? " selected" : "");
|
||||||
if (idx === selectedPlaylist) {
|
|
||||||
item.classList.add("selected");
|
|
||||||
}
|
|
||||||
item.textContent = p.name;
|
item.textContent = p.name;
|
||||||
playlistList.appendChild(item);
|
playlistList.appendChild(item);
|
||||||
});
|
});
|
||||||
@@ -1608,10 +1379,7 @@ async function renderPlaylistPanel() {
|
|||||||
songList.innerHTML = "";
|
songList.innerHTML = "";
|
||||||
playlistSongs.forEach((s, idx) => {
|
playlistSongs.forEach((s, idx) => {
|
||||||
const item = document.createElement("div");
|
const item = document.createElement("div");
|
||||||
item.className = "song-item";
|
item.className = "song-item" + (idx === selectedSong && activePane === 'song' ? " selected" : "");
|
||||||
if (idx === selectedSong && activePane === 'song') {
|
|
||||||
item.classList.add("selected");
|
|
||||||
}
|
|
||||||
item.textContent = `${s.name} - ${s.artist}`;
|
item.textContent = `${s.name} - ${s.artist}`;
|
||||||
songList.appendChild(item);
|
songList.appendChild(item);
|
||||||
});
|
});
|
||||||
@@ -1621,9 +1389,7 @@ async function renderPlaylistPanel() {
|
|||||||
|
|
||||||
function scrollSelectedIntoView() {
|
function scrollSelectedIntoView() {
|
||||||
const selectedItem = document.querySelector(activePane === 'playlist' ? '.playlist-item.selected' : '.song-item.selected');
|
const selectedItem = document.querySelector(activePane === 'playlist' ? '.playlist-item.selected' : '.song-item.selected');
|
||||||
if (selectedItem) {
|
if (selectedItem) selectedItem.scrollIntoView({ block: 'nearest' });
|
||||||
selectedItem.scrollIntoView({ block: 'nearest' });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handlePlaylistPanelKeydown(e) {
|
async function handlePlaylistPanelKeydown(e) {
|
||||||
@@ -1678,7 +1444,6 @@ async function handlePlaylistPanelKeydown(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getTrackTitle(track, index = 0) {
|
function getTrackTitle(track, index = 0) {
|
||||||
const meta = track?.metadata || track?.contextTrack?.metadata || {};
|
const meta = track?.metadata || track?.contextTrack?.metadata || {};
|
||||||
return track?.name || track?.title || meta.title || meta.name || `Track ${index + 1}`;
|
return track?.name || track?.title || meta.title || meta.name || `Track ${index + 1}`;
|
||||||
@@ -1740,16 +1505,10 @@ function handleRepeatCommand(kind, arg) {
|
|||||||
const targetMode = kind === "loop" ? 1 : 2;
|
const targetMode = kind === "loop" ? 1 : 2;
|
||||||
let nextMode = targetMode;
|
let nextMode = targetMode;
|
||||||
|
|
||||||
if (arg === "on") {
|
if (arg === "on") nextMode = targetMode;
|
||||||
nextMode = targetMode;
|
else if (arg === "off") nextMode = 0;
|
||||||
} else if (arg === "off") {
|
else if (arg === "") nextMode = current === targetMode ? 0 : targetMode;
|
||||||
nextMode = 0;
|
else { print(`Usage: /${kind} [on|off]`); return; }
|
||||||
} else if (arg === "") {
|
|
||||||
nextMode = current === targetMode ? 0 : targetMode;
|
|
||||||
} else {
|
|
||||||
print(`Usage: /${kind} [on|off]`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Spicetify.Player.setRepeat(nextMode);
|
Spicetify.Player.setRepeat(nextMode);
|
||||||
print(`${kind === "loop" ? "Loop" : "Superloop"}: ${nextMode === 0 ? "OFF" : "ON"}`);
|
print(`${kind === "loop" ? "Loop" : "Superloop"}: ${nextMode === 0 ? "OFF" : "ON"}`);
|
||||||
@@ -1775,7 +1534,6 @@ async function getPlaylists() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LYRICS_STORAGE_KEY = "spotui:lyrics-open";
|
const LYRICS_STORAGE_KEY = "spotui:lyrics-open";
|
||||||
|
|
||||||
let lyricsPanelOpen = false;
|
let lyricsPanelOpen = false;
|
||||||
let lyricsLoadToken = 0;
|
let lyricsLoadToken = 0;
|
||||||
let lyricsActiveIndex = -1;
|
let lyricsActiveIndex = -1;
|
||||||
@@ -1866,7 +1624,7 @@ async function fetchLrclibLyrics(info) {
|
|||||||
const result = normalizeLrclibPayload(data);
|
const result = normalizeLrclibPayload(data);
|
||||||
if (result) return result;
|
if (result) return result;
|
||||||
}
|
}
|
||||||
} catch { /* try search */ }
|
} catch { }
|
||||||
try {
|
try {
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
track_name: info.title,
|
track_name: info.title,
|
||||||
@@ -1982,8 +1740,8 @@ async function loadLyricsForCurrentTrack() {
|
|||||||
|
|
||||||
if (lyricsCache.uri === info.uri && (lyricsCache.lines.length || lyricsCache.instrumental || lyricsCache.error)) {
|
if (lyricsCache.uri === info.uri && (lyricsCache.lines.length || lyricsCache.instrumental || lyricsCache.error)) {
|
||||||
setLyricsHeader(info, lyricsCache.instrumental ? "instrumental" : `${lyricsCache.synced ? "synced" : "unsynced"} · ${lyricsCache.provider || "cache"}`);
|
setLyricsHeader(info, lyricsCache.instrumental ? "instrumental" : `${lyricsCache.synced ? "synced" : "unsynced"} · ${lyricsCache.provider || "cache"}`);
|
||||||
if (lyricsCache.instrumental) { renderLyricsEmpty("Instrumental", "No vocals to show for this track."); }
|
if (lyricsCache.instrumental) renderLyricsEmpty("Instrumental", "No vocals to show for this track.");
|
||||||
else if (lyricsCache.error) { renderLyricsEmpty("No lyrics", lyricsCache.error); }
|
else if (lyricsCache.error) renderLyricsEmpty("No lyrics", lyricsCache.error);
|
||||||
else { renderLyricsLines(lyricsCache.lines, lyricsCache.synced); syncLyricsHighlight(true); }
|
else { renderLyricsLines(lyricsCache.lines, lyricsCache.synced); syncLyricsHighlight(true); }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2011,7 +1769,7 @@ async function loadLyricsForCurrentTrack() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function storeLyricsOpen(open) {
|
function storeLyricsOpen(open) {
|
||||||
try { localStorage.setItem(LYRICS_STORAGE_KEY, open ? "1" : "0"); } catch { /* ignore */ }
|
try { localStorage.setItem(LYRICS_STORAGE_KEY, open ? "1" : "0"); } catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function openLyricsPanel() {
|
function openLyricsPanel() {
|
||||||
@@ -2077,16 +1835,13 @@ injectStyle();
|
|||||||
setTimeout(createCopyButton, 500);
|
setTimeout(createCopyButton, 500);
|
||||||
setTimeout(initLyricsBridge, 1000);
|
setTimeout(initLyricsBridge, 1000);
|
||||||
|
|
||||||
if (Spicetify?.Platform) {
|
if (Spicetify?.Platform) createTerminal();
|
||||||
createTerminal();
|
else setTimeout(createTerminal, 1500);
|
||||||
} else {
|
|
||||||
setTimeout(createTerminal, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (localStorage.getItem(LYRICS_STORAGE_KEY) === "1") {
|
if (localStorage.getItem(LYRICS_STORAGE_KEY) === "1") {
|
||||||
setTimeout(() => openLyricsPanel(), 2000);
|
setTimeout(() => openLyricsPanel(), 2000);
|
||||||
}
|
}
|
||||||
} catch { /* ignore */ }
|
} catch { }
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user