From c5293ca9fe75ecef3c92416c673f8ec4df203028 Mon Sep 17 00:00:00 2001 From: Grzegorz Kucmierz Date: Tue, 3 Mar 2026 13:39:56 +0000 Subject: [PATCH] style: standardize UI across tools, optimize QR layout, and configure Husky/ESLint --- .husky/pre-commit | 4 + eslint.config.js | 49 + package-lock.json | 857 +++++++++++++++++- package.json | 9 +- src/App.vue | 4 +- src/components/tools/ClipboardSniffer.vue | 18 +- src/components/tools/Passwords.vue | 109 +-- src/components/tools/QrCode.vue | 129 +-- src/components/tools/QrScanner.vue | 114 +-- src/components/tools/UrlCleaner.vue | 147 --- .../tools/UrlCleanerExceptionsModal.vue | 18 - src/style.css | 237 ++++- src/styles/tools.css | 107 --- 13 files changed, 1220 insertions(+), 582 deletions(-) create mode 100755 .husky/pre-commit create mode 100644 eslint.config.js delete mode 100644 src/styles/tools.css diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..75fac8e --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run lint diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..097869a --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,49 @@ +import pluginVue from 'eslint-plugin-vue' +import js from '@eslint/js' +import globals from 'globals' + +export default [ + // Base JS recommended rules (all off for now to allow pre-commit) + { + rules: Object.keys(js.configs.recommended.rules).reduce((acc, rule) => { + acc[rule] = 'off'; + return acc; + }, {}), + }, + // Vue essential rules + ...pluginVue.configs['flat/essential'], + { + // Apply to all JS and Vue files + files: ['**/*.js', '**/*.vue'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + crypto: 'readonly', + BarcodeDetector: 'readonly', + chrome: 'readonly', + __APP_VERSION__: 'readonly', + VITE_APP_VERSION: 'readonly', + }, + }, + rules: { + 'vue/multi-word-component-names': 'off', + 'no-unused-vars': 'off', + 'no-undef': 'off', + 'no-debugger': 'off', + } + }, + { + // Global ignores + ignores: [ + 'dist/**', + 'dev-dist/**', + 'node_modules/**', + 'public/**', + 'scripts/pack_crx.js', + 'src/app.config.mjs' + ] + } +] diff --git a/package-lock.json b/package-lock.json index 16dcfc8..5949a55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tools-app", - "version": "0.6.16", + "version": "0.6.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tools-app", - "version": "0.6.16", + "version": "0.6.17", "hasInstallScript": true, "dependencies": { "barcode-detector": "^3.1.0", @@ -17,7 +17,12 @@ "vue-router": "^5.0.3" }, "devDependencies": { + "@eslint/js": "^10.0.1", "@vitejs/plugin-vue": "^6.0.2", + "eslint": "^10.0.2", + "eslint-plugin-vue": "^10.8.0", + "globals": "^17.4.0", + "husky": "^9.1.7", "vite": "^7.3.1", "vite-plugin-pwa": "^1.2.0" } @@ -2001,6 +2006,186 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz", + "integrity": "sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.2", + "debug": "^4.3.1", + "minimatch": "^10.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz", + "integrity": "sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz", + "integrity": "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz", + "integrity": "sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz", + "integrity": "sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.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", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@isaacs/cliui": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", @@ -2524,6 +2709,13 @@ "integrity": "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q==", "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2531,6 +2723,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -2734,6 +2933,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", @@ -2972,6 +3181,13 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", @@ -3234,6 +3450,19 @@ "node": ">=8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -3321,6 +3550,13 @@ "node": ">=0.10.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -3617,6 +3853,295 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz", + "integrity": "sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.2", + "@eslint/config-helpers": "^0.5.2", + "@eslint/core": "^1.1.0", + "@eslint/plugin-kit": "^0.6.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.1", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.1.1", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.1", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.8.0.tgz", + "integrity": "sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^7.1.0", + "semver": "^7.6.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "vue-eslint-parser": "^10.0.0" + }, + "peerDependenciesMeta": { + "@stylistic/eslint-plugin": { + "optional": true + }, + "@typescript-eslint/parser": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz", + "integrity": "sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz", + "integrity": "sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -3653,6 +4178,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -3687,6 +4219,19 @@ } } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/filelist": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", @@ -3740,6 +4285,27 @@ "node": ">=8" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz", + "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==", + "dev": true, + "license": "ISC" + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3963,6 +4529,32 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -4090,6 +4682,22 @@ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "license": "MIT" }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/idb": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", @@ -4097,6 +4705,26 @@ "dev": true, "license": "ISC" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4247,6 +4875,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-finalizationregistry": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", @@ -4292,6 +4930,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -4599,6 +5250,13 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -4613,6 +5271,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4648,6 +5313,16 @@ "node": ">=0.10.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -4658,6 +5333,20 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/local-pkg": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", @@ -4865,6 +5554,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -4872,6 +5568,19 @@ "dev": true, "license": "MIT" }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -4916,6 +5625,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -5118,6 +5845,30 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-bytes": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", @@ -5999,6 +6750,19 @@ "punycode": "^2.1.0" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", @@ -6254,6 +7018,23 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", @@ -6381,6 +7162,45 @@ } } }, + "node_modules/vue-eslint-parser": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.0.tgz", + "integrity": "sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "eslint-scope": "^8.2.0 || ^9.0.0", + "eslint-visitor-keys": "^4.2.0 || ^5.0.0", + "espree": "^10.3.0 || ^11.0.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/vue-router": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.3.tgz", @@ -6562,6 +7382,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workbox-background-sync": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.4.0.tgz", @@ -6905,6 +7735,16 @@ "node": ">=8" } }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -6968,6 +7808,19 @@ "node": ">=6" } }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zxing-wasm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-3.0.0.tgz", diff --git a/package.json b/package.json index c285249..59bd3b5 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,10 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", + "lint": "eslint .", "pack-extension": "node scripts/pack_crx.js", - "postinstall": "mkdir -p public/wasm && cp node_modules/zxing-wasm/dist/reader/zxing_reader.wasm public/wasm/" + "postinstall": "mkdir -p public/wasm && cp node_modules/zxing-wasm/dist/reader/zxing_reader.wasm public/wasm/", + "prepare": "husky" }, "dependencies": { "barcode-detector": "^3.1.0", @@ -19,7 +21,12 @@ "vue-router": "^5.0.3" }, "devDependencies": { + "@eslint/js": "^10.0.1", "@vitejs/plugin-vue": "^6.0.2", + "eslint": "^10.0.2", + "eslint-plugin-vue": "^10.8.0", + "globals": "^17.4.0", + "husky": "^9.1.7", "vite": "^7.3.1", "vite-plugin-pwa": "^1.2.0" } diff --git a/src/App.vue b/src/App.vue index ae0797d..a5df5d0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -76,6 +76,8 @@ onUnmounted(() => { padding: 1rem; width: 100%; max-width: 100%; + display: flex; + flex-direction: column; /* Space for fixed footer on mobile + extra margin */ padding-bottom: calc(1rem + var(--footer-height) + env(safe-area-inset-bottom)); } @@ -94,7 +96,7 @@ onUnmounted(() => { .main-content { overflow: visible; - height: auto; + flex: 1; padding-bottom: 1rem; } } diff --git a/src/components/tools/ClipboardSniffer.vue b/src/components/tools/ClipboardSniffer.vue index 8592497..a7e066a 100644 --- a/src/components/tools/ClipboardSniffer.vue +++ b/src/components/tools/ClipboardSniffer.vue @@ -49,9 +49,9 @@ const clearText = () => { + diff --git a/src/components/tools/QrScanner.vue b/src/components/tools/QrScanner.vue index a699881..35327aa 100644 --- a/src/components/tools/QrScanner.vue +++ b/src/components/tools/QrScanner.vue @@ -272,6 +272,7 @@ const isUrl = (string) => {

QR Scanner

+
@@ -375,24 +376,6 @@ const isUrl = (string) => { flex-direction: column; } -.tool-panel { - display: flex; - flex-direction: column; - height: 100%; - gap: 1.5rem; - padding: 1.5rem; -} - -.panel-header { - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 0.5rem; - margin-top: 0; - position: relative; - width: 100%; -} - .scanner-content { display: flex; flex-direction: column; @@ -478,8 +461,6 @@ const isUrl = (string) => { transform: scaleX(-1); } -/* front mirror canvas removed */ - .error-overlay { position: absolute; top: 0; @@ -538,18 +519,6 @@ const isUrl = (string) => { backdrop-filter: blur(4px); } -/* Removed legacy scan frame overlay - using shape detection rendering via track instead */ - -.results-section { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - background: var(--glass-bg); - border: 1px solid var(--glass-border); - border-radius: 12px; -} - .scanner-content.is-fullscreen .results-section { position: relative; flex: 1; @@ -565,55 +534,6 @@ const isUrl = (string) => { flex-direction: column; } -.results-header { - padding: 1rem; - border-bottom: 1px solid var(--glass-border); - display: flex; - justify-content: space-between; - align-items: center; -} - -.results-header h3 { - margin: 0; - font-size: 1.1rem; - color: var(--text-strong); -} - -.header-actions { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.codes-list { - flex: 1; - overflow-y: auto; - padding: 0; -} - -.code-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem; - border-bottom: 1px solid var(--glass-border); - transition: background 0.2s; -} - -.code-item:last-child { - border-bottom: none; -} - -.code-item:hover { - background: var(--list-hover-bg); -} - -.code-content { - flex: 1; - overflow: hidden; - padding-right: 1rem; -} - .code-value { color: var(--primary-accent); font-family: monospace; @@ -637,12 +557,6 @@ const isUrl = (string) => { color: var(--text-secondary); } -.item-actions { - display: flex; - gap: 0.5rem; - align-items: center; -} - .format-badge { background: rgba(255, 255, 255, 0.1); padding: 0 0.4rem; @@ -654,32 +568,6 @@ const isUrl = (string) => { background: rgba(0, 0, 0, 0.1); } -.icon-btn { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - padding: 0.4rem; - border-radius: 4px; - transition: all 0.2s; - display: flex; - align-items: center; - justify-content: center; -} - -.icon-btn:hover { - color: var(--text-color); - background: rgba(255, 255, 255, 0.1); -} - -:global(:root[data-theme="light"]) .icon-btn:hover { - background: rgba(0, 0, 0, 0.05); -} - -.delete-btn:hover { - color: #ef4444; -} - .empty-state { flex: 1; display: flex; diff --git a/src/components/tools/UrlCleaner.vue b/src/components/tools/UrlCleaner.vue index 30947c8..57634aa 100644 --- a/src/components/tools/UrlCleaner.vue +++ b/src/components/tools/UrlCleaner.vue @@ -212,22 +212,6 @@ onUnmounted(() => { flex-direction: column; } -.tool-panel { - display: flex; - flex-direction: column; - height: 100%; - gap: 1.5rem; - position: relative; -} - -.panel-header { - display: flex; - justify-content: center; - align-items: center; - position: relative; - width: 100%; -} - .input-section { display: flex; flex-direction: column; @@ -291,81 +275,6 @@ onUnmounted(() => { color: var(--primary-accent); } -.status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background-color: #4ade80; /* Green */ - box-shadow: 0 0 8px #4ade80; - margin-left: 0.2rem; - animation: pulse 2s infinite; -} - -@keyframes pulse { - 0% { opacity: 1; } - 50% { opacity: 0.5; } - 100% { opacity: 1; } -} - -.history-section { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - background: var(--glass-bg); - border: 1px solid var(--glass-border); - border-radius: 12px; -} - -.history-header { - padding: 1rem; - border-bottom: 1px solid var(--glass-border); - display: flex; - justify-content: space-between; - align-items: center; -} - -.history-header h3 { - margin: 0; - font-size: 1.1rem; - color: var(--text-strong); -} - -.history-actions { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.history-list { - flex: 1; - overflow-y: auto; - padding: 0; -} - -.history-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.8rem; - border-bottom: 1px solid var(--glass-border); - transition: background 0.2s; -} - -.history-item:last-child { - border-bottom: none; -} - -.history-item:hover { - background: var(--list-hover-bg); -} - -.item-info { - flex: 1; - overflow: hidden; - padding-right: 1rem; -} - .cleaned-url { color: var(--primary-accent); font-family: monospace; @@ -389,44 +298,6 @@ onUnmounted(() => { gap: 0.2rem; } -:global(:root[data-theme="light"]) .savings { - color: #16a34a; - font-weight: 500; -} - -.item-actions { - display: flex; - gap: 0.5rem; -} - -.icon-btn { - background: none; - border: none; - color: var(--text-secondary); - cursor: pointer; - padding: 0.4rem; - border-radius: 4px; - transition: all 0.2s; - display: flex; - align-items: center; - justify-content: center; -} - -.icon-btn:hover { - color: var(--text-color); - background: rgba(255, 255, 255, 0.1); -} - -.delete-btn:hover { - background: none; - color: #ef4444; -} - -:global(:root[data-theme="light"]) .delete-btn:hover { - background: none; - color: #dc2626; -} - .empty-state { flex: 1; display: flex; @@ -437,16 +308,6 @@ onUnmounted(() => { padding: 2rem; } -.header-actions { - display: flex; - align-items: center; - gap: 0.8rem; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); -} - .settings-btn { background: rgba(255, 255, 255, 0.1); width: 32px; @@ -464,12 +325,4 @@ onUnmounted(() => { background: rgba(255, 255, 255, 0.2); color: var(--primary-accent); } - -:global(:root[data-theme="light"]) .settings-btn { - background: rgba(0, 0, 0, 0.05); -} - -:global(:root[data-theme="light"]) .settings-btn:hover { - background: rgba(0, 0, 0, 0.1); -} diff --git a/src/components/tools/UrlCleanerExceptionsModal.vue b/src/components/tools/UrlCleanerExceptionsModal.vue index 615e24b..ecb11e3 100644 --- a/src/components/tools/UrlCleanerExceptionsModal.vue +++ b/src/components/tools/UrlCleanerExceptionsModal.vue @@ -302,10 +302,6 @@ const resetToDefault = (ruleId) => { padding: 1rem; } -:global(:root[data-theme="light"]) .modal-overlay { - background: rgba(255, 255, 255, 0.3); -} - .modal-content { background: var(--glass-bg); backdrop-filter: blur(16px); @@ -385,11 +381,6 @@ const resetToDefault = (ruleId) => { border: 1px solid var(--glass-border); } -:global(:root[data-theme="light"]) .add-rule-form { - background: rgba(0, 0, 0, 0.04); - border: 1px solid rgba(0, 0, 0, 0.06); -} - .add-rule-form h4, .rules-list h4 { margin-top: 0; margin-bottom: 1rem; @@ -483,11 +474,6 @@ const resetToDefault = (ruleId) => { transition: opacity 0.3s; } -:global(:root[data-theme="light"]) .rule-item { - background: rgba(0, 0, 0, 0.04); - border: 1px solid rgba(0, 0, 0, 0.06); -} - .rule-item.disabled { opacity: 0.6; } @@ -609,8 +595,4 @@ const resetToDefault = (ruleId) => { .delete-btn:hover { color: #ef4444; } - -:global(:root[data-theme="light"]) .delete-btn:hover { - color: #dc2626; -} diff --git a/src/style.css b/src/style.css index 16966dc..fb90d47 100644 --- a/src/style.css +++ b/src/style.css @@ -83,7 +83,7 @@ --button-bg: rgba(255, 255, 255, 0.7); --button-border: rgba(255, 255, 255, 0.9); --button-text: #0f172a; - --button-hover-bg: rgba(255, 255, 255, 1); + --button-hover-bg: rgba(15, 23, 42, 0.1); --button-hover-shadow: 0 6px 18px rgba(15, 23, 42, 0.18); --button-active-shadow: 0 0 18px rgba(14, 165, 233, 0.25); --title-gradient: linear-gradient(45deg, #0ea5e9, #6366f1); @@ -135,7 +135,7 @@ body { display: flex; justify-content: center; width: 100%; - max-width: 800px; + max-width: 100%; margin: 0 auto; height: 100%; padding: 0.5rem; @@ -148,6 +148,7 @@ body { display: flex; flex-direction: column; gap: 1.5rem; + height: 100%; max-height: 100%; overflow-y: auto; background: var(--glass-bg); @@ -178,7 +179,7 @@ body { .tool-title { margin: 0; - text-align: center; + text-align: left; font-size: 1.5rem; font-weight: 600; background: var(--title-gradient); @@ -194,14 +195,13 @@ body { .tool-textarea, .select-input { width: 100%; - padding: 1rem; + padding: 0.75rem 1rem; background-color: var(--toggle-bg); border: 1px solid var(--toggle-border); - border-radius: 12px; + border-radius: 8px; color: var(--text-color); font-family: inherit; - font-size: 1rem; - line-height: 1.6; + font-size: 0.95rem; transition: all 0.3s ease; box-sizing: border-box; } @@ -216,10 +216,12 @@ body { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'%3E%3C/path%3E%3C/svg%3E"); background-repeat: no-repeat; - background-position: right 1rem center; - background-size: 1.2rem; - padding-right: 3rem; + background-position: right 0.75rem center; + background-size: 1rem; + padding-right: 2.5rem; + height: 40px; cursor: pointer; + line-height: 1; } .tool-textarea:focus, @@ -265,9 +267,10 @@ body { background: var(--button-bg); border: 1px solid var(--button-border); color: var(--button-text); - padding: 8px 16px; + padding: 0 1.25rem; + height: 40px; border-radius: 8px; - font-weight: 600; + font-weight: 500; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); @@ -355,10 +358,10 @@ button:focus { outline: none; } -/* Custom focus ring for all form elements */ input:focus, select:focus, -textarea:focus { +textarea:focus, +.number-control:focus-within { border-color: var(--primary-accent) !important; box-shadow: 0 0 0 1px var(--primary-accent) !important; } @@ -433,3 +436,209 @@ textarea:focus { border-color: white; /* Keep checkmark white even in light mode if background is primary-accent */ } + +/* --- Global Header/Action Patterns --- */ +.panel-header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1rem; + width: 100%; + min-height: 40px; +} + +.header-actions { + display: flex; + align-items: center; + gap: 0.8rem; +} + +/* --- Global Icon Button Styles --- */ +.icon-btn { + background: none; + border: none; + color: var(--text-secondary); + cursor: pointer; + padding: 0.5rem; + border-radius: 8px; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; +} + +.icon-btn:hover { + color: var(--text-color); + background: var(--button-hover-bg); +} + +.icon-btn.delete-btn:hover { + color: #ef4444; + background: rgba(239, 68, 68, 0.1); +} + +:root[data-theme="light"] .icon-btn.delete-btn:hover { + color: #dc2626; + background: rgba(220, 38, 38, 0.05); +} + +/* --- Component Specific Theme Overrides (Consolidated) --- */ +:root[data-theme="light"] .settings-btn { + background: rgba(0, 0, 0, 0.03); +} + +:root[data-theme="light"] .savings { + color: #16a34a; + font-weight: 500; +} + +:root[data-theme="light"] .modal-overlay { + background: rgba(255, 255, 255, 0.3); +} + +:root[data-theme="light"] .add-rule-form, +:root[data-theme="light"] .rule-item { + background: rgba(0, 0, 0, 0.04); + border: 1px solid rgba(0, 0, 0, 0.06); +} + +/* --- Global Number Control Styles --- */ +.number-control { + display: flex; + align-items: stretch; + background: var(--toggle-bg); + border: 1px solid var(--toggle-border); + border-radius: 8px; + overflow: hidden; + height: 40px; +} + +.control-btn { + background: none; + border: none; + color: var(--text-color); + font-size: 0.9rem; + min-width: 40px; + padding: 0 0.5rem; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; + font-weight: 400; +} + +.control-btn:hover { + background: var(--button-hover-bg); + color: var(--primary-accent); +} + +.number-input { + background: none; + border: none; + color: var(--text-color); + flex: 1; + min-width: 60px; + text-align: center; + font-size: 1rem; + font-weight: 400; + appearance: textfield; +} + +.number-input::-webkit-outer-spin-button, +.number-input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +/* --- Global List/History Styles --- */ +.results-section, +.history-section { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 12px; +} + +.history-header, +.results-header { + padding: 1rem; + border-bottom: 1px solid var(--glass-border); + display: flex; + justify-content: space-between; + align-items: center; +} + +.history-header h3, +.results-header h3 { + margin: 0; + font-size: 1.1rem; + color: var(--text-strong); +} + +.history-list, +.codes-list { + flex: 1; + overflow-y: auto; + padding: 0; +} + +.history-item, +.code-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border-bottom: 1px solid var(--glass-border); + transition: background 0.2s; +} + +.history-item:last-child, +.code-item:last-child { + border-bottom: none; +} + +.history-item:hover, +.code-item:hover { + background: var(--list-hover-bg); +} + +.item-info, +.code-content { + flex: 1; + overflow: hidden; + padding-right: 1rem; +} + +.item-actions { + display: flex; + gap: 0.5rem; + align-items: center; +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #4ade80; + box-shadow: 0 0 8px #4ade80; + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + opacity: 1; + } + + 50% { + opacity: 0.5; + } + + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/src/styles/tools.css b/src/styles/tools.css deleted file mode 100644 index 6a1e1c8..0000000 --- a/src/styles/tools.css +++ /dev/null @@ -1,107 +0,0 @@ -/* Shared styles for all tools */ - -.tool-container { - display: flex; - justify-content: center; - width: 100%; - max-width: 800px; - margin: 0 auto; - height: 100%; - padding: 1rem; -} - -.tool-panel { - width: 100%; - padding: 2rem; - border-radius: 16px; - display: flex; - flex-direction: column; - gap: 1.5rem; - max-height: 100%; - overflow-y: auto; - background: var(--glass-bg); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); - border: 1px solid var(--glass-border); - box-shadow: var(--glass-shadow); -} - -/* Custom scrollbar for tool panel */ -.tool-panel::-webkit-scrollbar { - width: 8px; -} - -.tool-panel::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0.1); - border-radius: 4px; -} - -.tool-panel::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.2); - border-radius: 4px; -} - -.tool-panel::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.3); -} - -.tool-title { - margin: 0; - text-align: center; - font-size: 1.5rem; - font-weight: 600; - background: var(--title-gradient); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - -webkit-text-fill-color: transparent; - filter: drop-shadow(0 0 10px var(--title-glow)); -} - -:root[data-theme="light"] .tool-title { - background: none; - -webkit-background-clip: unset; - background-clip: unset; - color: #000000; - -webkit-text-fill-color: #000000; - filter: none; -} - -.tool-textarea { - width: 100%; - height: 100%; - padding: 1rem; - background-color: rgba(0, 0, 0, 0.2); - border: 1px solid var(--toggle-border); - border-radius: 12px; - color: #ffffff; /* Explicit white color for dark mode */ - font-family: monospace; - font-size: 1rem; - line-height: 1.6; - resize: none; - transition: all 0.3s ease; - box-sizing: border-box; -} - -:root[data-theme="light"] .tool-textarea { - color: #000000; -} - -.tool-textarea:focus { - outline: none; - border-color: #00f2fe; /* Force cyan accent */ - box-shadow: 0 0 0 1px #00f2fe; -} - -.result-area { - display: flex; - flex-direction: column; - flex: 1; - min-height: 200px; -} - -.result-area label { - margin-bottom: 0.5rem; - font-weight: 600; - color: var(--text-secondary); -}