Merge branch 'main' of github.com:monochrome-music/monochrome
This commit is contained in:
commit
83c705a60a
22 changed files with 925 additions and 2716 deletions
|
|
@ -1,52 +1,26 @@
|
|||
# ------------------------------------------------------------
|
||||
# Base Image
|
||||
# ------------------------------------------------------------
|
||||
FROM debian:unstable-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
FROM mcr.microsoft.com/devcontainers/base:debian
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# System Dependencies
|
||||
# ------------------------------------------------------------
|
||||
RUN apt-get update && apt-get upgrade -y && \
|
||||
RUN apt update && apt upgrade -y && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
ca-certificates \
|
||||
git \
|
||||
build-essential \
|
||||
sudo \
|
||||
git-lfs \
|
||||
fish \
|
||||
unzip \
|
||||
xz-utils \
|
||||
libatomic1 \
|
||||
libc6 \
|
||||
wget \
|
||||
nodejs \
|
||||
npm && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Create Non-Root User
|
||||
# ------------------------------------------------------------
|
||||
ARG USERNAME=devuser
|
||||
ARG UID=1000
|
||||
ARG GID=1000
|
||||
|
||||
RUN groupadd --gid ${GID} ${USERNAME} && \
|
||||
useradd --uid ${UID} --gid ${GID} -m -s /usr/bin/fish ${USERNAME} && \
|
||||
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
|
||||
|
||||
USER ${USERNAME}
|
||||
WORKDIR /home/${USERNAME}
|
||||
npm \
|
||||
curl
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Install Bun (Non-Root)
|
||||
# ------------------------------------------------------------
|
||||
ENV BUN_INSTALL=/home/${USERNAME}/.bun
|
||||
ENV PATH="${BUN_INSTALL}/bin:${PATH}"
|
||||
|
||||
ENV BUN_INSTALL="$HOME/.bun"
|
||||
ENV PATH="$BUN_INSTALL/bin:$PATH"
|
||||
|
||||
RUN curl -fsSL https://bun.sh/install | bash
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
|
@ -58,7 +32,7 @@ RUN curl -fsSL https://opencode.ai/install -o opencode-install && \
|
|||
rm opencode-install
|
||||
|
||||
# Add OpenCode to PATH permanently
|
||||
ENV PATH="/home/${USERNAME}/.opencode/bin:${PATH}"
|
||||
ENV PATH="$HOME/.opencode/bin:$PATH"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Ensure fish is Default Shell
|
||||
|
|
|
|||
|
|
@ -1,22 +1,14 @@
|
|||
{
|
||||
"name": "debian-npm-fish-devcontainer",
|
||||
"name": "Monochrome Dev Container",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
"context": "..",
|
||||
"dockerfile": "./Dockerfile"
|
||||
},
|
||||
|
||||
"remoteUser": "devuser",
|
||||
|
||||
"features": {},
|
||||
|
||||
"postCreateCommand": "git config --local core.editor \"code --wait\" && git config --local commit.gpgsign false && npm install && bun install",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
}
|
||||
},
|
||||
|
||||
"postCreateCommand": "npm install",
|
||||
|
||||
"remoteEnv": {
|
||||
"SHELL": "/usr/bin/fish"
|
||||
}
|
||||
"mounts": ["source=${env:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached"]
|
||||
}
|
||||
|
|
|
|||
23
.vscode/tasks.json
vendored
Normal file
23
.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"label": "npm: build",
|
||||
"detail": "vite build"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev",
|
||||
"problemMatcher": [],
|
||||
"label": "npm: dev",
|
||||
"detail": "vite"
|
||||
}
|
||||
]
|
||||
}
|
||||
321
bun.lock
321
bun.lock
|
|
@ -17,13 +17,13 @@
|
|||
"fuse.js": "^7.1.0",
|
||||
"hls.js": "^1.6.15",
|
||||
"jose": "^6.1.3",
|
||||
"npm": "^11.11.0",
|
||||
"pocketbase": "^0.26.8",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@neutralinojs/neu": "^11.7.0",
|
||||
"eslint": "^9.39.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"formidable": "^3.5.4",
|
||||
"globals": "^17.4.0",
|
||||
"htmlhint": "^1.9.1",
|
||||
"miniflare": "^4.20260301.1",
|
||||
|
|
@ -38,6 +38,7 @@
|
|||
},
|
||||
},
|
||||
"overrides": {
|
||||
"serialize-javascript": "^7.0.3",
|
||||
"source-map": "^0.7.4",
|
||||
"sourcemap-codec": "npm:@jridgewell/sourcemap-codec@^1.4.14",
|
||||
},
|
||||
|
|
@ -418,12 +419,16 @@
|
|||
|
||||
"@neutralinojs/neu": ["@neutralinojs/neu@11.7.0", "", { "dependencies": { "@electron/asar": "^3.0.3", "chalk": "^4.1.0", "chokidar": "^4.0.3", "commander": "^7.2.0", "configstore": "^5.0.1", "edit-json-file": "^1.6.2", "follow-redirects": "^1.13.1", "fs-extra": "^9.0.1", "pe-library": "^1.0.1", "png2icons": "^2.0.1", "postject": "1.0.0-alpha.6", "recursive-readdir": "^2.2.2", "resedit": "^2.0.2", "spawn-command": "^1.0.0", "tcp-port-used": "^1.0.2", "uuid": "^8.3.2", "websocket": "^1.0.35", "zip-lib": "^1.0.4" }, "bin": { "neu": "bin/neu.js" } }, "sha512-fUqvR70a+BpKI9mrD92ldZkVC24Rs8XL/9m7zmOCLgCRys3yuWy7vEsxpHzKMzqTiQJkTYIsLmcR8VMzNIjuSw=="],
|
||||
|
||||
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.3.1", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw=="],
|
||||
|
||||
"@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="],
|
||||
|
||||
"@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="],
|
||||
|
|
@ -546,6 +551,8 @@
|
|||
|
||||
"arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
|
||||
|
||||
"asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="],
|
||||
|
||||
"astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="],
|
||||
|
||||
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
|
||||
|
|
@ -672,6 +679,8 @@
|
|||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="],
|
||||
|
||||
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
|
||||
|
||||
"dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="],
|
||||
|
|
@ -782,6 +791,8 @@
|
|||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"formidable": ["formidable@3.5.4", "", { "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" } }, "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug=="],
|
||||
|
||||
"fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
|
||||
|
||||
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
||||
|
|
@ -1062,8 +1073,6 @@
|
|||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"npm": ["npm@11.11.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^9.4.0", "@npmcli/config": "^10.7.1", "@npmcli/fs": "^5.0.0", "@npmcli/map-workspaces": "^5.0.3", "@npmcli/metavuln-calculator": "^9.0.3", "@npmcli/package-json": "^7.0.5", "@npmcli/promise-spawn": "^9.0.1", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.3", "@sigstore/tuf": "^4.0.1", "abbrev": "^4.0.0", "archy": "~1.0.0", "cacache": "^20.0.3", "chalk": "^5.6.2", "ci-info": "^4.4.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^13.0.6", "graceful-fs": "^4.2.11", "hosted-git-info": "^9.0.2", "ini": "^6.0.0", "init-package-json": "^8.2.5", "is-cidr": "^6.0.3", "json-parse-even-better-errors": "^5.0.0", "libnpmaccess": "^10.0.3", "libnpmdiff": "^8.1.3", "libnpmexec": "^10.2.3", "libnpmfund": "^7.0.17", "libnpmorg": "^8.0.1", "libnpmpack": "^9.1.3", "libnpmpublish": "^11.1.3", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.3", "make-fetch-happen": "^15.0.4", "minimatch": "^10.2.2", "minipass": "^7.1.3", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^12.2.0", "nopt": "^9.0.0", "npm-audit-report": "^7.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.2", "npm-pick-manifest": "^11.0.3", "npm-profile": "^12.0.1", "npm-registry-fetch": "^19.1.1", "npm-user-validate": "^4.0.0", "p-map": "^7.0.4", "pacote": "^21.4.0", "parse-conflict-json": "^5.0.1", "proc-log": "^6.1.0", "qrcode-terminal": "^0.12.0", "read": "^5.0.1", "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "ssri": "^13.0.1", "supports-color": "^10.2.2", "tar": "^7.5.9", "text-table": "~0.2.0", "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^7.0.2", "which": "^6.0.1" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" } }, "sha512-82gRxKrh/eY5UnNorkTFcdBQAGpgjWehkfGVqAGlJjejEtJZGGJUqjo3mbBTNbc5BTnPKGVtGPBZGhElujX5cw=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
||||
|
|
@ -1146,8 +1155,6 @@
|
|||
|
||||
"r-json": ["r-json@1.3.1", "", { "dependencies": { "w-json": "1.3.10" } }, "sha512-5nhRFfjVMQdrwKUfUlRpDUCocdKtjSnYZ1R/86mpZDV3MfsZ3dYYNjSGuMX+mPBvFvQBhdzxSqxkuLPLv4uFGg=="],
|
||||
|
||||
"randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"recursive-readdir": ["recursive-readdir@2.2.3", "", { "dependencies": { "minimatch": "^3.0.5" } }, "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA=="],
|
||||
|
|
@ -1194,7 +1201,7 @@
|
|||
|
||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
|
||||
"serialize-javascript": ["serialize-javascript@7.0.4", "", {}, "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg=="],
|
||||
|
||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||
|
||||
|
|
@ -1512,298 +1519,6 @@
|
|||
|
||||
"node-sarif-builder/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="],
|
||||
|
||||
"npm/@gar/promise-retry": ["@gar/promise-retry@1.0.2", "", { "dependencies": { "retry": "^0.13.1" } }, "sha512-Lm/ZLhDZcBECta3TmCQSngiQykFdfw+QtI1/GYMsZd4l3nG+P8WLB16XuS7WaBGLQ+9E+cOcWQsth9cayuGt8g=="],
|
||||
|
||||
"npm/@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
"npm/@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", { "bundled": true }, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="],
|
||||
|
||||
"npm/@npmcli/agent": ["@npmcli/agent@4.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA=="],
|
||||
|
||||
"npm/@npmcli/arborist": ["@npmcli/arborist@9.4.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bundled": true, "bin": { "arborist": "bin/index.js" } }, "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA=="],
|
||||
|
||||
"npm/@npmcli/config": ["@npmcli/config@10.7.1", "", { "dependencies": { "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-lh0eZYOknIpIKYKxbQKX7xFmb4FbmrOHUD25+0iEo3djRQP6YleHwBFgjH3X7QvUVM4t+Xm7rGsjDwJp63WkAg=="],
|
||||
|
||||
"npm/@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" }, "bundled": true }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="],
|
||||
|
||||
"npm/@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="],
|
||||
|
||||
"npm/@npmcli/installed-package-contents": ["@npmcli/installed-package-contents@4.0.0", "", { "dependencies": { "npm-bundled": "^5.0.0", "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" } }, "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA=="],
|
||||
|
||||
"npm/@npmcli/map-workspaces": ["@npmcli/map-workspaces@5.0.3", "", { "dependencies": { "@npmcli/name-from-folder": "^4.0.0", "@npmcli/package-json": "^7.0.0", "glob": "^13.0.0", "minimatch": "^10.0.3" }, "bundled": true }, "sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw=="],
|
||||
|
||||
"npm/@npmcli/metavuln-calculator": ["@npmcli/metavuln-calculator@9.0.3", "", { "dependencies": { "cacache": "^20.0.0", "json-parse-even-better-errors": "^5.0.0", "pacote": "^21.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5" }, "bundled": true }, "sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg=="],
|
||||
|
||||
"npm/@npmcli/name-from-folder": ["@npmcli/name-from-folder@4.0.0", "", {}, "sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg=="],
|
||||
|
||||
"npm/@npmcli/node-gyp": ["@npmcli/node-gyp@5.0.0", "", {}, "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ=="],
|
||||
|
||||
"npm/@npmcli/package-json": ["@npmcli/package-json@7.0.5", "", { "dependencies": { "@npmcli/git": "^7.0.0", "glob": "^13.0.0", "hosted-git-info": "^9.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.5.3", "spdx-expression-parse": "^4.0.0" }, "bundled": true }, "sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ=="],
|
||||
|
||||
"npm/@npmcli/promise-spawn": ["@npmcli/promise-spawn@9.0.1", "", { "dependencies": { "which": "^6.0.0" }, "bundled": true }, "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q=="],
|
||||
|
||||
"npm/@npmcli/query": ["@npmcli/query@5.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" } }, "sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ=="],
|
||||
|
||||
"npm/@npmcli/redact": ["@npmcli/redact@4.0.0", "", { "bundled": true }, "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q=="],
|
||||
|
||||
"npm/@npmcli/run-script": ["@npmcli/run-script@10.0.4", "", { "dependencies": { "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "node-gyp": "^12.1.0", "proc-log": "^6.0.0" }, "bundled": true }, "sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg=="],
|
||||
|
||||
"npm/@sigstore/bundle": ["@sigstore/bundle@4.0.0", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A=="],
|
||||
|
||||
"npm/@sigstore/core": ["@sigstore/core@3.1.0", "", {}, "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A=="],
|
||||
|
||||
"npm/@sigstore/protobuf-specs": ["@sigstore/protobuf-specs@0.5.0", "", {}, "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA=="],
|
||||
|
||||
"npm/@sigstore/sign": ["@sigstore/sign@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "make-fetch-happen": "^15.0.3", "proc-log": "^6.1.0", "promise-retry": "^2.0.1" } }, "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg=="],
|
||||
|
||||
"npm/@sigstore/tuf": ["@sigstore/tuf@4.0.1", "", { "dependencies": { "@sigstore/protobuf-specs": "^0.5.0", "tuf-js": "^4.1.0" }, "bundled": true }, "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw=="],
|
||||
|
||||
"npm/@sigstore/verify": ["@sigstore/verify@3.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0" } }, "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag=="],
|
||||
|
||||
"npm/@tufjs/canonical-json": ["@tufjs/canonical-json@2.0.0", "", {}, "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA=="],
|
||||
|
||||
"npm/@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="],
|
||||
|
||||
"npm/abbrev": ["abbrev@4.0.0", "", { "bundled": true }, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="],
|
||||
|
||||
"npm/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"npm/aproba": ["aproba@2.1.0", "", {}, "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew=="],
|
||||
|
||||
"npm/archy": ["archy@1.0.0", "", { "bundled": true }, "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw=="],
|
||||
|
||||
"npm/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"npm/bin-links": ["bin-links@6.0.0", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w=="],
|
||||
|
||||
"npm/binary-extensions": ["binary-extensions@3.1.0", "", {}, "sha512-Jvvd9hy1w+xUad8+ckQsWA/V1AoyubOvqn0aygjMOVM4BfIaRav1NFS3LsTSDaV4n4FtcCtQXvzep1E6MboqwQ=="],
|
||||
|
||||
"npm/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
|
||||
|
||||
"npm/cacache": ["cacache@20.0.3", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0", "unique-filename": "^5.0.0" }, "bundled": true }, "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw=="],
|
||||
|
||||
"npm/chalk": ["chalk@5.6.2", "", { "bundled": true }, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
|
||||
"npm/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||
|
||||
"npm/ci-info": ["ci-info@4.4.0", "", { "bundled": true }, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
|
||||
|
||||
"npm/cidr-regex": ["cidr-regex@5.0.3", "", {}, "sha512-zfPT2uurEroxXqefaL2L7/fT5ED2XTutC6UwFbSZfqSOk1vk5VFY6xa6/R6pBxB4Uc8MNPbRW5ykqutFG5P5ww=="],
|
||||
|
||||
"npm/cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="],
|
||||
|
||||
"npm/common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="],
|
||||
|
||||
"npm/cssesc": ["cssesc@3.0.0", "", { "bin": "bin/cssesc" }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"npm/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"npm/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="],
|
||||
|
||||
"npm/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
|
||||
|
||||
"npm/err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="],
|
||||
|
||||
"npm/exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
|
||||
|
||||
"npm/fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", { "bundled": true }, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="],
|
||||
|
||||
"npm/fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"npm/fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="],
|
||||
|
||||
"npm/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" }, "bundled": true }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
||||
|
||||
"npm/graceful-fs": ["graceful-fs@4.2.11", "", { "bundled": true }, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"npm/hosted-git-info": ["hosted-git-info@9.0.2", "", { "dependencies": { "lru-cache": "^11.1.0" }, "bundled": true }, "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg=="],
|
||||
|
||||
"npm/http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
|
||||
|
||||
"npm/http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
||||
|
||||
"npm/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"npm/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"npm/ignore-walk": ["ignore-walk@8.0.0", "", { "dependencies": { "minimatch": "^10.0.3" } }, "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A=="],
|
||||
|
||||
"npm/imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"npm/ini": ["ini@6.0.0", "", { "bundled": true }, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
|
||||
|
||||
"npm/init-package-json": ["init-package-json@8.2.5", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "npm-package-arg": "^13.0.0", "promzard": "^3.0.1", "read": "^5.0.1", "semver": "^7.7.2", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-IknQ+upLuJU6t3p0uo9wS3GjFD/1GtxIwcIGYOWR8zL2HxQeJwvxYTgZr9brJ8pyZ4kvpkebM8ZKcyqOeLOHSg=="],
|
||||
|
||||
"npm/ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
|
||||
|
||||
"npm/is-cidr": ["is-cidr@6.0.3", "", { "dependencies": { "cidr-regex": "^5.0.1" }, "bundled": true }, "sha512-tPdsizbDiISrc4PoII6ZfpmAokx0oDKeYqAUp5bXOfznauOFXfEeosKBRrl0o0SriE4xoRR05Czn4YPCFMjSHA=="],
|
||||
|
||||
"npm/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="],
|
||||
|
||||
"npm/json-parse-even-better-errors": ["json-parse-even-better-errors@5.0.0", "", { "bundled": true }, "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ=="],
|
||||
|
||||
"npm/json-stringify-nice": ["json-stringify-nice@1.1.4", "", {}, "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw=="],
|
||||
|
||||
"npm/jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="],
|
||||
|
||||
"npm/just-diff": ["just-diff@6.0.2", "", {}, "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA=="],
|
||||
|
||||
"npm/just-diff-apply": ["just-diff-apply@5.5.0", "", {}, "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw=="],
|
||||
|
||||
"npm/libnpmaccess": ["libnpmaccess@10.0.3", "", { "dependencies": { "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-JPHTfWJxIK+NVPdNMNGnkz4XGX56iijPbe0qFWbdt68HL+kIvSzh+euBL8npLZvl2fpaxo+1eZSdoG15f5YdIQ=="],
|
||||
|
||||
"npm/libnpmdiff": ["libnpmdiff@8.1.3", "", { "dependencies": { "@npmcli/arborist": "^9.4.0", "@npmcli/installed-package-contents": "^4.0.0", "binary-extensions": "^3.0.0", "diff": "^8.0.2", "minimatch": "^10.0.3", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "tar": "^7.5.1" }, "bundled": true }, "sha512-QZ9rpchNXSzvxTRHzEqxCfYBK2h+6j4J7IbBViBGy3xSJDBl026BCMhmlZQ0a69GeQkjkbM9X1hzRV9N5cdQog=="],
|
||||
|
||||
"npm/libnpmexec": ["libnpmexec@10.2.3", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/arborist": "^9.4.0", "@npmcli/package-json": "^7.0.0", "@npmcli/run-script": "^10.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2", "proc-log": "^6.0.0", "read": "^5.0.1", "semver": "^7.3.7", "signal-exit": "^4.1.0", "walk-up-path": "^4.0.0" }, "bundled": true }, "sha512-tCeneLdUhmn8GTORbui7QZrr1Rv8Y2/mQRwMjUeyY8IrhCjv29RkoH3gFz+1CCPGGMp26eT8KI977G74+rXMpw=="],
|
||||
|
||||
"npm/libnpmfund": ["libnpmfund@7.0.17", "", { "dependencies": { "@npmcli/arborist": "^9.4.0" }, "bundled": true }, "sha512-0VRPO+Bs21kneI3J01QqnuxiNnHn1lErTqLIbI3zGM9LvsPtc2q2/xhjACuXbkcejuHVm3T9mWaky0IjM9gQeQ=="],
|
||||
|
||||
"npm/libnpmorg": ["libnpmorg@8.0.1", "", { "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-/QeyXXg4hqMw0ESM7pERjIT2wbR29qtFOWIOug/xO4fRjS3jJJhoAPQNsnHtdwnCqgBdFpGQ45aIdFFZx2YhTA=="],
|
||||
|
||||
"npm/libnpmpack": ["libnpmpack@9.1.3", "", { "dependencies": { "@npmcli/arborist": "^9.4.0", "@npmcli/run-script": "^10.0.0", "npm-package-arg": "^13.0.0", "pacote": "^21.0.2" }, "bundled": true }, "sha512-7Uvo0mDIidFCOGwZJghTuk9glaR6Es9FxmLWJobOS857/cb5SO5YPqgYLlC1TZB6L0c2jtu8XB1GfxKRf4W4GA=="],
|
||||
|
||||
"npm/libnpmpublish": ["libnpmpublish@11.1.3", "", { "dependencies": { "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "npm-package-arg": "^13.0.0", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7", "sigstore": "^4.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-NVPTth/71cfbdYHqypcO9Lt5WFGTzFEcx81lWd7GDJIgZ95ERdYHGUfCtFejHCyqodKsQkNEx2JCkMpreDty/A=="],
|
||||
|
||||
"npm/libnpmsearch": ["libnpmsearch@9.0.1", "", { "dependencies": { "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-oKw58X415ERY/BOGV3jQPVMcep8YeMRWMzuuqB0BAIM5VxicOU1tQt19ExCu4SV77SiTOEoziHxGEgJGw3FBYQ=="],
|
||||
|
||||
"npm/libnpmteam": ["libnpmteam@8.0.2", "", { "dependencies": { "aproba": "^2.0.0", "npm-registry-fetch": "^19.0.0" }, "bundled": true }, "sha512-ypLrDUQoi8EhG+gzx5ENMcYq23YjPV17Mfvx4nOnQiHOi8vp47+4GvZBrMsEM4yeHPwxguF/HZoXH4rJfHdH/w=="],
|
||||
|
||||
"npm/libnpmversion": ["libnpmversion@8.0.3", "", { "dependencies": { "@npmcli/git": "^7.0.0", "@npmcli/run-script": "^10.0.0", "json-parse-even-better-errors": "^5.0.0", "proc-log": "^6.0.0", "semver": "^7.3.7" }, "bundled": true }, "sha512-Avj1GG3DT6MGzWOOk3yA7rORcMDUPizkIGbI8glHCO7WoYn3NYNmskLDwxg2NMY1Tyf2vrHAqTuSG58uqd1lJg=="],
|
||||
|
||||
"npm/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
|
||||
|
||||
"npm/make-fetch-happen": ["make-fetch-happen@15.0.4", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" }, "bundled": true }, "sha512-vM2sG+wbVeVGYcCm16mM3d5fuem9oC28n436HjsGO3LcxoTI8LNVa4rwZDn3f76+cWyT4GGJDxjTYU1I2nr6zw=="],
|
||||
|
||||
"npm/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" }, "bundled": true }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
|
||||
|
||||
"npm/minipass": ["minipass@7.1.3", "", { "bundled": true }, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"npm/minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="],
|
||||
|
||||
"npm/minipass-fetch": ["minipass-fetch@5.0.2", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^2.0.0", "minizlib": "^3.0.1" }, "optionalDependencies": { "iconv-lite": "^0.7.2" } }, "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ=="],
|
||||
|
||||
"npm/minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="],
|
||||
|
||||
"npm/minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" }, "bundled": true }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="],
|
||||
|
||||
"npm/minipass-sized": ["minipass-sized@2.0.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA=="],
|
||||
|
||||
"npm/minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
||||
|
||||
"npm/ms": ["ms@2.1.3", "", { "bundled": true }, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"npm/mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="],
|
||||
|
||||
"npm/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"npm/node-gyp": ["node-gyp@12.2.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^15.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "tar": "^7.5.4", "tinyglobby": "^0.2.12", "which": "^6.0.0" }, "bundled": true, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ=="],
|
||||
|
||||
"npm/nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bundled": true, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="],
|
||||
|
||||
"npm/npm-audit-report": ["npm-audit-report@7.0.0", "", { "bundled": true }, "sha512-bluLL4xwGr/3PERYz50h2Upco0TJMDcLcymuFnfDWeGO99NqH724MNzhWi5sXXuXf2jbytFF0LyR8W+w1jTI6A=="],
|
||||
|
||||
"npm/npm-bundled": ["npm-bundled@5.0.0", "", { "dependencies": { "npm-normalize-package-bin": "^5.0.0" } }, "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw=="],
|
||||
|
||||
"npm/npm-install-checks": ["npm-install-checks@8.0.0", "", { "dependencies": { "semver": "^7.1.1" }, "bundled": true }, "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA=="],
|
||||
|
||||
"npm/npm-normalize-package-bin": ["npm-normalize-package-bin@5.0.0", "", {}, "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="],
|
||||
|
||||
"npm/npm-package-arg": ["npm-package-arg@13.0.2", "", { "dependencies": { "hosted-git-info": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^7.0.0" }, "bundled": true }, "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA=="],
|
||||
|
||||
"npm/npm-packlist": ["npm-packlist@10.0.4", "", { "dependencies": { "ignore-walk": "^8.0.0", "proc-log": "^6.0.0" } }, "sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng=="],
|
||||
|
||||
"npm/npm-pick-manifest": ["npm-pick-manifest@11.0.3", "", { "dependencies": { "npm-install-checks": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "bundled": true }, "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ=="],
|
||||
|
||||
"npm/npm-profile": ["npm-profile@12.0.1", "", { "dependencies": { "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0" }, "bundled": true }, "sha512-Xs1mejJ1/9IKucCxdFMkiBJUre0xaxfCpbsO7DB7CadITuT4k68eI05HBlw4kj+Em1rsFMgeFNljFPYvPETbVQ=="],
|
||||
|
||||
"npm/npm-registry-fetch": ["npm-registry-fetch@19.1.1", "", { "dependencies": { "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", "npm-package-arg": "^13.0.0", "proc-log": "^6.0.0" }, "bundled": true }, "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw=="],
|
||||
|
||||
"npm/npm-user-validate": ["npm-user-validate@4.0.0", "", { "bundled": true }, "sha512-TP+Ziq/qPi/JRdhaEhnaiMkqfMGjhDLoh/oRfW+t5aCuIfJxIUxvwk6Sg/6ZJ069N/Be6gs00r+aZeJTfS9uHQ=="],
|
||||
|
||||
"npm/p-map": ["p-map@7.0.4", "", { "bundled": true }, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
|
||||
|
||||
"npm/pacote": ["pacote@21.4.0", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bundled": true, "bin": { "pacote": "bin/index.js" } }, "sha512-DR7mn7HUOomAX1BORnpYy678qVIidbvOojkBscqy27dRKN+s/hLeQT1MeYYrx1Cxh62jyKjiWiDV7RTTqB+ZEQ=="],
|
||||
|
||||
"npm/parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "bundled": true }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="],
|
||||
|
||||
"npm/path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
|
||||
|
||||
"npm/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"npm/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
|
||||
|
||||
"npm/proc-log": ["proc-log@6.1.0", "", { "bundled": true }, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="],
|
||||
|
||||
"npm/proggy": ["proggy@4.0.0", "", {}, "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ=="],
|
||||
|
||||
"npm/promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="],
|
||||
|
||||
"npm/promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="],
|
||||
|
||||
"npm/promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="],
|
||||
|
||||
"npm/promzard": ["promzard@3.0.1", "", { "dependencies": { "read": "^5.0.0" } }, "sha512-M5mHhWh+Adz0BIxgSrqcc6GTCSconR7zWQV9vnOSptNtr6cSFlApLc28GbQhuN6oOWBQeV2C0bNE47JCY/zu3Q=="],
|
||||
|
||||
"npm/qrcode-terminal": ["qrcode-terminal@0.12.0", "", { "bundled": true, "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="],
|
||||
|
||||
"npm/read": ["read@5.0.1", "", { "dependencies": { "mute-stream": "^3.0.0" }, "bundled": true }, "sha512-+nsqpqYkkpet2UVPG8ZiuE8d113DK4vHYEoEhcrXBAlPiq6di7QRTuNiKQAbaRYegobuX2BpZ6QjanKOXnJdTA=="],
|
||||
|
||||
"npm/read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="],
|
||||
|
||||
"npm/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
||||
|
||||
"npm/safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"npm/semver": ["semver@7.7.4", "", { "bundled": true, "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||
|
||||
"npm/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"npm/sigstore": ["sigstore@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.0", "@sigstore/tuf": "^4.0.1", "@sigstore/verify": "^3.1.0" } }, "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA=="],
|
||||
|
||||
"npm/smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||
|
||||
"npm/socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
|
||||
|
||||
"npm/socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
|
||||
|
||||
"npm/spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
|
||||
|
||||
"npm/spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" }, "bundled": true }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="],
|
||||
|
||||
"npm/spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="],
|
||||
|
||||
"npm/ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" }, "bundled": true }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
|
||||
|
||||
"npm/supports-color": ["supports-color@10.2.2", "", { "bundled": true }, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||
|
||||
"npm/tar": ["tar@7.5.10", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "bundled": true }, "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw=="],
|
||||
|
||||
"npm/text-table": ["text-table@0.2.0", "", { "bundled": true }, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="],
|
||||
|
||||
"npm/tiny-relative-date": ["tiny-relative-date@2.0.2", "", { "bundled": true }, "sha512-rGxAbeL9z3J4pI2GtBEoFaavHdO4RKAU54hEuOef5kfx5aPqiQtbhYktMOTL5OA33db8BjsDcLXuNp+/v19PHw=="],
|
||||
|
||||
"npm/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"npm/treeverse": ["treeverse@3.0.0", "", { "bundled": true }, "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ=="],
|
||||
|
||||
"npm/tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="],
|
||||
|
||||
"npm/unique-filename": ["unique-filename@5.0.0", "", { "dependencies": { "unique-slug": "^6.0.0" } }, "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg=="],
|
||||
|
||||
"npm/unique-slug": ["unique-slug@6.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw=="],
|
||||
|
||||
"npm/util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"npm/validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", { "bundled": true }, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="],
|
||||
|
||||
"npm/walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="],
|
||||
|
||||
"npm/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bundled": true, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="],
|
||||
|
||||
"npm/write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="],
|
||||
|
||||
"npm/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||
|
||||
"path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||
|
||||
"postject/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="],
|
||||
|
|
@ -1858,12 +1573,6 @@
|
|||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||
|
||||
"npm/minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"npm/minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||
|
||||
"npm/promise-retry/retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
|
||||
|
||||
"stylelint/file-entry-cache/flat-cache": ["flat-cache@6.1.19", "", { "dependencies": { "cacheable": "^2.2.0", "flatted": "^3.3.3", "hookified": "^1.13.0" } }, "sha512-l/K33newPTZMTGAnnzaiqSl6NnH7Namh8jBNjrgjprWxGmZUuxx/sJNIRaijOh3n7q7ESbhNZC+pvVZMFdeU4A=="],
|
||||
|
||||
"table/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
|
@ -1888,10 +1597,6 @@
|
|||
|
||||
"glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"npm/minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"npm/minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"workbox-build/glob/path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
753
index.html
753
index.html
|
|
@ -3450,6 +3450,7 @@
|
|||
<button class="settings-tab" data-tab="scrobbling">Scrobbling</button>
|
||||
<button class="settings-tab" data-tab="audio">Audio</button>
|
||||
<button class="settings-tab" data-tab="downloads">Downloads</button>
|
||||
<button class="settings-tab" data-tab="instances">Instances</button>
|
||||
<button class="settings-tab" data-tab="system">System</button>
|
||||
</div>
|
||||
<div class="settings-tab-content active" id="settings-tab-appearance">
|
||||
|
|
@ -3687,10 +3688,13 @@
|
|||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Full-screen Visualizer</span>
|
||||
<span class="description">Enable particle visualizer in full-screen mode</span>
|
||||
<span class="description">Enable the visualizer in full-screen mode</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="visualizer-enabled-toggle" checked />
|
||||
|
|
@ -3707,6 +3711,7 @@
|
|||
<option value="particles">Particles</option>
|
||||
<option value="unknown-pleasures">Unknown Pleasures</option>
|
||||
<option value="butterchurn">Butterchurn (Milkdrop)</option>
|
||||
<option value="kawarp">Kawarp</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item" id="visualizer-mode-setting">
|
||||
|
|
@ -3915,6 +3920,33 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Compact Artists</span>
|
||||
<span class="description"
|
||||
>Show artist cards in a compact, horizontal layout</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="compact-artist-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Compact Albums</span>
|
||||
<span class="description"
|
||||
>Show album cards in a compact, horizontal layout</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="compact-album-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group" id="sidebar-order-settings-group">
|
||||
<div class="sidebar-settings-section sidebar-settings-main">
|
||||
<span class="sidebar-settings-section-label">TOP SECTION</span>
|
||||
|
|
@ -4549,43 +4581,6 @@
|
|||
<option value="LOW">AAC 96kbps</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Download Quality</span>
|
||||
<span class="description">Quality for track downloads</span>
|
||||
</div>
|
||||
<select id="download-quality-setting">
|
||||
<option value="HI_RES_LOSSLESS">Hi-Res FLAC (24-bit)</option>
|
||||
<option value="LOSSLESS">FLAC (Lossless)</option>
|
||||
<option value="MP3_320">MP3 320kbps</option>
|
||||
<option value="HIGH">AAC 320kbps</option>
|
||||
<option value="LOW">AAC 96kbps</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Lossless Container</span>
|
||||
<span class="description">Container format for lossless downloads</span>
|
||||
</div>
|
||||
<select id="lossless-container-setting">
|
||||
<option value="flac">FLAC</option>
|
||||
<option value="alac">Apple Lossless</option>
|
||||
<option value="nochange">Don't change</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Cover Art Size</span>
|
||||
<span class="description">Size for downloaded/embedded cover art</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="cover-art-size-setting"
|
||||
class="template-input"
|
||||
style="width: 120px; text-align: right"
|
||||
placeholder="1280x1280"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Show Quality Badges</span>
|
||||
|
|
@ -4618,6 +4613,9 @@
|
|||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">ReplayGain Mode</span>
|
||||
|
|
@ -4671,7 +4669,7 @@
|
|||
<span class="label">Playback Speed</span>
|
||||
<span class="description">Adjust playback speed (0.01x - 100x)</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 12px">
|
||||
<div class="playback-speed-control">
|
||||
<input
|
||||
type="range"
|
||||
id="playback-speed-slider"
|
||||
|
|
@ -4679,7 +4677,7 @@
|
|||
max="4.0"
|
||||
step="0.01"
|
||||
value="1.0"
|
||||
style="width: 150px"
|
||||
class="playback-speed-slider"
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -4688,18 +4686,16 @@
|
|||
max="100"
|
||||
step="0.01"
|
||||
value="1.0"
|
||||
style="
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
font-family: var(--font-family);
|
||||
padding: 4px 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
"
|
||||
class="playback-speed-number-input"
|
||||
/>
|
||||
<span style="font-family: var(--font-family)">x</span>
|
||||
<span class="playback-speed-unit">x</span>
|
||||
<button
|
||||
id="playback-speed-reset"
|
||||
class="btn-secondary"
|
||||
title="Reset to default"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -4971,342 +4967,367 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-tab-content" id="settings-tab-downloads">
|
||||
<div class="settings-list">
|
||||
<div class="settings-group">
|
||||
<div class="info">
|
||||
<span class="label">Compact Artists</span>
|
||||
<span class="description">Show artist cards in a compact, horizontal layout</span>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Zipped Bulk Downloads</span>
|
||||
<span class="description"
|
||||
>Download multiple tracks as a single ZIP file (requires browser
|
||||
support)</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="zipped-bulk-downloads-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="compact-artist-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Compact Albums</span>
|
||||
<span class="description">Show album cards in a compact, horizontal layout</span>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Download Lyrics</span>
|
||||
<span class="description"
|
||||
>Include .lrc files when downloading tracks/albums</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="download-lyrics-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="compact-album-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-tab-content" id="settings-tab-downloads">
|
||||
<div class="settings-list">
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Zipped Bulk Downloads</span>
|
||||
<span class="description"
|
||||
>Download multiple tracks as a single ZIP file (requires browser support)</span
|
||||
>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Romaji Lyrics</span>
|
||||
<span class="description"
|
||||
>Convert Japanese lyrics to Romaji (Latin characters)</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="romaji-lyrics-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="zipped-bulk-downloads-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Download Lyrics</span>
|
||||
<span class="description">Include .lrc files when downloading tracks/albums</span>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Download Quality</span>
|
||||
<span class="description">Quality for track downloads</span>
|
||||
</div>
|
||||
<select id="download-quality-setting">
|
||||
<option value="HI_RES_LOSSLESS">Hi-Res FLAC (24-bit)</option>
|
||||
<option value="LOSSLESS">FLAC (Lossless)</option>
|
||||
<option value="MP3_320">MP3 320kbps</option>
|
||||
<option value="HIGH">AAC 320kbps</option>
|
||||
<option value="LOW">AAC 96kbps</option>
|
||||
</select>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="download-lyrics-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Romaji Lyrics</span>
|
||||
<span class="description"
|
||||
>Convert Japanese lyrics to Romaji (Latin characters)</span
|
||||
>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Lossless Container</span>
|
||||
<span class="description">Container format for lossless downloads</span>
|
||||
</div>
|
||||
<select id="lossless-container-setting">
|
||||
<option value="flac">FLAC</option>
|
||||
<option value="alac">Apple Lossless</option>
|
||||
<option value="nochange">Don't change</option>
|
||||
</select>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="romaji-lyrics-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Filename Template</span>
|
||||
<span class="description"
|
||||
>Customize download filenames. Available: {trackNumber}, {artist}, {title},
|
||||
{album}</span
|
||||
>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="filename-template"
|
||||
class="template-input"
|
||||
placeholder="{trackNumber} - {artist} - {title}"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">ZIP Folder Template</span>
|
||||
<span class="description"
|
||||
>Customize album folder names. Available: {albumTitle}, {albumArtist},
|
||||
{year}</span
|
||||
>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="zip-folder-template"
|
||||
class="template-input"
|
||||
placeholder="{albumTitle} - {albumArtist} - monochrome.tf"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate M3U</span>
|
||||
<span class="description">Include M3U playlist files in downloads</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-m3u-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate M3U8</span>
|
||||
<span class="description">Include extended M3U8 playlist files in downloads</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-m3u8-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate CUE</span>
|
||||
<span class="description"
|
||||
>Include CUE sheets for gapless playback in downloads</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-cue-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate NFO</span>
|
||||
<span class="description">Include NFO files for media center compatibility</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-nfo-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate JSON</span>
|
||||
<span class="description">Include JSON files with rich metadata</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-json-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Relative Paths</span>
|
||||
<span class="description">Use relative paths in playlist files</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="relative-paths-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Separate Discs in ZIP</span>
|
||||
<span class="description"
|
||||
>Put tracks in Disc folders when a release has multiple discs</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="separate-discs-zip-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-tab-content" id="settings-tab-system">
|
||||
<div class="settings-list">
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Keyboard Shortcuts</span>
|
||||
<span class="description">View and customize keyboard shortcuts</span>
|
||||
</div>
|
||||
<button id="customize-shortcuts-btn" class="btn-secondary">Customize</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Cache</span>
|
||||
<span class="description" id="cache-info"
|
||||
>Stores API responses to reduce requests</span
|
||||
>
|
||||
</div>
|
||||
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Auto-Update App</span>
|
||||
<span class="description"
|
||||
>Automatically reload when a new version is available</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="pwa-auto-update-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item" id="desktop-update-container" style="display: none">
|
||||
<div class="info">
|
||||
<span class="label">Desktop Update</span>
|
||||
<span class="description">Check for updates to the desktop application</span>
|
||||
</div>
|
||||
<button id="check-desktop-updates-btn" class="btn-secondary">Check for Updates</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Analytics</span>
|
||||
<span class="description">Send anonymous usage data to help improve the app</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="analytics-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Reset Local Data</span>
|
||||
<span class="description"
|
||||
>Clear all local storage and cached data (does not affect cloud sync)</span
|
||||
>
|
||||
</div>
|
||||
<button id="reset-local-data-btn" class="btn-secondary danger">Reset</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Clear Cloud Data</span>
|
||||
<span class="description"
|
||||
>Delete all your data from the cloud (cannot be undone)</span
|
||||
>
|
||||
</div>
|
||||
<button id="firebase-clear-cloud-btn" class="btn-secondary danger">
|
||||
Clear Cloud Data
|
||||
</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Backup & Restore</span>
|
||||
<span class="description">Export or import your library and history as JSON</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem">
|
||||
<button id="export-library-btn" class="btn-secondary">Export</button>
|
||||
<button id="import-library-btn" class="btn-secondary">Import</button>
|
||||
<input type="file" id="import-library-input" style="display: none" accept=".json" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Export All Settings</span>
|
||||
<span class="description">Export all app settings as JSON</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem">
|
||||
<button id="export-settings-btn" class="btn-secondary">Export</button>
|
||||
<button id="import-settings-btn" class="btn-secondary">Import</button>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Cover Art Size</span>
|
||||
<span class="description">Size for downloaded/embedded cover art</span>
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
id="import-settings-input"
|
||||
style="display: none"
|
||||
accept=".json"
|
||||
type="text"
|
||||
id="cover-art-size-setting"
|
||||
class="template-input"
|
||||
style="width: 120px; text-align: right"
|
||||
placeholder="1280x1280"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Filename Template</span>
|
||||
<span class="description"
|
||||
>Customize download filenames. Available: {trackNumber}, {artist}, {title},
|
||||
{album}</span
|
||||
>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="filename-template"
|
||||
class="template-input"
|
||||
placeholder="{trackNumber} - {artist} - {title}"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">ZIP Folder Template</span>
|
||||
<span class="description"
|
||||
>Customize album folder names. Available: {albumTitle}, {albumArtist},
|
||||
{year}</span
|
||||
>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="zip-folder-template"
|
||||
class="template-input"
|
||||
placeholder="{albumTitle} - {albumArtist} - monochrome.tf"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">ADVANCED: Custom Database/Auth</span>
|
||||
<span class="description">Configure custom PocketBase and Firebase instances</span>
|
||||
</div>
|
||||
<button id="custom-db-btn" class="btn-secondary">Configure</button>
|
||||
</div>
|
||||
<div id="api-instance-manager">
|
||||
<div class="setting-item" style="padding-bottom: 1rem; border: none">
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">API Instances</span>
|
||||
<span class="description">Manage and prioritize API instances.</span>
|
||||
<span class="label">Generate M3U</span>
|
||||
<span class="description">Include M3U playlist files in downloads</span>
|
||||
</div>
|
||||
<button id="refresh-speed-test-btn" class="btn-secondary">
|
||||
Refresh Instance List
|
||||
</button>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-m3u-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate M3U8</span>
|
||||
<span class="description"
|
||||
>Include extended M3U8 playlist files in downloads</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-m3u8-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate CUE</span>
|
||||
<span class="description"
|
||||
>Include CUE sheets for gapless playback in downloads</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-cue-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate NFO</span>
|
||||
<span class="description"
|
||||
>Include NFO files for media center compatibility</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-nfo-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Generate JSON</span>
|
||||
<span class="description">Include JSON files with rich metadata</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="generate-json-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<ul id="api-instance-list"></ul>
|
||||
</div>
|
||||
|
||||
<div class="setting-item" style="padding-bottom: 1rem; border-top: 1px solid var(--border)">
|
||||
<div class="info">
|
||||
<span class="label">Blocked Content</span>
|
||||
<span class="description"
|
||||
>Manage artists, albums, and tracks you've blocked from recommendations</span
|
||||
>
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Relative Paths</span>
|
||||
<span class="description">Use relative paths in playlist files</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="relative-paths-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem">
|
||||
<button id="manage-blocked-btn" class="btn-secondary">Manage</button>
|
||||
<button
|
||||
id="clear-all-blocked-btn"
|
||||
class="btn-secondary danger"
|
||||
style="display: none"
|
||||
>
|
||||
Clear All
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Separate Discs in ZIP</span>
|
||||
<span class="description"
|
||||
>Put tracks in Disc folders when a release has multiple discs</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="separate-discs-zip-toggle" />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-content" id="settings-tab-instances">
|
||||
<div class="settings-list">
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">ADVANCED: Custom Database/Auth</span>
|
||||
<span class="description"
|
||||
>Configure custom PocketBase and Firebase instances</span
|
||||
>
|
||||
</div>
|
||||
<button id="custom-db-btn" class="btn-secondary">Configure</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div id="api-instance-manager">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">API Instances</span>
|
||||
<span class="description">Manage and prioritize API instances.</span>
|
||||
</div>
|
||||
<button id="refresh-speed-test-btn" class="btn-secondary">
|
||||
Refresh Instance List
|
||||
</button>
|
||||
</div>
|
||||
<ul id="api-instance-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-content" id="settings-tab-system">
|
||||
<div class="settings-list">
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Keyboard Shortcuts</span>
|
||||
<span class="description">View and customize keyboard shortcuts</span>
|
||||
</div>
|
||||
<button id="customize-shortcuts-btn" class="btn-secondary">Customize</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Cache</span>
|
||||
<span class="description" id="cache-info"
|
||||
>Stores API responses to reduce requests</span
|
||||
>
|
||||
</div>
|
||||
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Auto-Update App</span>
|
||||
<span class="description"
|
||||
>Automatically reload when a new version is available</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="pwa-auto-update-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-item" id="desktop-update-container" style="display: none">
|
||||
<div class="info">
|
||||
<span class="label">Desktop Update</span>
|
||||
<span class="description">Check for updates to the desktop application</span>
|
||||
</div>
|
||||
<button id="check-desktop-updates-btn" class="btn-secondary">
|
||||
Check for Updates
|
||||
</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Analytics</span>
|
||||
<span class="description"
|
||||
>Send anonymous usage data to help improve the app</span
|
||||
>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="analytics-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Reset Local Data</span>
|
||||
<span class="description"
|
||||
>Clear all local storage and cached data (does not affect cloud sync)</span
|
||||
>
|
||||
</div>
|
||||
<button id="reset-local-data-btn" class="btn-secondary danger">Reset</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Clear Cloud Data</span>
|
||||
<span class="description"
|
||||
>Delete all your data from the cloud (cannot be undone)</span
|
||||
>
|
||||
</div>
|
||||
<button id="firebase-clear-cloud-btn" class="btn-secondary danger">
|
||||
Clear Cloud Data
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="blocked-content-list" style="display: none">
|
||||
<div id="blocked-artists-section" style="margin-bottom: 1rem">
|
||||
<h4
|
||||
style="font-size: 0.9rem; margin-bottom: 0.5rem; color: var(--muted-foreground)"
|
||||
>
|
||||
Blocked Artists
|
||||
</h4>
|
||||
<ul id="blocked-artists-list" class="blocked-items-list"></ul>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Backup & Restore</span>
|
||||
<span class="description"
|
||||
>Export or import your library and history as JSON</span
|
||||
>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem">
|
||||
<button id="export-library-btn" class="btn-secondary">Export</button>
|
||||
<button id="import-library-btn" class="btn-secondary">Import</button>
|
||||
<input
|
||||
type="file"
|
||||
id="import-library-input"
|
||||
style="display: none"
|
||||
accept=".json"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="blocked-albums-section" style="margin-bottom: 1rem">
|
||||
<h4
|
||||
style="font-size: 0.9rem; margin-bottom: 0.5rem; color: var(--muted-foreground)"
|
||||
>
|
||||
Blocked Albums
|
||||
</h4>
|
||||
<ul id="blocked-albums-list" class="blocked-items-list"></ul>
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Export All Settings</span>
|
||||
<span class="description">Export all app settings as JSON</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem">
|
||||
<button id="export-settings-btn" class="btn-secondary">Export</button>
|
||||
<button id="import-settings-btn" class="btn-secondary">Import</button>
|
||||
<input
|
||||
type="file"
|
||||
id="import-settings-input"
|
||||
style="display: none"
|
||||
accept=".json"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="blocked-tracks-section" style="margin-bottom: 1rem">
|
||||
<h4
|
||||
style="font-size: 0.9rem; margin-bottom: 0.5rem; color: var(--muted-foreground)"
|
||||
>
|
||||
Blocked Tracks
|
||||
</h4>
|
||||
<ul id="blocked-tracks-list" class="blocked-items-list"></ul>
|
||||
</div>
|
||||
<div
|
||||
id="blocked-empty-message"
|
||||
style="
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
color: var(--muted-foreground);
|
||||
display: none;
|
||||
"
|
||||
>
|
||||
No blocked content
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="setting-item">
|
||||
<div class="info">
|
||||
<span class="label">Blocked Content</span>
|
||||
<span class="description"
|
||||
>Manage artists, albums, and tracks you've blocked from
|
||||
recommendations</span
|
||||
>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem">
|
||||
<button id="manage-blocked-btn" class="btn-secondary">Manage</button>
|
||||
<button
|
||||
id="clear-all-blocked-btn"
|
||||
class="btn-secondary danger"
|
||||
style="display: none"
|
||||
>
|
||||
Clear All
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export class AuthManager {
|
|||
this.user = await auth.get();
|
||||
this.updateUI(this.user);
|
||||
this.authListeners.forEach((listener) => listener(this.user));
|
||||
} catch (error) {
|
||||
} catch {
|
||||
this.user = null;
|
||||
this.updateUI(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -658,4 +658,4 @@ if (pb) {
|
|||
authManager.onAuthStateChanged(syncManager.onAuthStateChanged.bind(syncManager));
|
||||
}
|
||||
|
||||
export { pb, syncManager };
|
||||
export { pb, syncManager };
|
||||
|
|
|
|||
54
js/api.js
54
js/api.js
|
|
@ -1389,38 +1389,38 @@ export class LosslessAPI {
|
|||
}
|
||||
}
|
||||
|
||||
if (quality.endsWith('LOSSLESS')) {
|
||||
try {
|
||||
switch (losslessContainerSettings.getContainer()) {
|
||||
case 'flac':
|
||||
if ((await getExtensionFromBlob(blob)) != 'flac') {
|
||||
blob = await ffmpeg(
|
||||
blob,
|
||||
{ args: ['-c:a', 'copy'] },
|
||||
'output.flac',
|
||||
'audio/flac',
|
||||
onProgress,
|
||||
options.signal
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'alac':
|
||||
if (quality.endsWith('LOSSLESS')) {
|
||||
try {
|
||||
switch (losslessContainerSettings.getContainer()) {
|
||||
case 'flac':
|
||||
if ((await getExtensionFromBlob(blob)) != 'flac') {
|
||||
blob = await ffmpeg(
|
||||
blob,
|
||||
{ args: ['-c:a', 'alac'] },
|
||||
'output.m4a',
|
||||
'audio/mp4',
|
||||
{ args: ['-vn', '-map_metadata', '-1', '-map', '0:a', '-c:a', 'flac'] },
|
||||
'output.flac',
|
||||
'audio/flac',
|
||||
onProgress,
|
||||
options.signal
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error?.name === 'AbortError') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'alac':
|
||||
blob = await ffmpeg(
|
||||
blob,
|
||||
{ args: ['-c:a', 'alac'] },
|
||||
'output.m4a',
|
||||
'audio/mp4',
|
||||
onProgress,
|
||||
options.signal
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error?.name === 'AbortError') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.error('Lossless container conversion failed:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2986,8 +2986,6 @@ function showCustomizeShortcutsModal() {
|
|||
let recordingAction = null;
|
||||
let recordingTimeout = null;
|
||||
|
||||
const shortcuts = keyboardShortcuts.getShortcuts();
|
||||
|
||||
const formatKey = (key) => {
|
||||
if (!key) return 'none';
|
||||
const keyMap = {
|
||||
|
|
|
|||
|
|
@ -195,11 +195,21 @@ export function updateDownloadProgress(trackId, progress) {
|
|||
const percent = progress.totalBytes ? Math.round((progress.receivedBytes / progress.totalBytes) * 100) : 0;
|
||||
|
||||
progressFill.style.width = `${percent}%`;
|
||||
progressFill.style.background = 'var(--highlight)';
|
||||
|
||||
const receivedMB = (progress.receivedBytes / (1024 * 1024)).toFixed(1);
|
||||
const totalMB = progress.totalBytes ? (progress.totalBytes / (1024 * 1024)).toFixed(1) : '?';
|
||||
|
||||
statusEl.textContent = `Downloading: ${receivedMB}MB / ${totalMB}MB (${percent}%)`;
|
||||
} else if (progress.stage === 'encoding') {
|
||||
const percent = progress.progress ? Math.round(progress.progress) : 0;
|
||||
progressFill.style.width = `${percent}%`;
|
||||
progressFill.style.background = '#3b82f6'; // Blue for encoding
|
||||
statusEl.textContent = `Converting: ${percent}%`;
|
||||
} else if (progress.stage === 'finalizing' || progress.stage === 'processing') {
|
||||
progressFill.style.width = '100%';
|
||||
progressFill.style.background = '#3b82f6';
|
||||
statusEl.textContent = progress.message || 'Processing...';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +278,7 @@ function removeBulkDownloadTask(notifEl) {
|
|||
}, 300);
|
||||
}
|
||||
|
||||
async function downloadTrackBlob(track, quality, api, lyricsManager = null, signal = null) {
|
||||
async function downloadTrackBlob(track, quality, api, lyricsManager = null, signal = null, onProgress = null) {
|
||||
let enrichedTrack = {
|
||||
...track,
|
||||
artist: track.artist || (track.artists && track.artists.length > 0 ? track.artists[0] : null),
|
||||
|
|
@ -343,7 +353,7 @@ async function downloadTrackBlob(track, quality, api, lyricsManager = null, sign
|
|||
// Fallback
|
||||
if (downloadQuality !== 'LOSSLESS') {
|
||||
console.warn('Falling back to LOSSLESS (16-bit) download.');
|
||||
return downloadTrackBlob(track, 'LOSSLESS', api, lyricsManager, signal);
|
||||
return downloadTrackBlob(track, 'LOSSLESS', api, lyricsManager, signal, onProgress);
|
||||
}
|
||||
throw dashError;
|
||||
}
|
||||
|
|
@ -357,7 +367,7 @@ async function downloadTrackBlob(track, quality, api, lyricsManager = null, sign
|
|||
|
||||
// Convert to MP3 320kbps if requested
|
||||
if (quality === 'MP3_320') {
|
||||
blob = await encodeToMp3(blob, () => undefined, signal);
|
||||
blob = await encodeToMp3(blob, onProgress || (() => undefined), signal);
|
||||
}
|
||||
|
||||
if (quality.endsWith('LOSSLESS')) {
|
||||
|
|
@ -367,10 +377,10 @@ async function downloadTrackBlob(track, quality, api, lyricsManager = null, sign
|
|||
if ((await getExtensionFromBlob(blob)) != 'flac') {
|
||||
blob = await ffmpeg(
|
||||
blob,
|
||||
{ args: ['-c:a', 'copy'] },
|
||||
{ args: ['-vn', '-map_metadata', '-1', '-map', '0:a', '-c:a', 'flac'] },
|
||||
'output.flac',
|
||||
'audio/flac',
|
||||
() => undefined,
|
||||
onProgress,
|
||||
signal
|
||||
);
|
||||
}
|
||||
|
|
@ -381,7 +391,7 @@ async function downloadTrackBlob(track, quality, api, lyricsManager = null, sign
|
|||
{ args: ['-c:a', 'alac'] },
|
||||
'output.m4a',
|
||||
'audio/mp4',
|
||||
() => undefined,
|
||||
onProgress,
|
||||
signal
|
||||
);
|
||||
break;
|
||||
|
|
@ -556,7 +566,9 @@ async function bulkDownloadToZipStream(
|
|||
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle);
|
||||
|
||||
try {
|
||||
const { blob, extension } = await downloadTrackBlob(track, quality, api, null, signal);
|
||||
const { blob, extension } = await downloadTrackBlob(track, quality, api, null, signal, (p) => {
|
||||
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle, p);
|
||||
});
|
||||
const filename = buildTrackFilename(track, quality, extension);
|
||||
const discNumber = discLayout.resolveDiscNumber(i);
|
||||
yield {
|
||||
|
|
@ -698,7 +710,9 @@ async function bulkDownloadToZipBlob(
|
|||
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle);
|
||||
|
||||
try {
|
||||
const { blob, extension } = await downloadTrackBlob(track, quality, api, null, signal);
|
||||
const { blob, extension } = await downloadTrackBlob(track, quality, api, null, signal, (p) => {
|
||||
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle, p);
|
||||
});
|
||||
const filename = buildTrackFilename(track, quality, extension);
|
||||
const discNumber = discLayout.resolveDiscNumber(i);
|
||||
yield {
|
||||
|
|
@ -841,7 +855,9 @@ async function bulkDownloadToZipNeutralino(
|
|||
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle);
|
||||
|
||||
try {
|
||||
const { blob, extension } = await downloadTrackBlob(track, quality, api, null, signal);
|
||||
const { blob, extension } = await downloadTrackBlob(track, quality, api, null, signal, (p) => {
|
||||
updateBulkDownloadProgress(notification, i, tracks.length, trackTitle, p);
|
||||
});
|
||||
const filename = buildTrackFilename(track, quality, extension);
|
||||
const discNumber = discLayout.resolveDiscNumber(i);
|
||||
yield {
|
||||
|
|
@ -1303,12 +1319,21 @@ function createBulkDownloadNotification(type, name, _totalItems) {
|
|||
return notifEl;
|
||||
}
|
||||
|
||||
function updateBulkDownloadProgress(notifEl, current, total, currentItem) {
|
||||
function updateBulkDownloadProgress(notifEl, current, total, currentItem, ffmpegProgress = null) {
|
||||
const progressFill = notifEl.querySelector('.download-progress-fill');
|
||||
const statusEl = notifEl.querySelector('.download-status');
|
||||
|
||||
if (ffmpegProgress && (ffmpegProgress.stage === 'encoding' || ffmpegProgress.stage === 'finalizing')) {
|
||||
const percent = ffmpegProgress.progress ? Math.round(ffmpegProgress.progress) : 100;
|
||||
progressFill.style.width = `${percent}%`;
|
||||
progressFill.style.background = '#3b82f6'; // Blue for encoding
|
||||
statusEl.textContent = `Converting ${current}/${total}: ${percent}%`;
|
||||
return;
|
||||
}
|
||||
|
||||
const percent = total > 0 ? Math.round((current / total) * 100) : 0;
|
||||
progressFill.style.width = `${percent}%`;
|
||||
progressFill.style.background = 'var(--highlight)';
|
||||
statusEl.textContent = `${current}/${total} - ${currentItem}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -779,9 +779,6 @@ export function openLyricsPanel(track, audioPlayer, lyricsManager, forceOpen = f
|
|||
const offsetDisplay = manager.getOffsetDisplayString(manager.timingOffset);
|
||||
|
||||
container.innerHTML = `
|
||||
<button id="close-side-panel-btn" class="btn-icon" title="Close">
|
||||
${SVG_CLOSE}
|
||||
</button>
|
||||
<div class="lyrics-timing-controls">
|
||||
<button id="lyrics-timing-minus-btn" class="btn-icon" title="Decrease delay (lyrics earlier) -0.5s">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
|
|
@ -810,6 +807,9 @@ export function openLyricsPanel(track, audioPlayer, lyricsManager, forceOpen = f
|
|||
<button id="genius-toggle-btn" class="btn-icon ${isGeniusMode ? 'active-genius' : ''}" title="Genius Mode" style="${isGeniusMode ? 'color: #ffff64;' : ''}">
|
||||
${isGeniusMode ? SVG_GENIUS_ACTIVE : SVG_GENIUS_INACTIVE}
|
||||
</button>
|
||||
<button id="close-side-panel-btn" class="btn-icon" title="Close">
|
||||
${SVG_CLOSE}
|
||||
</button>
|
||||
`;
|
||||
|
||||
container.querySelector('#close-side-panel-btn').addEventListener('click', () => {
|
||||
|
|
|
|||
|
|
@ -1385,9 +1385,9 @@ function createStringAtom(type, value, truncateType = true) {
|
|||
|
||||
function createUserAtom(namespace, name, value) {
|
||||
const encoder = new TextEncoder();
|
||||
const dashBytes = encoder.encode('----'); // User-defined atom type
|
||||
encoder.encode('----'); // User-defined atom type
|
||||
const namespaceBytes = encoder.encode(namespace);
|
||||
const meanBytes = encoder.encode('mean'); // Standard 'mean' atom for namespace
|
||||
encoder.encode('mean'); // Standard 'mean' atom for namespace
|
||||
const nameBytes = encoder.encode(name);
|
||||
const valueBytes = encoder.encode('\x00\x00\x00\x01\x00\x00\x00\x00' + value);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,12 +37,14 @@ let currentFavoriteAlbums = [];
|
|||
const api = new MusicAPI(apiSettings);
|
||||
|
||||
async function uploadImage(file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const response = await fetch('/upload', { method: 'POST', body: formData });
|
||||
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
|
||||
const data = await response.json();
|
||||
if (!data.success) throw new Error(data.error || 'Upload failed');
|
||||
return data.url;
|
||||
} catch (error) {
|
||||
console.error('Upload error:', error);
|
||||
|
|
|
|||
|
|
@ -331,12 +331,12 @@ export class QobuzAPI {
|
|||
}
|
||||
|
||||
// Similar/recommendation methods
|
||||
async getSimilarArtists(artistId) {
|
||||
async getSimilarArtists(_artistId) {
|
||||
// Qobuz doesn't have a direct similar artists endpoint in this simplified API
|
||||
return [];
|
||||
}
|
||||
|
||||
async getSimilarAlbums(albumId) {
|
||||
async getSimilarAlbums(_albumId) {
|
||||
// Qobuz doesn't have a direct similar albums endpoint in this simplified API
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import {
|
|||
musicProviderSettings,
|
||||
analyticsSettings,
|
||||
modalSettings,
|
||||
keyboardShortcuts,
|
||||
} from './storage.js';
|
||||
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
||||
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
||||
|
|
@ -901,33 +900,56 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
|||
// ========================================
|
||||
const playbackSpeedSlider = document.getElementById('playback-speed-slider');
|
||||
const playbackSpeedInput = document.getElementById('playback-speed-input');
|
||||
const playbackSpeedReset = document.getElementById('playback-speed-reset');
|
||||
|
||||
if (playbackSpeedSlider && playbackSpeedInput) {
|
||||
const currentSpeed = audioEffectsSettings.getSpeed();
|
||||
// Clamp slider to its range (0.25-4), but show actual value in input
|
||||
playbackSpeedSlider.value = Math.max(0.25, Math.min(4.0, currentSpeed));
|
||||
playbackSpeedInput.value = currentSpeed;
|
||||
|
||||
// Slider only controls 0.25-4 range
|
||||
playbackSpeedSlider.addEventListener('input', (e) => {
|
||||
const speed = parseFloat(e.target.value) || 1.0;
|
||||
playbackSpeedInput.value = speed;
|
||||
player.setPlaybackSpeed(speed);
|
||||
});
|
||||
|
||||
// Input allows full 0.01-100 range
|
||||
const handleInputChange = () => {
|
||||
const speed = parseFloat(playbackSpeedInput.value) || 1.0;
|
||||
const validSpeed = Math.max(0.01, Math.min(100, speed));
|
||||
// Helper function to update both controls
|
||||
const updatePlaybackSpeedControls = (speed) => {
|
||||
const validSpeed = Math.max(0.01, Math.min(100, parseFloat(speed) || 1.0));
|
||||
playbackSpeedInput.value = validSpeed;
|
||||
// Only update slider if value is within slider range
|
||||
if (validSpeed >= 0.25 && validSpeed <= 4.0) {
|
||||
playbackSpeedSlider.value = validSpeed;
|
||||
}
|
||||
player.setPlaybackSpeed(validSpeed);
|
||||
return validSpeed;
|
||||
};
|
||||
|
||||
playbackSpeedInput.addEventListener('change', handleInputChange);
|
||||
playbackSpeedInput.addEventListener('blur', handleInputChange);
|
||||
// Initialize with current value
|
||||
const currentSpeed = audioEffectsSettings.getSpeed();
|
||||
updatePlaybackSpeedControls(currentSpeed);
|
||||
|
||||
playbackSpeedSlider.addEventListener('input', (e) => {
|
||||
const speed = parseFloat(e.target.value);
|
||||
playbackSpeedInput.value = speed;
|
||||
audioEffectsSettings.setSpeed(speed);
|
||||
player.setPlaybackSpeed(speed);
|
||||
});
|
||||
|
||||
playbackSpeedInput.addEventListener('input', (e) => {
|
||||
const speed = parseFloat(e.target.value);
|
||||
if (!isNaN(speed) && speed >= 0.01 && speed <= 100) {
|
||||
if (speed >= 0.25 && speed <= 4.0) {
|
||||
playbackSpeedSlider.value = speed;
|
||||
}
|
||||
audioEffectsSettings.setSpeed(speed);
|
||||
player.setPlaybackSpeed(speed);
|
||||
}
|
||||
});
|
||||
|
||||
playbackSpeedInput.addEventListener('change', (e) => {
|
||||
const speed = parseFloat(e.target.value);
|
||||
const validSpeed = updatePlaybackSpeedControls(speed);
|
||||
audioEffectsSettings.setSpeed(validSpeed);
|
||||
player.setPlaybackSpeed(validSpeed);
|
||||
});
|
||||
|
||||
if (playbackSpeedReset) {
|
||||
playbackSpeedReset.addEventListener('click', () => {
|
||||
const defaultSpeed = audioEffectsSettings.resetSpeed();
|
||||
updatePlaybackSpeedControls(defaultSpeed);
|
||||
player.setPlaybackSpeed(defaultSpeed);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
|
|
@ -2305,20 +2327,20 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
|||
updateButterchurnSettingsVisibility();
|
||||
}
|
||||
|
||||
// Watch for audio tab becoming active and refresh presets
|
||||
const audioTabContent = document.getElementById('settings-tab-audio');
|
||||
if (audioTabContent) {
|
||||
// Watch for appearance tab becoming active and refresh presets
|
||||
const appearanceTabContent = document.getElementById('settings-tab-appearance');
|
||||
if (appearanceTabContent) {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
||||
if (audioTabContent.classList.contains('active')) {
|
||||
console.log('[Settings] Audio tab became active, refreshing presets');
|
||||
if (appearanceTabContent.classList.contains('active')) {
|
||||
console.log('[Settings] Appearance tab became active, refreshing presets');
|
||||
updateButterchurnSettingsVisibility();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(audioTabContent, { attributes: true });
|
||||
observer.observe(appearanceTabContent, { attributes: true });
|
||||
}
|
||||
|
||||
// Visualizer Mode Select
|
||||
|
|
|
|||
|
|
@ -1367,6 +1367,11 @@ export const audioEffectsSettings = {
|
|||
localStorage.setItem(this.SPEED_KEY, validSpeed.toString());
|
||||
},
|
||||
|
||||
resetSpeed() {
|
||||
this.setSpeed(1.0);
|
||||
return 1.0;
|
||||
},
|
||||
|
||||
// Preserve pitch when changing speed (default true)
|
||||
isPreservePitchEnabled() {
|
||||
try {
|
||||
|
|
|
|||
24
js/ui.js
24
js/ui.js
|
|
@ -1477,6 +1477,30 @@ export class UIRenderer {
|
|||
updateFsVolumeUI();
|
||||
};
|
||||
|
||||
const handleFsVolumeWheel = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const delta = e.deltaY > 0 ? -0.05 : 0.05;
|
||||
const currentVolume = this.player.userVolume;
|
||||
const newVolume = Math.max(0, Math.min(1, currentVolume + delta));
|
||||
|
||||
if (delta > 0 && audioPlayer.muted) {
|
||||
audioPlayer.muted = false;
|
||||
localStorage.setItem('muted', false);
|
||||
}
|
||||
|
||||
this.player.setVolume(newVolume);
|
||||
updateFsVolumeUI();
|
||||
};
|
||||
|
||||
[fsVolumeBar, fsVolumeBtn].forEach((el) => {
|
||||
if (el._fsVolumeWheelHandler) {
|
||||
el.removeEventListener('wheel', el._fsVolumeWheelHandler);
|
||||
}
|
||||
el._fsVolumeWheelHandler = handleFsVolumeWheel;
|
||||
el.addEventListener('wheel', handleFsVolumeWheel, { passive: false });
|
||||
});
|
||||
|
||||
const setFsVolume = (e) => {
|
||||
const rect = fsVolumeBar.getBoundingClientRect();
|
||||
const position = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
||||
|
|
|
|||
1944
package-lock.json
generated
1944
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -32,6 +32,7 @@
|
|||
"@neutralinojs/neu": "^11.7.0",
|
||||
"eslint": "^9.39.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"formidable": "^3.5.4",
|
||||
"globals": "^17.4.0",
|
||||
"htmlhint": "^1.9.1",
|
||||
"miniflare": "^4.20260301.1",
|
||||
|
|
@ -45,7 +46,8 @@
|
|||
},
|
||||
"overrides": {
|
||||
"sourcemap-codec": "npm:@jridgewell/sourcemap-codec@^1.4.14",
|
||||
"source-map": "^0.7.4"
|
||||
"source-map": "^0.7.4",
|
||||
"serialize-javascript": "^7.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ffmpeg/ffmpeg": "^0.12.15",
|
||||
|
|
@ -60,7 +62,6 @@
|
|||
"fuse.js": "^7.1.0",
|
||||
"hls.js": "^1.6.15",
|
||||
"jose": "^6.1.3",
|
||||
"npm": "^11.11.0",
|
||||
"pocketbase": "^0.26.8"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
219
styles.css
219
styles.css
|
|
@ -381,18 +381,6 @@ body {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
background-color: var(--muted);
|
||||
border: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.text-link {
|
||||
color: var(--primary);
|
||||
text-decoration: underline;
|
||||
|
|
@ -499,6 +487,75 @@ kbd {
|
|||
margin: var(--space-2);
|
||||
}
|
||||
|
||||
/* Base Elements */
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
background-color: var(--muted);
|
||||
border: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
outline: none;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 2px rgb(var(--highlight-rgb) / 0.2);
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.m-3 {
|
||||
margin: var(--space-3);
|
||||
}
|
||||
|
|
@ -964,23 +1021,6 @@ kbd {
|
|||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.sidebar-nav.bottom {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav.bottom .nav-item a {
|
||||
padding: 0.5rem 0.75rem;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sidebar-nav.bottom .nav-item a svg {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
grid-area: main;
|
||||
overflow-y: auto;
|
||||
|
|
@ -1134,6 +1174,23 @@ kbd {
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav.bottom {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar-nav.bottom .nav-item a {
|
||||
padding: 0.5rem 0.75rem;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sidebar-nav.bottom .nav-item a svg {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.pinned-items-header {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
|
|
@ -1772,6 +1829,17 @@ input[type='search']::-webkit-search-cancel-button {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-like-btn:hover,
|
||||
.card-menu-btn:hover,
|
||||
.edit-playlist-btn:hover,
|
||||
.delete-playlist-btn:hover {
|
||||
background: rgb(0, 0, 0, 0.7) !important;
|
||||
transform: scale(1.1) rotate(5deg) !important;
|
||||
|
||||
/* Playful rotation */
|
||||
border-color: rgb(255, 255, 255, 0.3) !important;
|
||||
}
|
||||
|
||||
.card:hover .card-like-btn,
|
||||
.card:hover .card-menu-btn,
|
||||
.card-like-btn.active,
|
||||
|
|
@ -1784,17 +1852,6 @@ input[type='search']::-webkit-search-cancel-button {
|
|||
/* Slight delay for staggered feel */
|
||||
}
|
||||
|
||||
.card-like-btn:hover,
|
||||
.card-menu-btn:hover,
|
||||
.edit-playlist-btn:hover,
|
||||
.delete-playlist-btn:hover {
|
||||
background: rgb(0, 0, 0, 0.7) !important;
|
||||
transform: scale(1.1) rotate(5deg) !important;
|
||||
|
||||
/* Playful rotation */
|
||||
border-color: rgb(255, 255, 255, 0.3) !important;
|
||||
}
|
||||
|
||||
.card-like-btn.active {
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
|
|
@ -2474,6 +2531,7 @@ input[type='search']::-webkit-search-cancel-button {
|
|||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
font-size: 0.8333rem;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
|
|
@ -2617,6 +2675,65 @@ input[type='search']::-webkit-search-cancel-button {
|
|||
width: 100px;
|
||||
}
|
||||
|
||||
.playback-speed-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.playback-speed-slider {
|
||||
appearance: none;
|
||||
width: 150px;
|
||||
height: 6px;
|
||||
background: var(--border);
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.playback-speed-slider::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: var(--primary);
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.playback-speed-slider::-webkit-slider-thumb:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.playback-speed-number-input {
|
||||
width: 80px;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--input);
|
||||
color: var(--foreground);
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.playback-speed-unit {
|
||||
font-size: 0.9rem;
|
||||
color: var(--muted-foreground);
|
||||
min-width: 1rem;
|
||||
}
|
||||
|
||||
.playback-speed-number-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* Hide arrows/spinners for number input */
|
||||
.playback-speed-number-input::-webkit-outer-spin-button,
|
||||
.playback-speed-number-input::-webkit-inner-spin-button {
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.template-input {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
|
|
@ -3623,10 +3740,6 @@ input:checked + .slider::before {
|
|||
}
|
||||
|
||||
/* When UI is hidden, only toggle button stays visible at right edge (when .visible class is added) */
|
||||
#fullscreen-cover-overlay.ui-hidden #toggle-ui-btn {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#fullscreen-cover-overlay.ui-hidden .fullscreen-lyrics-toggle,
|
||||
#fullscreen-cover-overlay.ui-hidden #close-fullscreen-cover-btn {
|
||||
|
|
@ -6909,24 +7022,6 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
|
|||
color: var(--primary-foreground);
|
||||
}
|
||||
|
||||
/* Inputs & Form Elements */
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
transition:
|
||||
border-color var(--transition-fast),
|
||||
box-shadow var(--transition-fast),
|
||||
background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--ring);
|
||||
box-shadow: 0 0 0 3px rgb(var(--highlight-rgb), 0.2);
|
||||
}
|
||||
|
||||
.modal.active .modal-content {
|
||||
animation: pop-in var(--transition-normal) var(--ease-out-back);
|
||||
box-shadow: var(--shadow-2xl);
|
||||
|
|
|
|||
84
vite-plugin-upload.js
Normal file
84
vite-plugin-upload.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { formidable } from 'formidable';
|
||||
import fs from 'fs';
|
||||
import { Blob } from 'buffer';
|
||||
import { loadEnv } from 'vite';
|
||||
|
||||
export default function uploadPlugin() {
|
||||
let env = {};
|
||||
|
||||
const handler = async (req, res, next) => {
|
||||
if (req.url === '/upload' && req.method === 'POST') {
|
||||
const form = formidable({});
|
||||
|
||||
try {
|
||||
const [_fields, files] = await form.parse(req);
|
||||
const uploadedFile = files.file?.[0];
|
||||
|
||||
if (!uploadedFile) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'No file provided' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const fileData = fs.readFileSync(uploadedFile.filepath);
|
||||
const useR2 = env.R2_ENABLED === 'true';
|
||||
|
||||
let url;
|
||||
|
||||
if (useR2) {
|
||||
// We could implement R2 upload here too, but for simplicity in dev
|
||||
// we'll stick to catbox unless specifically requested to match R2 perfectly.
|
||||
// However, to be helpful, let's at least mention it.
|
||||
console.log('R2 upload detected in env, but dev plugin is using catbox fallback for now.');
|
||||
}
|
||||
|
||||
// Forward to catbox.moe (default production behavior when R2 is disabled)
|
||||
const formData = new FormData();
|
||||
formData.append('reqtype', 'fileupload');
|
||||
formData.append(
|
||||
'fileToUpload',
|
||||
new Blob([fileData], { type: uploadedFile.mimetype }),
|
||||
uploadedFile.originalFilename
|
||||
);
|
||||
|
||||
const response = await fetch('https://catbox.moe/user/api.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
url = await response.text();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Upload failed: ${url}`);
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
url: url.trim(),
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('Local upload error:', err);
|
||||
res.statusCode = 500;
|
||||
res.end(JSON.stringify({ success: false, error: err.message }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'upload-plugin',
|
||||
config(_, { mode }) {
|
||||
env = loadEnv(mode, process.cwd(), '');
|
||||
},
|
||||
configureServer(server) {
|
||||
server.middlewares.use(handler);
|
||||
},
|
||||
configurePreviewServer(server) {
|
||||
server.middlewares.use(handler);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
|
|||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import neutralino from 'vite-plugin-neutralino';
|
||||
import authGatePlugin from './vite-plugin-auth-gate.js';
|
||||
import uploadPlugin from './vite-plugin-upload.js';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const IS_NEUTRALINO = mode === 'neutralino';
|
||||
|
|
@ -34,6 +35,7 @@ export default defineConfig(({ mode }) => {
|
|||
plugins: [
|
||||
IS_NEUTRALINO && neutralino(),
|
||||
authGatePlugin(),
|
||||
uploadPlugin(),
|
||||
VitePWA({
|
||||
registerType: 'prompt',
|
||||
workbox: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue