chore: update ESLint config (#80)

Co-authored-by: dudubtw <carloseduardo108090@hotmail.com>
This commit is contained in:
Bas van den Wollenberg
2024-10-21 00:49:31 +02:00
committed by GitHub
parent 0778a3d306
commit 5eb6a34397
25 changed files with 479 additions and 748 deletions

View File

@@ -35,7 +35,7 @@ jobs:
uses: ./.github/actions/setup
- name: Run Eslint
run: npm run lint:check
run: npm run lint
format:
timeout-minutes: 10

View File

@@ -1,5 +1,9 @@
/** @type {import("prettier").Options} */
export default {
printWidth: 100,
plugins: ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
plugins: [
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss",
"prettier-plugin-packagejson",
],
};

21
eslint.config.js Normal file
View File

@@ -0,0 +1,21 @@
import eslint from "@eslint/js";
import solid from "eslint-plugin-solid";
import tseslint from "typescript-eslint";
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
ignores: ["**/out", "**/build", "**/dist"],
},
{
files: ["**/*"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
},
},
{
files: ["src/renderer/**/*.tsx"],
...solid.configs["flat/recommended"],
},
);

View File

@@ -1,37 +0,0 @@
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
export default [
{
ignores: ["**/out", "**/build", "**/dist"],
},
...compat.extends("plugin:@typescript-eslint/recommended"),
{
plugins: {
"@typescript-eslint": typescriptEslint,
},
languageOptions: {
parser: tsParser,
},
},
{
files: ["**/*"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
},
},
];

398
package-lock.json generated
View File

@@ -12,7 +12,7 @@
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"@floating-ui/dom": "^1.6.11",
"@types/graceful-fs": "^4.1.6",
"@types/graceful-fs": "^4.1.9",
"@xhayper/discord-rpc": "^1.2.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
@@ -30,25 +30,27 @@
"@electron-toolkit/tsconfig": "^1.0.1",
"@electron/notarize": "^2.5.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.11.1",
"@eslint/js": "^9.13.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/node": "^22.7.4",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"@types/eslint__eslintrc": "^2.1.2",
"@types/eslint__js": "^8.42.3",
"@types/node": "^22.7.7",
"autoprefixer": "^10.4.20",
"electron": "^32.1.2",
"electron-builder": "^25.1.7",
"electron-vite": "^2.3.0",
"eslint": "^9.11.1",
"eslint": "^9.13.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-solid": "^0.14.3",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"prettier-plugin-packagejson": "^2.5.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"solid-js": "^1.7.6",
"tailwindcss": "^3.4.13",
"typescript": "~5.5.0",
"typescript": "~5.6.3",
"typescript-eslint": "^8.10.0",
"vite": "^5.4.8",
"vite-plugin-lucide-preprocess": "^1.1.1",
"vite-plugin-solid": "^2.7.0"
@@ -1348,9 +1350,9 @@
}
},
"node_modules/@eslint/core": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz",
"integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
"integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -1382,9 +1384,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.11.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz",
"integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==",
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
"integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1575,6 +1577,30 @@
"dev": true,
"license": "MIT"
},
"node_modules/@humanfs/core": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz",
"integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.5",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz",
"integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.0",
"@humanwhocodes/retry": "^0.3.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -1590,9 +1616,9 @@
}
},
"node_modules/@humanwhocodes/retry": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
"integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
"integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -2767,6 +2793,37 @@
"@types/ms": "*"
}
},
"node_modules/@types/eslint": {
"version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
}
},
"node_modules/@types/eslint__eslintrc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@types/eslint__eslintrc/-/eslint__eslintrc-2.1.2.tgz",
"integrity": "sha512-qXvzPFY7Rz05xD8ZApXJ3S8xStQD2Ibzu3EFIF0UMNOAfLY5xUu3H61q0JrHo2OXD6rcFG75yUxNQbkKtFKBSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "*"
}
},
"node_modules/@types/eslint__js": {
"version": "8.42.3",
"resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz",
"integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -2841,9 +2898,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.7.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
"integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
"version": "22.7.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz",
"integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
@@ -2889,17 +2946,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz",
"integrity": "sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz",
"integrity": "sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.8.0",
"@typescript-eslint/type-utils": "8.8.0",
"@typescript-eslint/utils": "8.8.0",
"@typescript-eslint/visitor-keys": "8.8.0",
"@typescript-eslint/scope-manager": "8.10.0",
"@typescript-eslint/type-utils": "8.10.0",
"@typescript-eslint/utils": "8.10.0",
"@typescript-eslint/visitor-keys": "8.10.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -2923,16 +2980,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.0.tgz",
"integrity": "sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.10.0.tgz",
"integrity": "sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/scope-manager": "8.8.0",
"@typescript-eslint/types": "8.8.0",
"@typescript-eslint/typescript-estree": "8.8.0",
"@typescript-eslint/visitor-keys": "8.8.0",
"@typescript-eslint/scope-manager": "8.10.0",
"@typescript-eslint/types": "8.10.0",
"@typescript-eslint/typescript-estree": "8.10.0",
"@typescript-eslint/visitor-keys": "8.10.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2952,14 +3009,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz",
"integrity": "sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.10.0.tgz",
"integrity": "sha512-AgCaEjhfql9MDKjMUxWvH7HjLeBqMCBfIaBbzzIcBbQPZE7CPh1m6FF+L75NUMJFMLYhCywJXIDEMa3//1A0dw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.8.0",
"@typescript-eslint/visitor-keys": "8.8.0"
"@typescript-eslint/types": "8.10.0",
"@typescript-eslint/visitor-keys": "8.10.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2970,14 +3027,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz",
"integrity": "sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz",
"integrity": "sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "8.8.0",
"@typescript-eslint/utils": "8.8.0",
"@typescript-eslint/typescript-estree": "8.10.0",
"@typescript-eslint/utils": "8.10.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -2995,9 +3052,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.0.tgz",
"integrity": "sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.10.0.tgz",
"integrity": "sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3009,14 +3066,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz",
"integrity": "sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.10.0.tgz",
"integrity": "sha512-3OE0nlcOHaMvQ8Xu5gAfME3/tWVDpb/HxtpUZ1WeOAksZ/h/gwrBzCklaGzwZT97/lBbbxJ16dMA98JMEngW4w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/types": "8.8.0",
"@typescript-eslint/visitor-keys": "8.8.0",
"@typescript-eslint/types": "8.10.0",
"@typescript-eslint/visitor-keys": "8.10.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -3064,16 +3121,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz",
"integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.10.0.tgz",
"integrity": "sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.8.0",
"@typescript-eslint/types": "8.8.0",
"@typescript-eslint/typescript-estree": "8.8.0"
"@typescript-eslint/scope-manager": "8.10.0",
"@typescript-eslint/types": "8.10.0",
"@typescript-eslint/typescript-estree": "8.10.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3087,13 +3144,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz",
"integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==",
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz",
"integrity": "sha512-k8nekgqwr7FadWk548Lfph6V3r9OVqjzAIVskE7orMZR23cGJjAOVazsZSJW+ElyjfTM4wx/1g88Mi70DDtG9A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.8.0",
"@typescript-eslint/types": "8.10.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -4668,6 +4725,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/detect-indent": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz",
"integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.20"
}
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
@@ -4677,6 +4744,19 @@
"node": ">=8"
}
},
"node_modules/detect-newline": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.1.tgz",
"integrity": "sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
@@ -4712,6 +4792,19 @@
"p-limit": "^3.1.0 "
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-type": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/discord-api-types": {
"version": "0.37.101",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.101.tgz",
@@ -5182,22 +5275,22 @@
}
},
"node_modules/eslint": {
"version": "9.11.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.11.1.tgz",
"integrity": "sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==",
"version": "9.13.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
"integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.11.0",
"@eslint/config-array": "^0.18.0",
"@eslint/core": "^0.6.0",
"@eslint/core": "^0.7.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "9.11.1",
"@eslint/js": "9.13.0",
"@eslint/plugin-kit": "^0.2.0",
"@humanfs/node": "^0.16.5",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
"@humanwhocodes/retry": "^0.3.1",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@@ -5205,9 +5298,9 @@
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.0.2",
"eslint-visitor-keys": "^4.0.0",
"espree": "^10.1.0",
"eslint-scope": "^8.1.0",
"eslint-visitor-keys": "^4.1.0",
"espree": "^10.2.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -5217,13 +5310,11 @@
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
"json-stable-stringify-without-jsonify": "^1.0.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
"text-table": "^0.2.0"
},
"bin": {
@@ -5884,6 +5975,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-stdin": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
"integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
@@ -5899,6 +6003,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/git-hooks-list": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.1.0.tgz",
"integrity": "sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/fisker/git-hooks-list?sponsor=1"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -5982,6 +6096,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/globby": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz",
"integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"dir-glob": "^3.0.1",
"fast-glob": "^3.3.0",
"ignore": "^5.2.4",
"merge2": "^1.4.1",
"slash": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -6509,14 +6643,17 @@
"node": ">=0.12.0"
}
},
"node_modules/is-path-inside": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-potential-custom-element-name": {
@@ -7853,6 +7990,16 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/pe-library": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz",
@@ -8131,6 +8278,25 @@
"node": ">=6.0.0"
}
},
"node_modules/prettier-plugin-packagejson": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.3.tgz",
"integrity": "sha512-ATMEEXr+ywls1kgrZEWl4SBPEm0uDdyDAjyNzUC0/Z8WZTD3RqbJcQDR+Dau+wYkW9KHK6zqQIsFyfn+9aduWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"sort-package-json": "2.10.1",
"synckit": "0.9.2"
},
"peerDependencies": {
"prettier": ">= 1.16.0"
},
"peerDependenciesMeta": {
"prettier": {
"optional": true
}
}
},
"node_modules/prettier-plugin-tailwindcss": {
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.8.tgz",
@@ -8842,6 +9008,19 @@
"node": ">=10"
}
},
"node_modules/slash": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
"integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
@@ -8950,6 +9129,33 @@
"solid-js": "^1.3"
}
},
"node_modules/sort-object-keys": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz",
"integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==",
"dev": true,
"license": "MIT"
},
"node_modules/sort-package-json": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.10.1.tgz",
"integrity": "sha512-d76wfhgUuGypKqY72Unm5LFnMpACbdxXsLPcL27pOsSrmVqH3PztFp1uq+Z22suk15h7vXmTesuh2aEjdCqb5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"detect-indent": "^7.0.1",
"detect-newline": "^4.0.0",
"get-stdin": "^9.0.0",
"git-hooks-list": "^3.0.0",
"globby": "^13.1.2",
"is-plain-obj": "^4.1.0",
"semver": "^7.6.0",
"sort-object-keys": "^1.1.3"
},
"bin": {
"sort-package-json": "cli.js"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -9247,9 +9453,9 @@
"license": "MIT"
},
"node_modules/synckit": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
"integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
"integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9578,9 +9784,9 @@
}
},
"node_modules/typescript": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -9591,6 +9797,30 @@
"node": ">=14.17"
}
},
"node_modules/typescript-eslint": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.10.0.tgz",
"integrity": "sha512-YIu230PeN7z9zpu/EtqCIuRVHPs4iSlqW6TEvjbyDAE3MZsSl2RXBo+5ag+lbABCG8sFM1WVKEXhlQ8Ml8A3Fw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.10.0",
"@typescript-eslint/parser": "8.10.0",
"@typescript-eslint/utils": "8.10.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/undici": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",

View File

@@ -2,32 +2,32 @@
"name": "osu-radio",
"version": "1.0.0",
"description": "Application to play your osu! songs.",
"main": "./out/main/index.js",
"type": "module",
"author": "@CaptSiro",
"homepage": "https://github.com/Team-BTMC/osu-radio",
"author": "@CaptSiro",
"type": "module",
"main": "./out/main/index.js",
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint . --fix",
"lint:check": "eslint .",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
"typecheck": "npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview",
"build": "npm run typecheck && electron-vite build",
"build:linux": "electron-vite build && electron-builder --linux --config",
"build:mac": "electron-vite build && electron-builder --mac --config",
"build:win": "npm run build && electron-builder --win --config",
"dev": "electron-vite dev",
"dev:watch": "electron-vite dev --watch",
"build": "npm run typecheck && electron-vite build",
"format": "prettier --write .",
"format:check": "prettier --check .",
"postinstall": "electron-builder install-app-deps",
"build:win": "npm run build && electron-builder --win --config",
"build:mac": "electron-vite build && electron-builder --mac --config",
"build:linux": "electron-vite build && electron-builder --linux --config"
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"start": "electron-vite preview",
"typecheck": "npm run typecheck:node && npm run typecheck:web",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"@floating-ui/dom": "^1.6.11",
"@types/graceful-fs": "^4.1.6",
"@types/graceful-fs": "^4.1.9",
"@xhayper/discord-rpc": "^1.2.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
@@ -45,25 +45,27 @@
"@electron-toolkit/tsconfig": "^1.0.1",
"@electron/notarize": "^2.5.0",
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.11.1",
"@eslint/js": "^9.13.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/node": "^22.7.4",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"@types/eslint__eslintrc": "^2.1.2",
"@types/eslint__js": "^8.42.3",
"@types/node": "^22.7.7",
"autoprefixer": "^10.4.20",
"electron": "^32.1.2",
"electron-builder": "^25.1.7",
"electron-vite": "^2.3.0",
"eslint": "^9.11.1",
"eslint": "^9.13.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-solid": "^0.14.3",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"prettier-plugin-packagejson": "^2.5.3",
"prettier-plugin-tailwindcss": "^0.6.8",
"solid-js": "^1.7.6",
"tailwindcss": "^3.4.13",
"typescript": "~5.5.0",
"typescript": "~5.6.3",
"typescript-eslint": "^8.10.0",
"vite": "^5.4.8",
"vite-plugin-lucide-preprocess": "^1.1.1",
"vite-plugin-solid": "^2.7.0"

View File

@@ -51,14 +51,14 @@ export class TemplateTokenizer {
switch (this.char) {
case "\0":
return createToken(Tokens.EOF, this.char, this.position);
case "\\":
case "\\": {
const next = this.peek();
if (next === "{" || next === "}") {
this.readChar();
return createToken(Tokens.Text, this.char, this.position);
}
return createToken(Tokens.Text, this.char, this.position);
}
case "{":
return createToken(Tokens.LeftSquirly, this.char, this.position);
case "}":

View File

@@ -52,7 +52,7 @@ async function configureOsuDir(mainWindow: BrowserWindow) {
let tables: Awaited<DirParseResult>;
const settings = Storage.getTable("settings");
do {
while (true) {
await Router.dispatch(mainWindow, "changeScene", "dir-select");
const dir = await dirSubmit();
@@ -91,7 +91,7 @@ async function configureOsuDir(mainWindow: BrowserWindow) {
// All went smoothly. Save osu directory and continue with import procedure
settings.write("osuSongsDir", dir);
break;
} while (true);
}
// Show finished state
await Router.dispatch(mainWindow, "loadingScene::update", {

View File

@@ -1,86 +0,0 @@
import "../assets/css/bar.css";
import { clamp } from "../lib/tungsten/math";
import { Component, createEffect, createSignal, onMount } from "solid-js";
type BarAlignment = "vertical" | "v" | "horizontal" | "h";
function isVertical(alignment?: BarAlignment): boolean {
return alignment === "vertical" || alignment === "v";
}
type BarProps = {
alignment?: BarAlignment;
fill: number;
setFill?: (fill: number) => any;
disabled?: boolean;
};
const Bar: Component<BarProps> = (props) => {
const [fill, setFill] = createSignal(props.fill);
let bar: HTMLDivElement | undefined, handle: HTMLDivElement | undefined;
onMount(() => {
createEffect(() => {
const f = fill();
bar?.style.setProperty("--fill-per", `${clamp(0, 1, f) * 100}%`);
if (props.setFill !== undefined) {
props.setFill(clamp(0, 1, f));
}
});
});
const calculateFill = (evt: PointerEvent) => {
if (props.disabled === true || !bar) {
return;
}
const rect: DOMRect = bar.getBoundingClientRect();
if (isVertical(props.alignment)) {
setFill(clamp(0, 1, -(evt.clientY - rect.top) / rect.height + 1));
return;
}
setFill(clamp(0, 1, (evt.clientX - rect.left) / rect.width));
};
const onDown = (evt: PointerEvent) => {
if (props.disabled === true) {
return;
}
handle?.setPointerCapture(evt.pointerId);
handle?.addEventListener("pointermove", calculateFill);
handle?.addEventListener(
"pointerup",
() => handle.removeEventListener("pointermove", calculateFill),
{ once: true },
);
};
return (
<div
ref={bar}
classList={{
bar: true,
vertical: props.alignment !== undefined,
editable: props.setFill !== undefined,
}}
style={{
"--fill-per": clamp(0, 1, props.fill) * 100 + "%",
}}
onPointerDown={calculateFill}
data-disabled={props.disabled}
>
<div class="filling-container">
<div class="filling"></div>
</div>
<div ref={handle} class="handle" onPointerDown={onDown}></div>
</div>
);
};
export default Bar;

View File

@@ -1,71 +0,0 @@
import "../assets/css/gradient.css";
import Impulse from "../lib/Impulse";
import { Component, createEffect, createSignal, JSXElement, onCleanup, onMount } from "solid-js";
export type GradientColors = {
top: string;
bottom: string;
};
export const [gradientColors, setGradientColors] = createSignal<GradientColors>({
top: "dodgerblue",
bottom: "crimson",
});
type GradientProps = {
classTop?: string;
classBottom?: string;
update?: Impulse;
children: JSXElement;
};
const Gradient: Component<GradientProps> = (props) => {
let bottomLayer: HTMLDivElement | undefined;
const calculateBackground = () => {
if (!bottomLayer) {
return;
}
const rect: DOMRect = bottomLayer.getBoundingClientRect();
bottomLayer.style.setProperty("--left", `${Math.round(-rect.left)}px`);
bottomLayer.style.setProperty("--top", `${Math.round(-rect.top)}px`);
bottomLayer.style.setProperty("--right", `${Math.round(rect.left + rect.width)}px`);
bottomLayer.style.setProperty("--bottom", `${Math.round(rect.top + rect.height)}px`);
bottomLayer.style.setProperty("--width", `${Math.round(window.innerWidth * 1.25)}px`);
bottomLayer.style.setProperty("--height", `${Math.round(window.innerHeight * 1.25)}px`);
};
if (props.update !== undefined) {
props.update.listen(calculateBackground);
}
onMount(() => {
calculateBackground();
window.addEventListener("resize", calculateBackground);
});
onCleanup(() => {
window.removeEventListener("resize", calculateBackground);
});
createEffect(() => {
const colors = gradientColors();
document.documentElement.style.setProperty("--circle-0", colors.top);
document.documentElement.style.setProperty("--circle-1", colors.bottom);
});
return (
<div ref={bottomLayer} class={`bottom-layer ${props.classBottom ?? ""}`}>
<div class={`gradient-layer`}>
<div class={`top-layer ${props.classTop ?? ""}`}>{props.children}</div>
</div>
</div>
);
};
export default Gradient;

View File

@@ -1,49 +0,0 @@
import "../assets/css/select.css";
import { Component, For, onMount, Setter } from "solid-js";
export type SelectOption = {
value: string;
text: string;
selected?: boolean;
};
type SelectProps = {
setValue: Setter<string>;
options: SelectOption[];
selected?: string;
disabled?: boolean;
};
const Select: Component<SelectProps> = (props) => {
let select: HTMLSelectElement | undefined;
onMount(() => {
if (!select) {
return;
}
props.setValue(select.value);
});
return (
<select
class={"button-like select"}
ref={select}
onchange={() => select && props.setValue(select.value)}
disabled={props.disabled}
>
<For each={props.options}>
{(option) => (
<option
value={option.value}
selected={option.selected === true || option.value === props.selected}
>
{option.text}
</option>
)}
</For>
</select>
);
};
export default Select;

View File

@@ -1,85 +0,0 @@
import { Component, JSX, onMount, Setter, Signal } from "solid-js";
type TextFieldProps = {
value: Signal<string>;
setInput?: Setter<HTMLElement | undefined>;
children?: JSX.Element;
};
const TextField: Component<TextFieldProps> = (props) => {
const [value, setValue] = props.value;
let input: HTMLInputElement | undefined;
onMount(() => {
if (!input) {
return;
}
if (props.setInput !== undefined) {
props.setInput(input);
}
input.textContent = value();
});
const onInput = () => {
if (!input) {
return;
}
setValue(
String(input.textContent).replaceAll(
String.fromCharCode(160), // non-breaking space
String.fromCharCode(32), // breaking space
) ?? "",
);
};
const onPaste = (evt: ClipboardEvent) => {
const selection = window.getSelection();
if (selection === null || !evt.clipboardData) return;
evt.stopPropagation();
evt.preventDefault();
selection.deleteFromDocument();
selection.getRangeAt(0).insertNode(document.createTextNode(evt.clipboardData.getData("Text")));
selection.collapseToEnd();
onInput();
};
const clear = () => {
if (!input) {
return;
}
input.textContent = "";
onInput();
input.focus();
};
return (
<div class="button-like flex w-full items-center gap-2 overflow-hidden p-2 focus-within:outline-2 focus-within:transition-none hover:cursor-text">
{props.children}
<div
class="flex-1 overflow-hidden whitespace-nowrap focus:outline-none"
ref={input}
onInput={onInput}
onKeyDown={(evt) => evt.stopPropagation()}
onPaste={onPaste}
contenteditable={true}
spellcheck={false}
></div>
<button
class="mr-1 grid place-items-center bg-transparent p-0 hover:cursor-pointer hover:bg-transparent focus:outline-none"
onClick={clear}
title="Clear text input"
>
{/* TODO: Add clear icon */}
</button>
</div>
);
};
export default TextField;

View File

@@ -2,7 +2,6 @@ import { Optional } from "../../../../@types";
import "../../assets/css/notice/notice.css";
import Impulse from "../../lib/Impulse";
import { none, orDefault, some } from "../../lib/rust-like-utils-client/Optional";
import Gradient from "../Gradient";
import { hideNotice } from "./NoticeContainer";
import { XIcon } from "lucide-solid";
import { Accessor, Component, createSignal } from "solid-js";
@@ -32,7 +31,9 @@ const Notice: Component<NoticeProps> = (props) => {
try {
pauseDrain();
} catch {}
} catch (error) {
console.error(error);
}
};
const [drain, setDrainTime, pauseDrain] = createDrainAnimation(
@@ -53,7 +54,6 @@ const Notice: Component<NoticeProps> = (props) => {
data-id={props.notice.id}
ref={onRef}
>
<Gradient update={props.updateGradient}>
<div class={"notice " + (props.notice.class !== "notice" ? props.notice.class : "")}>
<div class="content">
<div class="head">
@@ -76,7 +76,6 @@ const Notice: Component<NoticeProps> = (props) => {
}}
></div>
</div>
</Gradient>
</div>
);
};

View File

@@ -1,3 +1,3 @@
export default function NoScene() {
return <div class="scene"></div>;
return <div class="scene" />;
}

View File

@@ -1,35 +0,0 @@
import "../../assets/css/search/tag-item.css";
import { XIcon } from "lucide-solid";
import { Component } from "solid-js";
export type TagItemProps = {
name: string;
isSpecial: boolean;
onRemove: (name: string) => any;
onChange: (name: string) => any;
};
const TagItem: Component<TagItemProps> = (props) => {
let container;
const changeState = (evt: Event) => {
evt.preventDefault();
props.onChange(props.name);
};
return (
<div
ref={container}
class={"tag"}
classList={{ special: props.isSpecial === true }}
onContextMenu={changeState}
>
<span>{props.name}</span>
<button onClick={() => props.onRemove(props.name)}>
<XIcon size={20} />
</button>
</div>
);
};
export default TagItem;

View File

@@ -1,160 +0,0 @@
import "../../assets/css/search/tag-select.css";
import Gradient from "../Gradient";
import TextField from "../form/TextField";
import TagItem from "./TagItem";
import { TagIcon } from "lucide-solid";
import { Component, createEffect, createSignal, For, Signal } from "solid-js";
export type Tag = {
name: string;
isSpecial?: boolean;
};
type TagSelectProps = {
/** Must have ```json
* { equals: false }
* ``` */
tags: Signal<Tag[]>;
disabled?: boolean;
};
const TagSelect: Component<TagSelectProps> = (props) => {
const [tags, setTags] = props.tags;
const tagSignal = createSignal("");
const [tagField, setTagField] = createSignal<HTMLElement | undefined>();
let dialog: HTMLDialogElement | undefined;
const openDialog = () => {
dialog?.showModal();
window.dispatchEvent(new Event("resize"));
};
const closeDialog = () => {
dialog?.close();
const field = tagField();
if (field === undefined) {
return;
}
field.textContent = "";
};
const onKeyDown = (evt: KeyboardEvent) => {
if (evt.key !== "Enter") {
return;
}
const input = tagField();
if (input === undefined) {
return;
}
evt.preventDefault();
evt.stopPropagation();
if (input.textContent === null) {
return;
}
const newTags = input.textContent.trim().split(" ");
const oldTags = tags();
for (let i = 0; i < newTags.length; i++) {
if (newTags[i] === "") {
continue;
}
const index = oldTags.findIndex((t) => t.name === newTags[i]);
if (index !== -1) {
continue;
}
oldTags.push({ name: newTags[i] });
}
setTags(oldTags);
input.textContent = "";
input.focus();
};
createEffect(() => {
const input = tagField();
if (input === undefined) {
return;
}
input.addEventListener("keydown", onKeyDown);
});
const onTagRemove = (name: string) => {
const t = tags();
const index = t.findIndex((x) => x.name === name);
if (index === -1) {
return;
}
t.splice(index, 1);
setTags(t);
};
const onTagChange = (name: string) => {
const t = tags();
const index = t.findIndex((x) => x.name === name);
if (index === -1) {
return;
}
t[index] = {
name,
isSpecial: !t[index].isSpecial,
};
setTags(t);
};
return (
<div class={"tags"}>
<button
onClick={openDialog}
disabled={props.disabled}
title={"Add/Remove tags for searching"}
>
<TagIcon size={20} />
</button>
<dialog ref={dialog} class={"tag-select"}>
<Gradient classTop={"tag-select-container"}>
<TextField value={tagSignal} setInput={setTagField} />
<span class={"hint"}>
Type the name of a tag into input above and press enter to add it. After it is added you
can right-click it and change it to exclude songs with given tag.
</span>
<div class={"tags-container"}>
<For each={tags()}>
{(tag: Tag) => (
<TagItem
name={tag.name}
isSpecial={tag.isSpecial === true}
onRemove={onTagRemove}
onChange={onTagChange}
/>
)}
</For>
</div>
<button onClick={closeDialog}>Close</button>
</Gradient>
</dialog>
</div>
);
};
export default TagSelect;

View File

@@ -1,4 +1,4 @@
import { Component, Show } from "solid-js";
import { Component, For, Show } from "solid-js";
type SettingDropdownProps = {
label: string;
@@ -27,9 +27,9 @@ const SettingDropdown: Component<SettingDropdownProps> = (props) => {
disabled={props.disabled}
onChange={changeOption}
>
{Array.from(props.options.keys()).map((option) => (
<option value={option}>{option}</option>
))}
<For each={Array.from(props.options.keys())}>
{(option) => <option value={option}>{option}</option>}
</For>
</select>
</Show>
</div>

View File

@@ -2,7 +2,7 @@ import { cn } from "../../lib/css.utils";
import Dropdown from "../dropdown/Dropdown";
import { changeAudioDevice } from "@renderer/components/song/song.utils";
import { GlobeIcon, LucideIcon, Volume2Icon } from "lucide-solid";
import { Component, createEffect, createSignal, JSX, onMount } from "solid-js";
import { Component, createEffect, createSignal, For, JSX, onMount } from "solid-js";
const Settings: Component = () => {
return (
@@ -22,14 +22,14 @@ type SettingsSectionProps = JSX.IntrinsicElements["div"] & {
Icon: LucideIcon;
};
const SettingsSection: Component<SettingsSectionProps> = ({ title, Icon, children, ...rest }) => {
const SettingsSection: Component<SettingsSectionProps> = (props) => {
return (
<div class={cn("flex flex-col gap-6", rest.class)}>
<div class={cn("flex flex-col gap-6", props.class)}>
<div class="flex items-center gap-3">
<Icon class="text-text opacity-70" size={16} />
<h3 class="text-base text-text">{title}</h3>
<props.Icon class="text-text opacity-70" size={16} />
<h3 class="text-base text-text">{props.title}</h3>
</div>
{children}
{props.children}
</div>
);
};
@@ -39,13 +39,13 @@ type SettingProps = JSX.IntrinsicElements["div"] & {
name: string;
};
const Setting: Component<SettingProps> = ({ label, name, children, ...rest }) => {
const Setting: Component<SettingProps> = (props) => {
return (
<div class={cn("flex flex-col gap-2.5", rest.class)}>
<label class="text-sm font-semibold text-text" for={name}>
{label}
<div class={cn("flex flex-col gap-2.5", props.class)}>
<label class="text-sm font-semibold text-text" for={props.name}>
{props.label}
</label>
{children}
{props.children}
</div>
);
};
@@ -85,9 +85,9 @@ const AudioDeviceSetting: Component = () => {
</Dropdown.SelectTrigger>
<Dropdown.List value={selectedAudioDevice} onValueChange={handleValueChange}>
{Array.from(audioDevices().keys()).map((audioDevice) => (
<Dropdown.Item value={audioDevice}>{audioDevice}</Dropdown.Item>
))}
<For each={Array.from(audioDevices().keys())}>
{(audioDevice) => <Dropdown.Item value={audioDevice}>{audioDevice}</Dropdown.Item>}
</For>
</Dropdown.List>
</Dropdown>
</Setting>

View File

@@ -1,10 +1,10 @@
export default function SongHint(): HTMLElement {
return (
<div class="flex items-center rounded-md bg-black bg-opacity-80 p-2 shadow-lg">
<div class="mr-3 h-12 w-12 rounded-md bg-gray-700"></div>
<div class="mr-3 h-12 w-12 rounded-md bg-gray-700" />
<div class="flex flex-col">
<div class="mb-2 h-4 w-32 rounded bg-gray-700"></div>
<div class="h-3 w-24 rounded bg-gray-600"></div>
<div class="mb-2 h-4 w-32 rounded bg-gray-700" />
<div class="h-3 w-24 rounded bg-gray-600" />
</div>
</div>
) as HTMLElement;

View File

@@ -4,7 +4,7 @@ import SongHint from "../SongHint";
import SongImage from "../SongImage";
import { ignoreClickInContextMenu } from "../context-menu/SongContextMenu";
import { song as selectedSong } from "../song.utils";
import { Component, createSignal, onMount } from "solid-js";
import { Component, createMemo, onMount } from "solid-js";
type SongItemProps = {
song: Song;
@@ -16,71 +16,54 @@ type SongItemProps = {
children?: any;
};
const SongItem: Component<SongItemProps> = ({
group,
onSelect,
song,
children,
draggable: isDraggable,
onDrop,
selectable,
}) => {
const showSignal = createSignal(false);
const [, setCoords] = createSignal<[number, number]>([0, 0], { equals: false });
const SongItem: Component<SongItemProps> = (props) => {
let item: HTMLDivElement | undefined;
const showMenu = (evt: MouseEvent) => {
if (children === undefined) {
showSignal[1](false);
return;
}
setCoords([evt.clientX, evt.clientY]);
showSignal[1](true);
};
onMount(() => {
if (!item) {
return;
}
draggable(item, {
onClick: ignoreClickInContextMenu(() => onSelect(song.path)),
onDrop: onDrop ?? (() => {}),
onClick: ignoreClickInContextMenu(() => props.onSelect(props.song.path)),
onDrop: props.onDrop ?? (() => {}),
createHint: SongHint,
useOnlyAsOnClickBinder: !isDraggable || selectedSong().path === song.path,
useOnlyAsOnClickBinder: !props.draggable || selectedSong().path === props.song.path,
});
if (selectable === true) {
item.dataset.path = song.path;
if (props.selectable === true) {
item.dataset.path = props.song.path;
}
});
const isActive = createMemo(() => {
return selectedSong().path === props.song.path;
});
return (
<div
class="group relative isolate select-none rounded-md"
classList={{
"outline outline-2 outline-accent": selectedSong().path === song.path,
"outline outline-2 outline-accent": isActive(),
}}
data-active={selectedSong().path === song.path}
data-active={isActive()}
ref={item}
data-url={song.bg}
onContextMenu={showMenu}
data-url={props.song.bg}
>
<SongImage
class="absolute inset-0 z-[-1] h-full w-full rounded-md bg-cover bg-center bg-no-repeat opacity-30 group-hover:opacity-90"
classList={{
"opacity-90": selectedSong().path === song.path,
"opacity-90": isActive(),
}}
src={song.bg}
group={group}
src={props.song.bg}
group={props.group}
/>
<div class="flex min-h-[72px] flex-col justify-center overflow-hidden rounded-md bg-black/50 p-3">
<h3 class="text-shadow text-[22px] font-extrabold leading-7 shadow-black/60">
{song.title}
{props.song.title}
</h3>
<p class="text-base text-subtext">{song.artist}</p>
<p class="text-base text-subtext">{props.song.artist}</p>
</div>
</div>
);

View File

@@ -1,6 +1,5 @@
import { Optional, Order } from "../../../../../@types";
import { Optional, Order, Tag } from "../../../../../@types";
import { SearchQueryError } from "../../../../../main/lib/search-parser/@search-types";
import { Tag } from "../../search/TagSelect";
import { setSongsSearch } from "../song-list/song-list.utils";
import SongListSearchOrderBy from "./SongListSearchOrderBy";
import { SearchIcon } from "lucide-solid";

View File

@@ -1,7 +1,7 @@
import Button from "@renderer/components/button/Button";
import Dropdown from "@renderer/components/dropdown/Dropdown";
import { ArrowDownAzIcon, ArrowUpZaIcon } from "lucide-solid";
import { Component, createMemo, createSignal, Match, Setter, Switch } from "solid-js";
import { Component, createMemo, createSignal, For, Match, Setter, Switch } from "solid-js";
import { OrderDirection, OrderOptions, Order } from "src/@types";
type OrderOption = {
@@ -84,14 +84,16 @@ const SongListSearchOrderBy: Component<OrderSelectProps> = (props) => {
}}
value={option}
>
{orderOptions.map((option) => (
<For each={orderOptions}>
{(option) => (
<Dropdown.Item
class="px-4 py-2 transition-colors duration-200 hover:bg-accent/20"
value={option.value}
>
{option.text}
</Dropdown.Item>
))}
)}
</For>
</Dropdown.List>
</Dropdown>
</div>

View File

@@ -16,17 +16,18 @@ export type SongViewProps = {
playlist?: string;
};
const SongList: Component<SongViewProps> = (props) => {
const tagsSignal = createSignal<Tag[]>([], { equals: false });
const [tags] = tagsSignal;
const DEFAULT_TAGS_VALUE: Tag[] = [];
const DEFAULT_ORDER_VALUE: Order = { option: "title", direction: "asc" };
const [order, setOrder] = createSignal<Order>({ option: "title", direction: "asc" });
const SongList: Component<SongViewProps> = (props) => {
const [tags, setTags] = createSignal(DEFAULT_TAGS_VALUE, { equals: false });
const [order, setOrder] = createSignal(DEFAULT_ORDER_VALUE);
const [count, setCount] = createSignal(0);
const [payload, setPayload] = createSignal<SongsQueryPayload>({
view: props,
order: order(),
tags: tags(),
order: DEFAULT_ORDER_VALUE,
tags: DEFAULT_TAGS_VALUE,
});
const [searchError, setSearchError] = createSignal<Optional<SearchQueryError>>(none(), {
@@ -75,7 +76,12 @@ const SongList: Component<SongViewProps> = (props) => {
return (
<div class="flex h-full flex-col">
<div class="sticky top-0 z-10">
<SongListSearch tags={tagsSignal} setOrder={setOrder} count={count} error={searchError} />
<SongListSearch
tags={[tags, setTags]}
setOrder={setOrder}
count={count}
error={searchError}
/>
</div>
<div class="flex-grow overflow-y-auto p-5 py-0">

View File

@@ -1,3 +1,3 @@
export default function NoScene() {
return <div class="scene"></div>;
return <div class="scene" />;
}

View File

@@ -21,6 +21,7 @@ import {
JSXElement,
Match,
onCleanup,
onMount,
Setter,
Show,
Switch,
@@ -53,7 +54,7 @@ const Nav: Component = () => {
const [os, setOs] = createSignal<NodeJS.Platform>();
const [maximized, setMaximized] = createSignal<boolean>(false);
createEffect(async () => {
onMount(async () => {
const fetchOS = async () => {
return await window.api.request("os::platform");
};
@@ -64,10 +65,17 @@ const Nav: Component = () => {
setOs(await fetchOS());
setMaximized(await fetchMaximized());
window.api.listen("window::maximizeChange", (maximized: boolean) => {
setMaximized(maximized);
});
createEffect(() => {
const resizeListener = (maximized: boolean) => {
setMaximized(maximized);
};
window.api.listen("window::maximizeChange", resizeListener);
return () => {
window.api.removeListener("window::maximizeChange", resizeListener);
};
});
return (
@@ -101,13 +109,13 @@ function WindowControls(props: { maximized: Accessor<boolean>; setMaximized: Set
return (
<div class="nav-window-controls">
<button
onclick={async () => window.api.request("window::minimize")}
onClick={async () => window.api.request("window::minimize")}
class="nav-window-control"
>
<MinusIcon size={20} />
</button>
<button
onclick={async () => {
onClick={async () => {
window.api.request("window::maximize");
props.setMaximized(!props.maximized());
}}
@@ -116,7 +124,7 @@ function WindowControls(props: { maximized: Accessor<boolean>; setMaximized: Set
{props.maximized() ? <Minimize2Icon size={20} /> : <SquareIcon size={18} />}
</button>
<button
onclick={async () => window.api.request("window::close")}
onClick={async () => window.api.request("window::close")}
class="nav-window-control close"
>
<XIcon size={20} />
@@ -128,19 +136,19 @@ function WindowControls(props: { maximized: Accessor<boolean>; setMaximized: Set
type NavItemProps = Pick<Tab, "value" | "Icon"> & {
children: JSXElement;
};
const NavItem: Component<NavItemProps> = ({ children, value, Icon }) => {
const NavItem: Component<NavItemProps> = (props) => {
return (
<button
class={`nav-item flex items-center gap-4 rounded-sm px-4 py-1 hover:bg-surface ${mainActiveTab() === value ? "bg-surface" : ""}`}
onclick={() => setMainActiveTab(value)}
class={`nav-item flex items-center gap-4 rounded-sm px-4 py-1 hover:bg-surface ${mainActiveTab() === props.value ? "bg-surface" : ""}`}
onClick={() => setMainActiveTab(props.value)}
>
<span class={`${mainActiveTab() === value ? "" : "opacity-70"}`}>
<Icon size={20} />
<span class={`${mainActiveTab() === props.value ? "" : "opacity-70"}`}>
<props.Icon size={20} />
</span>
<span
class={`text-base font-semibold ${mainActiveTab() === value ? "text-text" : "text-subtext"}`}
class={`text-base font-semibold ${mainActiveTab() === props.value ? "text-text" : "text-subtext"}`}
>
{children}
{props.children}
</span>
</button>
);