diff --git a/.github/workflows/gitweb-build.yml b/.github/workflows/gitweb-build.yml index a13681e..9639f27 100644 --- a/.github/workflows/gitweb-build.yml +++ b/.github/workflows/gitweb-build.yml @@ -68,6 +68,11 @@ jobs: - name: Install dependencies run: npm ci + # oxlint fails only on errors (real bugs); warnings are listed in the log + # and stay non-blocking until the backlog is cleared — see .oxlintrc.json. + - name: Lint + run: npm run lint + - name: Typecheck run: npm run typecheck diff --git a/GitWeb/.oxlintrc.json b/GitWeb/.oxlintrc.json new file mode 100644 index 0000000..6d7516b --- /dev/null +++ b/GitWeb/.oxlintrc.json @@ -0,0 +1,55 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "react", "jsx-a11y", "import", "unicorn"], + "categories": { + "correctness": "error", + "suspicious": "warn" + }, + "rules": { + "react/rules-of-hooks": "error", + "react/exhaustive-deps": "warn", + + "react/react-in-jsx-scope": "off", + "jsx-a11y/prefer-tag-over-role": "off", + "unicorn/no-array-sort": "off", + "unicorn/no-array-reverse": "off", + "unicorn/consistent-function-scoping": "off", + "eslint/no-shadow": "off", + "eslint/no-underscore-dangle": "off", + "import/no-unassigned-import": "off", + + "react/set-state-in-effect": "warn", + "react/refs": "warn", + "react/purity": "warn", + // Only meaningful under the React Compiler, which this project does not use. + "react/incompatible-library": "off", + "react/preserve-manual-memoization": "off", + "react/exhaustive-effect-dependencies": "warn", + + "jsx-a11y/no-noninteractive-element-interactions": "warn", + "jsx-a11y/click-events-have-key-events": "warn", + "jsx-a11y/no-noninteractive-element-to-interactive-role": [ + "warn", + { + "ul": ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"], + "ol": ["listbox", "menu", "menubar", "radiogroup", "tablist", "tree", "treegrid"], + "li": ["menuitem", "menuitemradio", "menuitemcheckbox", "option", "row", "tab", "treeitem"], + "table": ["grid"], + "td": ["gridcell"], + "fieldset": ["radiogroup", "presentation"] + } + ], + "jsx-a11y/interactive-supports-focus": "warn", + "jsx-a11y/control-has-associated-label": "warn", + "jsx-a11y/aria-role": "warn", + "jsx-a11y/label-has-associated-control": ["warn", { "depth": 3 }], + "jsx-a11y/no-noninteractive-tabindex": "warn", + "jsx-a11y/no-autofocus": "warn" + }, + "env": { + "browser": true, + "node": true, + "es2024": true + }, + "ignorePatterns": ["out/**", "dist/**", "node_modules/**"] +} diff --git a/GitWeb/electron.vite.config.ts b/GitWeb/electron.vite.config.ts index 06ebc9a..e578672 100644 --- a/GitWeb/electron.vite.config.ts +++ b/GitWeb/electron.vite.config.ts @@ -1,8 +1,18 @@ import { resolve } from 'path' +import { execFileSync } from 'child_process' import { defineConfig } from 'electron-vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' +/** The commit this build came from, for About's "git log -1" card; '' outside a checkout. */ +function buildCommit(): string { + try { + return execFileSync('git', ['rev-parse', '--short=7', 'HEAD'], { cwd: __dirname, encoding: 'utf8' }).trim() + } catch { + return '' + } +} + export default defineConfig({ main: { build: { @@ -30,7 +40,8 @@ export default defineConfig({ } }, define: { - __BUILD_TIME__: JSON.stringify(new Date().toISOString()) + __BUILD_TIME__: JSON.stringify(new Date().toISOString()), + __BUILD_COMMIT__: JSON.stringify(buildCommit()) }, plugins: [react(), tailwindcss()] } diff --git a/GitWeb/package-lock.json b/GitWeb/package-lock.json index 2c28c51..fab6ccf 100644 --- a/GitWeb/package-lock.json +++ b/GitWeb/package-lock.json @@ -12,29 +12,30 @@ "@fontsource/inter": "^5.3.0", "@fontsource/jetbrains-mono": "^5.3.0", "@icons-pack/react-simple-icons": "^13.15.1", - "@tailwindcss/vite": "^4.3.4", + "@tailwindcss/vite": "^4.3.5", "@tanstack/react-virtual": "^3.14.26", "electron-updater": "^6.8.11", - "lucide-react": "^1.69.0", + "lucide-react": "^1.73.0", "react": "^19.3.1", "react-dom": "^19.3.1", "react-markdown": "^10.1.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", - "tailwindcss": "^4.3.4", - "zustand": "^5.0.16" + "tailwindcss": "^4.3.5", + "zustand": "^5.1.0" }, "devDependencies": { "@types/node": "^26.6.6", - "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", - "@vitejs/plugin-react": "^6.2.1", - "electron": "^45.2.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@vitejs/plugin-react": "^6.3.0", + "electron": "^45.3.2", "electron-builder": "^26.18.0", "electron-vite": "^6.0.0", + "oxlint": "^1.93.0", "typescript": "^7.0.2", - "vite": "^8.3.6", + "vite": "^8.3.8", "vitest": "^5.1.0" } }, @@ -64,21 +65,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "version": "7.29.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.13.tgz", + "integrity": "sha512-j1nvOQCZYSgp7HLmisnCXFTpVvFODiijQ9QHkORfGKWH6YDtskTEeCJS7eV545GZ9UBzrq0e2xsOd5QQ5PjIqw==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", + "@babel/generator": "^7.29.13", + "@babel/helper-compilation-targets": "^7.29.13", + "@babel/helper-module-transforms": "^7.29.13", + "@babel/helpers": "^7.29.13", + "@babel/parser": "^7.29.13", "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/traverse": "^7.29.13", + "@babel/types": "^7.29.11", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -95,9 +96,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.2.tgz", + "integrity": "sha512-GYrCADwpu6yV5OAvchkQ27Z5wwMxWAEMg5NNWjyHh/5DboFFqbdbvYNIvZouQpxXyeMPoTOh0vdPYKlf0ZswdQ==", "dev": true, "license": "ISC", "bin": { @@ -105,14 +106,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.14.tgz", + "integrity": "sha512-qSY56jgW5JnSXzuekLVil6zaOQxOCtOHidr65KkaMBw772IuAY28Qx4ZykAcDn8+FRF15qk80XzKwgSc/RYW6g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", + "@babel/parser": "^7.29.14", + "@babel/types": "^7.29.14", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -122,9 +123,9 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "version": "7.29.13", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.13.tgz", + "integrity": "sha512-8WxJ4/Vmes3ghacj8VOJy3NW8eyHiFY/4thU/LSBuBE470+igjR/j1ZTNn8fV5VdNrLx6iXUwig526UdOvfP0w==", "dev": true, "license": "MIT", "dependencies": { @@ -139,9 +140,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.2.tgz", + "integrity": "sha512-GYrCADwpu6yV5OAvchkQ27Z5wwMxWAEMg5NNWjyHh/5DboFFqbdbvYNIvZouQpxXyeMPoTOh0vdPYKlf0ZswdQ==", "dev": true, "license": "ISC", "bin": { @@ -173,15 +174,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.14.tgz", + "integrity": "sha512-2XDvQVd9JESaOgGc4jT2/jquTAO7ppA5noTZQqH+MmZ89yjCzScLwtCPbWtkPZylL+TLWR49IwWmk+QkRR4GsA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/traverse": "^7.29.14" }, "engines": { "node": ">=6.9.0" @@ -201,9 +202,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.14.tgz", + "integrity": "sha512-CXvKMFvM80ihI7Vox9jb+kQjnyjESQNPM+vB7U0Zz6TWuZA+Z/FL0QPMH300h33qJtIx+REeBhXqWOPW48TcAQ==", "dev": true, "license": "MIT", "engines": { @@ -231,27 +232,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.14.tgz", + "integrity": "sha512-6E3Mi1r0B0umq6vmZa4/78q+64fqO2YY0THUdjDPKUCKhNb0yQy5NQIaPHCSD3ryTgiTss//P4JsWS80AsZ6LQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.14" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.14.tgz", + "integrity": "sha512-VL220vXuyO2z3rNIQwY7ftj9UwpqOt+33sH+sBWkOekbsSR2g7EEMnOx24ZlCH0xOA3DM/33SifVFGbnJD8r/w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.8" + "@babel/types": "^7.29.14" }, "bin": { "parser": "bin/babel-parser.js" @@ -292,18 +293,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.14.tgz", + "integrity": "sha512-RAdQJrBgmIH1FWvN0X+1hoDxzMzlP4cv/1Zi4dn4tG1H0cTb+rw7CzUxQJhp1f4ANd8j5vH3fy8BAWFGkBDEsA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", + "@babel/generator": "^7.29.14", "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", + "@babel/parser": "^7.29.14", "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", + "@babel/types": "^7.29.14", "debug": "^4.3.1" }, "engines": { @@ -311,13 +312,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "version": "7.29.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.14.tgz", + "integrity": "sha512-UjI/dKmwJwT9pOszJitq2G6EObZPnsZUfdCr92z2TlF3EZK9J3EdfOKUrCzHl6wFthbzwhzl1Soc/UIwxRiq6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-string-parser": "^7.29.14", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { @@ -325,11 +326,11 @@ } }, "node_modules/@electron-internal/extract-zip": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.5.tgz", - "integrity": "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.8.tgz", + "integrity": "sha512-8iajeLFbzNwM0m3AsQZSZgYREWglAImyGvvFSWU34FgMW8sHlzdwj9QKvv9dgN7jMP591EFUF/sLOC3Jl9YIjQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { "node": ">=22.12.0" } @@ -360,9 +361,9 @@ "license": "MIT" }, "node_modules/@electron/asar/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.22.tgz", + "integrity": "sha512-CKLDTuSsN0+r1ThM0WkzzBI7dnPI6xEGfa/JnVYIK9JNBA1N1NyPwXVHor//NKD/y3sDxp764sZdLhFK7pH8XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -556,23 +557,6 @@ "node": ">=14.14" } }, - "node_modules/@electron/universal/node_modules/minimatch": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.4.tgz", - "integrity": "sha512-/UJ1dEoVQTjPNDh6SGlUo+ueLMmoHTas4q+8yaAR/5t6nLAIiBDJtoYUh20IYleix7ehiQkgczb84PE3uzHkxg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/nonrecursive-glob-match": "^1.0.3", - "brace-expansion": "^5.0.12" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@electron/windows-sign": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", @@ -612,6 +596,37 @@ "node": ">=14.14" } }, + "node_modules/@emnapi/core": { + "version": "2.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-beta.2.tgz", + "integrity": "sha512-MzCVoWtcg4Pm2B3je5mMvYMvj3MmBKw+fU7vbsJc9/iT8ip6/eYIHo/Dsm/mr+wsHB6SEyJDuxB09Yy6r/8+4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "2.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "2.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-beta.2.tgz", + "integrity": "sha512-JcrSPzPvZuNZvJqPTF9Fw25LbOJ8avzFVlaf2grmH4Cku0oGP47tRgqPZadcfj3YoZMHkFVnCYFbQM8I+i5QEw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.2.0.tgz", + "integrity": "sha512-JP7OYH38Q7+pyTAe14raZHLQIK7BDKbdwji+Xi3pcd/6QkJYQH3SX4wu6EplGrGJWRepoNzedfm0G3B9Z4e9Rw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@fontsource/inter": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.3.0.tgz", @@ -653,9 +668,9 @@ } }, "node_modules/@isaacs/nonrecursive-glob-match": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.0.3.tgz", - "integrity": "sha512-SI7kyKHq4XXDRozw+r9GlRZkia1ZkFrpLW98r2IC2Bpr8YkFGE3fvjzmxCG0x78A++69HmDNrDvSMYBZmHGpKA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/nonrecursive-glob-match/-/nonrecursive-glob-match-1.1.0.tgz", + "integrity": "sha512-GPm/Dbvk5RLmhRnxXIa7nTpK0xtl4E0wj0MslsuNiJ1FIROU+2+9lgUC/dgF7Ey4QCWhfEsgxilMRMCdHPfDfw==", "dev": true, "license": "BlueOak-1.0.0" }, @@ -680,9 +695,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.3.tgz", + "integrity": "sha512-lhTtjtoEArWBjismtfS761S9Hlyn+nIZqOZVYoZ1Xn82PMNMVnk+jj/NhC4fAXGTs7ydONcJiR5vyQBTMDu27g==", "license": "MIT", "engines": { "node": ">=6.0.0" @@ -759,6 +774,27 @@ "node": ">=10" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.6.tgz", + "integrity": "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", + "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" + } + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -781,10 +817,357 @@ "url": "https://github.com/sponsors/oxc-project" } }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.93.0.tgz", + "integrity": "sha512-KKw70pbD5Pvq3G2lJ+WQ71XPMjCmg37rkSe2+mDqCRP3PoTSdDMuEi4UOnE8JBtyNM7bek61EC2s0Jt7S7u+Sg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.93.0.tgz", + "integrity": "sha512-fvr1fTUZGelwRxKDt64pf28aL1WaWgviwz3X+E/FcRYvrlEahH0NECRz/c1SY+K+KWmZk5uAf6h1kQnyBkp/2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.93.0.tgz", + "integrity": "sha512-VdvSN9JRf4Es6ZzoAdvEeh8tgKdw8L4H82WmA8MMPVfPdZGCwE/nI27Z1HrRHYQhPxOh4kmnxQEioSUFrOvDaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.93.0.tgz", + "integrity": "sha512-QR1V890Ml0YGRG0F/iV0K9igG+pOhe+VWd+aZnxY849rRHZf3F2gEoQks5+GVHf105X9Mh/6HdShTP5juekG2g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.93.0.tgz", + "integrity": "sha512-jDfIbi30zZaKi+rTvig3sge+Xhi8pFFSgHxb0jzovc4qh8ETm/irKV9c7N/JsuwnYc5sbvf4IC8w1vQQ0IL+BA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.93.0.tgz", + "integrity": "sha512-qtxZ714ND/koFgVH00QgxlzZiIbRPmguaPhnHRU5DFFtrtEdOrSnZD4U/H2AzXrXkZ59eQbMBnJwbum5XohrIQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.93.0.tgz", + "integrity": "sha512-5pJ9NjMvH7WMUqXB6bTTOSemBm39w2cod27Hu3+Yom/GhbTeNXlYcpDx1o4CgBP6fhp0YHG66TNdVaUm6sHjsA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.93.0.tgz", + "integrity": "sha512-pRf+7jtbt2fHE23SX7qBxRgnBZicUgsoCK1PVIjnOk+t3a6UBkpt/GrKI99WlxsKUxftz/5nv/kfKvScmYywkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.93.0.tgz", + "integrity": "sha512-zM8Fd/K8sqGvsBcJBZ3oVZ3ctGWbszA08IaGt5gFFF5RcunppAI/ZnAXbouABvnWeJ//S44KKlfPZnDDbeiphg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.93.0.tgz", + "integrity": "sha512-7qKCnvhZKDXJG2pdbLazd2hw4FxjJHbZOFrrbzyy5PdHfPLH9vnNUa1le01A8uItZuYKPBPncH+HCoEiFT4SgA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.93.0.tgz", + "integrity": "sha512-2pgulxC6G8V5wE750NJE/fRkf9aO6i6yoj10wyaR2EdiNtan4OemsOtsQ0ZTXGdY8Q3IoTmlND3GYvjJsvikYg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.93.0.tgz", + "integrity": "sha512-yWsn6Gg2PnGG5jlyDz5+pHGiMTMiy4adaa4WRMzDgzB8efaShk1JMmTHIBSVLQMh0tYXRs4CN5uJCO79r4Mi4g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.93.0.tgz", + "integrity": "sha512-WCn5plwp/ezS2RDPQKp0/EXEVRr9/dDexUdRKmsMFqzNM5xBSZ1wbsi3PuMk1VfkMRnI5BuazCsziCWMXRDOlw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.93.0.tgz", + "integrity": "sha512-F2CGjb9LAkzH+IYTZSzd1klQ1vk5z6Utjax64WeLByCkYXbVDbsun+Oy2hLfW2MDSKCvW+jwy/9ld0EPgdeu+A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.93.0.tgz", + "integrity": "sha512-plf5DE2objcG8Tf7mXRuRa8tT8rzDkpjqXQtKoGsKHWkpgsialbON1+cCtoSEUTVbQUjXvuAm8vV9+3rR0yCBw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.93.0.tgz", + "integrity": "sha512-Kp2yJrKvoTe9FfKdO/xwLNUbs2bYTMKx0+aoQD1s0h/Tx0kSgtHSdGmrk8tTKdluosjFbo88qYRIaNIX43AfFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.93.0.tgz", + "integrity": "sha512-vigjcHCFsR6KjnWTEVfaCxcMYapEuaK9FZa1MpewwQ1S+Xud3v0okfkj1RySsD6RF1NRHIjSlQSalTnKIaj3nA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.93.0.tgz", + "integrity": "sha512-0Ot3BEe/AAr2/uQhe55r8CmPlperYk2mQmxdutOru7oU0kir6IiEVHb1TG3GORRlfra97lPHVDLhgmihiKzLrg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.93.0.tgz", + "integrity": "sha512-ZzzIIVs/Qkm8trUWYKF0OYub5ALlv50/3gsEZbKAvWgz+yNJX6uRmQ8ySPXU2Q0/Y9m4JCOjhpmC8XUYbPHX5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@peculiar/asn1-schema": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.11.0.tgz", - "integrity": "sha512-8xh4avo53/vXO6UEv9yaoJ83hY581geaLTygf61NPIXP5AeIZCPjJhpTaYU23IZkG+NpLN/C9FYzWp7hphIT9A==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.11.1.tgz", + "integrity": "sha512-jaU9OaCcbZUdyXx9hMETZB9xoNmxTGjOGFqd3cOwKFkpKnyKd9WxF3uqj4PvX4cyOMP9bSdHg5YyML2qtx/d/g==", "dev": true, "license": "MIT", "dependencies": { @@ -820,9 +1203,9 @@ } }, "node_modules/@peculiar/webcrypto": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", - "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.2.tgz", + "integrity": "sha512-ktTSACRJ5WdpfjHjFT2EWJQQPacGv1cPCDfEbdmpAHOxdFiYqPe4lfnXGA3D8Dg9/0kHPYSvyRlgujIOVlTdvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -837,9 +1220,9 @@ } }, "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.16.tgz", - "integrity": "sha512-ia3DwAs0OsMGS7+IConptSiyk9pEXkOx/AbJ2n/lAGxH3QkWQus1t8d7gnAT38FCU2sQu8lUbeR6AiZFv4JxaA==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.19.tgz", + "integrity": "sha512-e+Ebe0WT+9tbXmNKPIiC8YrWAHYwug3m6i4p3rZGGulfZri1jtI4HslT+OZnFf/OIHowWF71DTlHwXBP2SiA7A==", "cpu": [ "arm" ], @@ -853,9 +1236,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.16.tgz", - "integrity": "sha512-kx5wBiMB6E7TEvpl2B9gjLU004BkvEm2Dr941BFjx/SontPwSzNuQUIoVKxHHoSjpFIfHMmcguyLOtyn0hObRg==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.19.tgz", + "integrity": "sha512-hOombrTJ+szt2ciEzS7oCA78lMPBgF5UMQWCJIyNfnrI7GLl8HYoFqCnpL2gcPOaXMqryGCU5ExdQKyDDcC+yA==", "cpu": [ "arm64" ], @@ -869,9 +1252,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.16.tgz", - "integrity": "sha512-qslZrXnKJU4cUpjoAmtAo/KQM9e2IRMlmpig6Zpwj+8KWxW8v+nkAf9216Bsek9lOC1jvg2yCjygCFo2f83msQ==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.19.tgz", + "integrity": "sha512-XoMA2pwCKGTQzUjPgtRwkedH4WmXhdIPKT6l6oOxd1PUbNodGI6DcBekgN7EC//+x3pfBYX/qL1gNmZAqivw5g==", "cpu": [ "arm64" ], @@ -885,9 +1268,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.16.tgz", - "integrity": "sha512-FdF+y48ND13eAyM2mQn+85kvAqNlvRWROy2rhgA463X/0Gv4vGtazZtfaF1mRBLscYvtOlh4QnLjOWiFzX69OQ==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.19.tgz", + "integrity": "sha512-zo8EsSSwMZCY/e+fRnGaX02YGgvJD0yvhNi/9hpGPA2MJ9ZF8dPCw75UGfpI6OpQ/myR/dIYmogRvqiZD0TrXg==", "cpu": [ "x64" ], @@ -901,9 +1284,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.16.tgz", - "integrity": "sha512-ddjqFM5e95AgLbUPtKIS7V1KTVlA0RUgCVr5mn+/IEhsdy3pr+Vuj/HZ7nlMN2FyxIwUktOH1LEPIQXO1v9y1Q==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.19.tgz", + "integrity": "sha512-O8ISAOfI1nj7AuBPd30FSF47z7f7lzlk5poQVGgiikvQuGEBFYvW8ki+UEnT+YJPnNGjQzn304klt4O9FUE7mA==", "cpu": [ "x64" ], @@ -917,9 +1300,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.16.tgz", - "integrity": "sha512-tHdKWbFkFBI687iim3LIyIvyL7j66VvQYkDPss5Stn/qyE2tWkR1YD0dXmXPFKKqT/o5ZEJVsaRsWS2meQwGOA==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.19.tgz", + "integrity": "sha512-/lYCba5NO0yq4S0jGMH8QTBtkvQxoXjoQWzF5BcWp9MjB9jxVRD1GQDNofkVjcOj30DWyhLIN/4Oe60JKzaONw==", "cpu": [ "arm" ], @@ -933,9 +1316,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.16.tgz", - "integrity": "sha512-FJiLGAblPBMlBEdbOqw6kW6QcXDWAf/oJu7gYs9ZJZwmg+K+/zjFLxPTIY9izJUzxzc24FHvmseyA0IvJSl17w==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.19.tgz", + "integrity": "sha512-pm/apFMBnSbFb3O6nG5pHO1iqHhZrjGDFEjSm+ot9J17XXPvH9L90y5h+jgfHCE+Qdlsvy8qKEcU4VKxgjD+vA==", "cpu": [ "arm64" ], @@ -952,9 +1335,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.16.tgz", - "integrity": "sha512-hrJpblMsrQ58U9veWpbXzkhV5zStunds6HWLprqq1qauvgBr1E6P+24kLj6lSLI9vjRn/RYpjprhkHQPKGWpNg==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.19.tgz", + "integrity": "sha512-K1SmQp3FFichn8oqrNIwvFoZ5wcvjC7goBzpaMNvLf9FZc+QB/QvLmnpJZgAAGHiD980tP8FZZnXk2K94rvlxg==", "cpu": [ "arm64" ], @@ -971,9 +1354,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.16.tgz", - "integrity": "sha512-qJd/dC6j6CwLG9/s/RP2nb1RZ0GIgj7Ioj7x/SYa3RzjVrlzrmH0fhIOpfvqQDeIsdP0loi+kHaMhJzY1PBIaA==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.19.tgz", + "integrity": "sha512-I0xwD57jP/3A7+5UPZin2w8Y7XHEmEM5zqpYZOcQ45MlNW6tUJUziymV3yRr7rL4vwyfEcRsRksC4a7Kc08p5Q==", "cpu": [ "ppc64" ], @@ -990,9 +1373,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.16.tgz", - "integrity": "sha512-Vx7Xg5uUJuNcujJjI70OICaH0EYj80BtOLZTSN7uOYg8QOLlyKnaG0IW7Fn/J0IpJsaKzmDKMhOQv1JBM9m9vg==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.19.tgz", + "integrity": "sha512-BrtEHT24SNXj9HiXqy+s/kXiSiQGt6EKCPUlRirAoIioVxKGnbXBUEkbZASAei7iZJB0/Ynm+0JP00Ipx7AnSw==", "cpu": [ "s390x" ], @@ -1009,9 +1392,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.16.tgz", - "integrity": "sha512-TNKhgE3uJTsTzatUSSYt0Kgo+Oa2uSlSjz4r1aEIea7HX6ULBym4fAlYUhG1i6pjNf2njfCUAfKwr6C3E9Xg1w==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.19.tgz", + "integrity": "sha512-rm75/9DkHKeU2WnA8w4X94WGYgLlDCFCfkQC+4bguVJmC/jvDRv2B+2wLOYhyE0fVuv7//Ckk39OoOufyCu76Q==", "cpu": [ "x64" ], @@ -1028,9 +1411,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.16.tgz", - "integrity": "sha512-7chXdpjXZ8qG0+mIxECN3cT9kvhZ3/oZhMX6Pp876P7CDrIy8nJswva6Mq357JR7eYXfuskwZxMeV1SzoZdHrA==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.19.tgz", + "integrity": "sha512-irh/4+iYFM4GAzpluDErgjz89bj6NDtqqMAG/6O1LG38Ed3NpyebzCpcVNkFW8uhy+/3/BocUvnXDR33CXWe9A==", "cpu": [ "x64" ], @@ -1047,9 +1430,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.16.tgz", - "integrity": "sha512-Kwjt+7uNTJK5/MDkGWGVO2KQPnj8yAV75dMpfwyHZrJSXWHrniOB0aUEWlVYf3H1hOqFLIStLdM5QXq6bnKmOQ==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.19.tgz", + "integrity": "sha512-+KWTWNoPt+CfSq94CiceWu3yPE+QzkaG+phPyz9G60tmZLwfQkcA4AeksafzvJAw4pvg1mcd5a1TkUqVv0s4JA==", "cpu": [ "arm64" ], @@ -1062,10 +1445,28 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.19.tgz", + "integrity": "sha512-7EH+skOsl5qpWnK0vzamgJFhOv1cwxQpsxm8tI883K9fur5c27p8q87fzEO6BbBPabA2/+s/zR63/pLc137iOg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "2.0.0-beta.2", + "@emnapi/runtime": "2.0.0-beta.2", + "@napi-rs/wasm-runtime": "1.2.6" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.16.tgz", - "integrity": "sha512-/LH6WfjDRXXJz/p5xl4TmU+jpcnwcxbKEQBW/GZE0JHfN0lGIyTtsUEGaQ/q/GikkGJ2yLKe6pwm7AnEWO8pdw==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.19.tgz", + "integrity": "sha512-3Q1Yg1Q7tEvBWoZf5rFvG/GKZQPgwfru+p4wBajDfJFBs0SHCpL++dfTjW9gyAKd1T7ZpIYvdaUax7Pa/IhFVQ==", "cpu": [ "arm64" ], @@ -1079,9 +1480,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.16.tgz", - "integrity": "sha512-1lv1KvX0Z871zfk7vw+dnXMMnHNyDwMzWG8Kv3HxDXMOhzCX4SHc7HK319fInP0i+0p9D8brkfzL7pJOaGl+tw==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.19.tgz", + "integrity": "sha512-y/sqRRc6d7hECzOFc5n3LSeqctmIp9mMjY1Kq4s14uLPd/LZ4z7i1zrW6fMtp2mPeuQIC4e6ULMcwQzMomqQag==", "cpu": [ "x64" ], @@ -1095,9 +1496,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.1.1.tgz", + "integrity": "sha512-glFIJDvLNDtJhT9qrMFWLPbvS+skxgt4L0K8Ei2YtnhPysAJnsI7nyxSJxKj4ADeKX0OLjC4ntRkwpLOoS3rwA==", "license": "MIT" }, "node_modules/@sindresorhus/is": { @@ -1127,9 +1528,9 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.4.tgz", - "integrity": "sha512-b7QK/QG04v3j04NbpG82XLCedRyulnkuwUDwRa9oK/ypnJIzMapz2Wg+0TltVczjUzDaJJHIa1+jj9aMUOFMTQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.5.tgz", + "integrity": "sha512-yT53FzV4ke0cr/BguDxhWMpm5MJu1O2muzk+tKw6lKHjODAsYArBbWwFbJiaksfzFv1tpkO9QHGzmfs1hJT66g==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", @@ -1138,36 +1539,36 @@ "lightningcss": "1.33.0", "magic-string": "^1.1.0", "source-map-js": "^1.2.1", - "tailwindcss": "4.3.4" + "tailwindcss": "4.3.5" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.4.tgz", - "integrity": "sha512-riqsRw2sLF7B1sNCCkTl3KYXEqvhTiABzZ/4oxxHfZ3c3Td60bCBfOrUuUGOiHagd3E7V+zG46c+XBDWZGFBcg==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.5.tgz", + "integrity": "sha512-PynqGLZsZvwM9tqD/CUxkOIfyd6K5elZ2MooauzJryP/UqXYgi3yYbtt3N0Xe+MHV/eFKPFQCEfIKAqlAS1CBg==", "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.4", - "@tailwindcss/oxide-darwin-arm64": "4.3.4", - "@tailwindcss/oxide-darwin-x64": "4.3.4", - "@tailwindcss/oxide-freebsd-x64": "4.3.4", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.4", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.4", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.4", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.4", - "@tailwindcss/oxide-linux-x64-musl": "4.3.4", - "@tailwindcss/oxide-wasm32-wasi": "4.3.4", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.4", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.4" + "@tailwindcss/oxide-android-arm64": "4.3.5", + "@tailwindcss/oxide-darwin-arm64": "4.3.5", + "@tailwindcss/oxide-darwin-x64": "4.3.5", + "@tailwindcss/oxide-freebsd-x64": "4.3.5", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.5", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.5", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.5", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.5", + "@tailwindcss/oxide-linux-x64-musl": "4.3.5", + "@tailwindcss/oxide-wasm32-wasi": "4.3.5", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.5", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.5" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.4.tgz", - "integrity": "sha512-qkjxt+EFocOi3mtsUS3ndlj6yj4SHubwBdvBynLnS8T4mvSzX0Enn0CpypJKmBEtczUAXOHVZKA2zTGzSNU2Sg==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.5.tgz", + "integrity": "sha512-Se0z5xMqcSQanJD1IYmO0cGpK3iv0fp3ZsIT7xRJZ1Da5PghvNoc46zz4jIQn+aan3dV9T8AermFNN9QPmsh0Q==", "cpu": [ "arm64" ], @@ -1181,9 +1582,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.4.tgz", - "integrity": "sha512-i/awUA/7mJQAbA3AzCW8aQtUh5goMieUdF1zOoclLaJUCxDvsXQ8VJqpQZY3dfcHS6PJjd3tw25nNneD/mEviQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.5.tgz", + "integrity": "sha512-UPrgTDuUbvmqGMZuCnmI3FKee/tYKzKdp00/kuB4oSGB213VYg8QTdF7lVhnPoBXq/mIfJ+ILoBFqNOboWcMwQ==", "cpu": [ "arm64" ], @@ -1197,9 +1598,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.4.tgz", - "integrity": "sha512-q56V9U6AnoYz/yj/alkDLhSQ9AZoXQSEl93f7WETa0+32CudbqM57BXdYXfMYPKudueXKaA+MG6L/0KRseF7Jg==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.5.tgz", + "integrity": "sha512-E+p7HEtjI0b8D7kbbWxH067cGui6d4c79COGko9fAPVE3rwc1E7ld2HFCbaG86AOGWS5VKQYqZOaf38ya4uxTQ==", "cpu": [ "x64" ], @@ -1213,9 +1614,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.4.tgz", - "integrity": "sha512-r0W7A+Z9HTHh7bwihJl45E/zZKLjxD50/jToVF/E/RZHSl8+EWO+4yKKJpPMcUooT9hdoip3Zx1uWnbMg4N79g==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.5.tgz", + "integrity": "sha512-yWEWcWyAG7rWOcTpJTjdhQ3pcaFY2h+0bqTRfgU3G2fz87ZU3AKdvdZKhslD+kyHnjBUWKvNdRYwIYC5dI1tIg==", "cpu": [ "x64" ], @@ -1229,9 +1630,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.4.tgz", - "integrity": "sha512-3aih/sRxzeYfi2Bkh+bGYNf2YU9Llamb2mpA7BNF1obYzdgEeWDAMQzK6BSwzLhs/eTdOaSur92qrGfchagUtQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.5.tgz", + "integrity": "sha512-WmeVjtJhZlr+smpqcdR2kQkkn6arHIJnUIBxTO2EJcTLGY1ecVXNo2hbSn8xc0IH2aEtys936iMWKGyj0GaKnA==", "cpu": [ "arm" ], @@ -1245,9 +1646,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.4.tgz", - "integrity": "sha512-ktJSrPGQou4OqL88rDEx5CkeM9BjzrwZvYo8u7B7VL1zkBa2XBm351o4MQrek95qikb2M++26Y7w7vbH6Mgl8g==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.5.tgz", + "integrity": "sha512-DTCSyucc6ShMp1QMqV5R94Z7xlgNgUzkGmFF1rL5HH2Qjy4eSmEqy+YLGkqqYvWa4sILtgrBt1JjRKbneQtiFQ==", "cpu": [ "arm64" ], @@ -1264,9 +1665,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.4.tgz", - "integrity": "sha512-rt3hjUxT7utAtcB+df6wHyaXDyxhE7Zd1x7L/nvB5Jb2qBb8dZwFUoWqeRja1BaF5OlSqkKSjBmVXj9OsevmCQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.5.tgz", + "integrity": "sha512-H9lsvO4x9E4nE4Hi8h7NHHw0O/kcVSu+iOyFHNGdYzazGgDjFHZbE6S3hQ8kMcbNXSBlMdNi67LV5EVqtpaJtw==", "cpu": [ "arm64" ], @@ -1283,9 +1684,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.4.tgz", - "integrity": "sha512-XE3jO2yOOWjttpeGvpwf3/b274NMi3QapQSAI/EzsgrmYNan1Tw8dXT9JQkN7K0OgvuqiC8mcCyNvGzEkpVJPQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.5.tgz", + "integrity": "sha512-4uxQvtBvPuIdl13WfUBj9xMZHH14PtHZ8OZdtLmdyDtG66sCa1t599NZT+jagkUzRtWhuul1qRZQ1nAJscXN5g==", "cpu": [ "x64" ], @@ -1302,9 +1703,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.4.tgz", - "integrity": "sha512-PaAZ2FjJaZk9SQLYtKIfQWiFm/xtl7pX2MKPgCzn0mS/VU54yu+vP457AJdB2XFLxU3gO2abLCt8N7Lg4EbKNg==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.5.tgz", + "integrity": "sha512-0JQAzs7jwbA7O6+z/adU+Jk3AXwsu3npAhtr5escXS7TqEqWcaDV7qHuI+0Vy/KQRuVGnLQREcSJWYauyV3oqg==", "cpu": [ "x64" ], @@ -1321,9 +1722,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.4.tgz", - "integrity": "sha512-bjO9WmoM687p8AoCIyw1DUqzpAs4JICLvNmIPxIWDmhw8IOsdubtWQLShyJmISnDKtRy1BBApHVOUl6sNFQQJA==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.5.tgz", + "integrity": "sha512-OJhFmlEfdzMr+avACdi40RPtnkW0B6hXyxL/8r+tnEHBWZji36CYRjEXrrvgBmaYsD3oEOBqwYFRKsYvzFe7Bw==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1410,9 +1811,9 @@ "optional": true }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.4.tgz", - "integrity": "sha512-jCrwCvWVtt7ruMXp6dFPd5KBNU8o1tWtEdZbTL/w/hvD3lUYZA0E2mmfvTzW4QLHIx5wGQU9HeY7997R1QTrEg==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.5.tgz", + "integrity": "sha512-9o8jgthi+QE5eandnboiJT64VQfnIArfZzSTdgbhEK5aafdiu2jTW9Honi/mwYHxBZuH1vAC57IFyrh+0iK5Gg==", "cpu": [ "arm64" ], @@ -1426,9 +1827,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.4.tgz", - "integrity": "sha512-cge1wcedn1FrRSXL22lF5sNDYr8c9+VImk7zFYLPs12jkDmchSrZNfzWqOKub+5Hb/aYRdDEqERGGDKONBlOzQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.5.tgz", + "integrity": "sha512-XiKFfIy1RWe7qK4WqByxNW7Oz1/nkOy8o/BWhHminyV1PmpUd8Eedj5v2A8h2kpqeVlz6xRnwk9Fr/ZXNLcZfg==", "cpu": [ "x64" ], @@ -1442,14 +1843,14 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.4.tgz", - "integrity": "sha512-wGukGWb8SVuN7+FJvSS7M5eSJWDIWwVSvUvj1q41eTIhGPsS2DAgFWjA7YURJNwjHSgty59Hz8iMUiyO+YMHBQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.5.tgz", + "integrity": "sha512-BCNiY0a1ZjdOHVqw7kyb7U72BwfMF3dfUuN+tQsLWky/J5JOI/r7abibglXfygrZhVO/3u3V+E25kkKTeMkXaQ==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.3.4", - "@tailwindcss/oxide": "4.3.4", - "tailwindcss": "4.3.4" + "@tailwindcss/node": "4.3.5", + "@tailwindcss/oxide": "4.3.5", + "tailwindcss": "4.3.5" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" @@ -1482,6 +1883,16 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -1523,9 +1934,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.10.tgz", + "integrity": "sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -1980,9 +2391,9 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.2.1.tgz", - "integrity": "sha512-9KyWCij3r0+qLG3Q62vBmv0VyevmYibAtxaCIVK9o0k6CXgaajZRIe8tpfchQcdZArJ5b/7OrIz4W3b2uxVRDA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.3.0.tgz", + "integrity": "sha512-H9E955aVvD2F4zNHCw4eIZbIfF6xKldlh7MNu+hSU7xwQzh65lI/N+CJWqmfhi0PEjDXIt/H7T7J/4wU63KJiw==", "dev": true, "license": "MIT", "dependencies": { @@ -2037,16 +2448,6 @@ } } }, - "node_modules/@vitest/mocker/node_modules/magic-string": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.5.tgz", - "integrity": "sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.6.0" - } - }, "node_modules/@vitest/spy": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.1.0.tgz", @@ -2225,9 +2626,9 @@ } }, "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.2.tgz", + "integrity": "sha512-GYrCADwpu6yV5OAvchkQ27Z5wwMxWAEMg5NNWjyHh/5DboFFqbdbvYNIvZouQpxXyeMPoTOh0vdPYKlf0ZswdQ==", "dev": true, "license": "ISC", "bin": { @@ -2270,23 +2671,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/app-builder-lib/node_modules/minimatch": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.4.tgz", - "integrity": "sha512-/UJ1dEoVQTjPNDh6SGlUo+ueLMmoHTas4q+8yaAR/5t6nLAIiBDJtoYUh20IYleix7ehiQkgczb84PE3uzHkxg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/nonrecursive-glob-match": "^1.0.3", - "brace-expansion": "^5.0.12" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/app-builder-lib/node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -2424,9 +2808,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.23", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz", - "integrity": "sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ==", + "version": "2.11.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.37.tgz", + "integrity": "sha512-QYph+vfurmaxhNwi9FprfmtkT5cx2Keg8FzZ0vnjO8ukscIybVyZGMFwdllKGlTI6qyU8ck43OM9UXh+IFJihQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2444,9 +2828,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.13.tgz", + "integrity": "sha512-Vh3dmFbc4wZeox7M9I/0L9GfFd83Su4+yCck/OF9Gi/P/dAHn0RT7X4K9R7gMNMhDck3DvhR6QXsnQdjjgwNvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2457,9 +2841,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", - "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "version": "4.29.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.5.tgz", + "integrity": "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==", "dev": true, "funding": [ { @@ -2477,11 +2861,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.11.20", - "caniuse-lite": "^1.0.30001810", - "electron-to-chromium": "^1.5.420", - "node-releases": "^2.0.54", - "update-browserslist-db": "^1.3.2" + "baseline-browser-mapping": "^2.11.33", + "caniuse-lite": "^1.0.30001819", + "electron-to-chromium": "^1.5.464", + "node-releases": "^2.0.60", + "update-browserslist-db": "^1.3.4" }, "bin": { "browserslist": "cli.js" @@ -2547,9 +2931,9 @@ } }, "node_modules/cac": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz", - "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.1.0.tgz", + "integrity": "sha512-LMDU/U2yA4IPIY8KT0NsNPWEYZvAMkbgyspxJ6btJaYV+WMDOHAZCmB0lsTObiHaRS1V/ztKAl7+TSkTqXyeoA==", "dev": true, "license": "MIT", "engines": { @@ -2600,9 +2984,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "version": "1.0.30001826", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001826.tgz", + "integrity": "sha512-wTB+9tIbLro/BCXwLuNV1meHE4L9dS6dArMDwgYfNBPMYScqClHyGunhkCSDtHhe/69Xl0sHlKgBVZ+fNsYtWA==", "dev": true, "funding": [ { @@ -2715,9 +3099,9 @@ "license": "MIT" }, "node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.5.0.tgz", + "integrity": "sha512-d1uM8k5UZEyPza1n/MG3ko8nKupKN0KJpFsipgPcgaCCvV+IqIfJ+NzTjetlLM+UcXZx3+cLrNr76+eAO7G1Mw==", "dev": true, "funding": [ { @@ -3062,9 +3446,9 @@ "license": "MIT" }, "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.22.tgz", + "integrity": "sha512-CKLDTuSsN0+r1ThM0WkzzBI7dnPI6xEGfa/JnVYIK9JNBA1N1NyPwXVHor//NKD/y3sDxp764sZdLhFK7pH8XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3169,9 +3553,9 @@ } }, "node_modules/electron": { - "version": "45.2.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-45.2.3.tgz", - "integrity": "sha512-Vurzu5UOA5nTGT4LqIwXD16QFGrOZdiXxF+1UP4KNsAOo/YtDcT3/u0jhP4WSwNJ0iLrLDi85hzBJ0arnGDhyw==", + "version": "45.3.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-45.3.2.tgz", + "integrity": "sha512-bPO0MMvJCdKdPfXgd3ES8zupu+ocr6XmSuRGwyZazTiigTz8jE7rwukSkck0iomS6UZeFuCwzu0uanMD/D34uw==", "dev": true, "license": "MIT", "dependencies": { @@ -3245,9 +3629,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.427", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.427.tgz", - "integrity": "sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==", + "version": "1.5.485", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.485.tgz", + "integrity": "sha512-m3uadzR0Z+sR19HGWgfxoZcKv9hE/edVM9OZ+FJ4wNRyfuFtkwZJIDoehEty5sisYVwmge8lKV1tenWOyClVgw==", "dev": true, "license": "ISC" }, @@ -3370,19 +3754,19 @@ } }, "node_modules/electron/node_modules/@types/node": { - "version": "24.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.4.tgz", - "integrity": "sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==", + "version": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/electron/node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, @@ -3404,9 +3788,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.26.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.1.tgz", - "integrity": "sha512-PPnnSJq3azKOIewXR/uppBV/6h98dprBJ/WaQ9Qgj10xX1lJLNfg+XShpbsrSqcDSy2yYYyhfXM1wWhftqgyzw==", + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.27.0.tgz", + "integrity": "sha512-Do6BB2xzlcc/odFg+XEYyneAx4T/WfywO71yw9ePS2tVfRsf7JVR4mUeELadIeHzixkzbG5DpluiEFPi2WGfYQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -3417,12 +3801,12 @@ } }, "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-9.0.0.tgz", + "integrity": "sha512-oJFNDJg3kTPwAD1cNn2+WmUq4wFoo09npc3Ydf1D7ED4smRiWrsKnaLxykrTWgyGEcusc87P5dxlAleol9kpkg==", "license": "BSD-2-Clause", "engines": { - "node": ">=0.12" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" @@ -3628,9 +4012,9 @@ "license": "MIT" }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", - "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.8.tgz", + "integrity": "sha512-BnlDzDYQoplTsZSvPi86CNzmbcb07X8ufR8fk1zivaiEJlXJkiq1MQyDCVmlWcsWbj6aWXqHnrwwevbrm/Saig==", "dev": true, "license": "MIT", "dependencies": { @@ -3817,9 +4201,9 @@ "license": "MIT" }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.22.tgz", + "integrity": "sha512-CKLDTuSsN0+r1ThM0WkzzBI7dnPI6xEGfa/JnVYIK9JNBA1N1NyPwXVHor//NKD/y3sDxp764sZdLhFK7pH8XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3841,9 +4225,9 @@ } }, "node_modules/global-agent": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-4.1.3.tgz", - "integrity": "sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-4.1.7.tgz", + "integrity": "sha512-pxaZqdsMwPA4i7TuRKpDUONm2xGRjvYdRoidCQz7VleUKVuUfh/4p5Q3QDTGrpAE5Kz5TxxcZ+uqEgTkoKRNtg==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -3915,10 +4299,10 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.12.tgz", + "integrity": "sha512-iuyGF2E4oafQfPsRrG2wtY2ILupMhvtdrhzrxB+0VcjsPpSPttpfYdjnGFGsTi5MaJFjI1jOKHPQ6UTbTj9TAw==", + "license": "BlueOak-1.0.0" }, "node_modules/has-flag": { "version": "4.0.0", @@ -3987,9 +4371,9 @@ } }, "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.4.tgz", + "integrity": "sha512-Yq7XlXor0Cpb71sI2E0zpv8QxQ5a6rfhDEETk4bcgrFX2SvJAY2PHocWwFtL6U9qyfiyByW3wZ6sZEfP2kW81g==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4020,9 +4404,9 @@ } }, "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.2.0.tgz", + "integrity": "sha512-1zVq3M9AlZiBZ4mlMPnLtkK2rdd+I8VhmFLNLb//XJm6xA2AVH2tz19EHy25RgRWut6z6FpTARAs7P8rnmRJUQ==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4032,7 +4416,7 @@ "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", + "parse5": "^8.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", @@ -4060,9 +4444,9 @@ } }, "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.7.tgz", + "integrity": "sha512-F7DNV2NMcTxA4GqPVHnXzX+mpoPHAIY9rJAVH3CZ1FdBZOcBUEjUuvMkMpHVs/sicEtznh9tMGj5WUR9S4E2rg==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -4077,7 +4461,7 @@ "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", + "style-to-object": "^2.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" }, @@ -4119,9 +4503,9 @@ } }, "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.3.tgz", + "integrity": "sha512-1ALZXSmCGmW3h8AMz4R4+QKSKFi7sNgXLVwmwB8FYVtl1CwQ3WDkSAEY/d5tp1g/vbQgq2s1WlHgfO3jbw1TsA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4257,9 +4641,9 @@ "license": "ISC" }, "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.9.tgz", + "integrity": "sha512-IttN8BqKLxzUrnTqHI+/hwAJQItBAUjdoHERCDv+oOuX48VP6bzW8+QjOXSfoevzq3YrYAwX1uLHp8TqO9yIeg==", "license": "MIT" }, "node_modules/is-alphabetical": { @@ -4794,9 +5178,9 @@ } }, "node_modules/lucide-react": { - "version": "1.69.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.69.0.tgz", - "integrity": "sha512-UTFECm4yEgkgMN0kfLuxWcmlk+4u02Jrqehgk3ZXRx/Ud68OpP7hjkp67bvkBHmIpMaZCIveF1QDYeupO7XmYQ==", + "version": "1.73.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.73.0.tgz", + "integrity": "sha512-fVOzGviL9mTfHenG1PbCfIceavyGlnpYNSYr+aGZHLRymAJ7DTP6KHuqt0uBkCKAoenr1UVkwSbDIYt2xIesEQ==", "license": "ISC", "peerDependencies": { "@types/react": "*", @@ -4818,9 +5202,9 @@ } }, "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.1.0.tgz", + "integrity": "sha512-xpeSCdM+B+6P3ncfgaB7FoDbtJ30xAe6Z18TGT7AgXJsHqCElCICZovQqdRMm7kHgpTyYv36EupqewoFFSB8vw==", "license": "MIT", "funding": { "type": "github", @@ -4869,15 +5253,15 @@ } }, "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.4.tgz", + "integrity": "sha512-z4B5c6jrnJ1ltOQemhWYPBSAo6y42Z8Gt2yEgfgNTMcLSU59B8bfj8Z8AnAlJrBGLnqkdAUZ7jdDEqQw3KuipQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "unist-util-visit-parents": "^6.1.0" }, "funding": { "type": "opencollective", @@ -4885,9 +5269,9 @@ } }, "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.1.0.tgz", + "integrity": "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4928,9 +5312,9 @@ } }, "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-Jx6QSK8wDo558Bcf7q+1pe0v81g2PVs09Hlvu9xJHmCFRF3sAPW7vXFVIAwBncs3sde92WbsVqNxwdqViA2XnQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4962,9 +5346,9 @@ } }, "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.1.tgz", + "integrity": "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4977,16 +5361,18 @@ } }, "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.1.tgz", + "integrity": "sha512-fiIjPrAy4kz68/eFbPHhsiWDGHhqUSVoU1XsfLM5DsEPXw7MxgiVVyulUggFRckVWeT1KXQXkg5vm3kv4oMSOA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" + "mdast-util-to-markdown": "^2.0.0", + "mdast-util-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" }, "funding": { "type": "opencollective", @@ -4994,9 +5380,9 @@ } }, "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.1.tgz", + "integrity": "sha512-jXVO1hvB2hs82g1esi7I9nIJDqiijbQLIyH2jDUb5s7jIAZMr/79h0VHSq7XHKTqpLARRm+DDNjduB7S9+6XZA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -5028,9 +5414,9 @@ } }, "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.1.tgz", + "integrity": "sha512-oXp1GIa+E9IwIs3a106+tr3Bz2MlTTBvlXjOZMoIat11kk7vjqgmBnksB9WHmWv+gZh7k3IUgpAHlwHB7DH4GQ==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -5084,9 +5470,9 @@ } }, "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.2.tgz", + "integrity": "sha512-l2pnmOuqs+SGLVpTVXnRLQu5v1AzvDe181404fbXzzqObVxrAnnVxapAP27pUwzLBL/ABIkgUhGlPVU7QSvIFg==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -5105,9 +5491,9 @@ } }, "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.2.3.tgz", + "integrity": "sha512-dImdB6VZRMRac1SwAW6o1c0ESk/7pFFAMCMuKM+9v7LpTSthMM5nzXpqKw4Mjo66aXVKhNkyrP4bMB9MurPyWg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -5115,8 +5501,10 @@ "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", + "micromark-util-character": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" }, @@ -5139,9 +5527,9 @@ } }, "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", + "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==", "funding": [ { "type": "GitHub Sponsors", @@ -5164,6 +5552,7 @@ "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", @@ -5174,9 +5563,9 @@ } }, "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz", + "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==", "funding": [ { "type": "GitHub Sponsors", @@ -5193,18 +5582,19 @@ "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", + "micromark-factory-space": "^2.1.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", + "micromark-util-edit-map": "^1.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "micromark-util-types": "^2.0.3" } }, "node_modules/micromark-extension-gfm": { @@ -5372,9 +5762,9 @@ } }, "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", + "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", "funding": [ { "type": "GitHub Sponsors", @@ -5556,6 +5946,25 @@ "micromark-util-symbol": "^2.0.0" } }, + "node_modules/micromark-util-edit-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz", + "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, "node_modules/micromark-util-encode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", @@ -5686,9 +6095,9 @@ "license": "MIT" }, "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", + "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", "funding": [ { "type": "GitHub Sponsors", @@ -5747,6 +6156,23 @@ "node": ">=4" } }, + "node_modules/minimatch": { + "version": "11.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-11.0.8.tgz", + "integrity": "sha512-nFQuYSTVrGvysVBps5Ih+0BdnYFN/Cj3C816r6+p7yK+FHrVLGE4O4N9AFwykrlNlvM2HYt+Rad1Uiro6h88Ig==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/nonrecursive-glob-match": "^1.1.0", + "brace-expansion": "^5.0.12" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -5920,9 +6346,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.55", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", - "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", + "version": "2.0.64", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.64.tgz", + "integrity": "sha512-zAPCKqMcnqs2nGy6nlkm7JduFseiBJy3EXHGLh+8pFR+TxGCBxxiEN21+UROU5CKwX/f4TlUcYjiM+vs/tsyxw==", "dev": true, "license": "MIT", "engines": { @@ -5993,6 +6419,55 @@ "wrappy": "1" } }, + "node_modules/oxlint": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.93.0.tgz", + "integrity": "sha512-19JMsQFX+YvEbcUTFQxjTwhvs0Ibg0D6CFneSLgpwnACWcY/6LjhpGpkPodh0T5aIT1pjMTLlYaeBFzaVgugTA==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.93.0", + "@oxlint/binding-android-arm64": "1.93.0", + "@oxlint/binding-darwin-arm64": "1.93.0", + "@oxlint/binding-darwin-x64": "1.93.0", + "@oxlint/binding-freebsd-x64": "1.93.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.93.0", + "@oxlint/binding-linux-arm-musleabihf": "1.93.0", + "@oxlint/binding-linux-arm64-gnu": "1.93.0", + "@oxlint/binding-linux-arm64-musl": "1.93.0", + "@oxlint/binding-linux-ppc64-gnu": "1.93.0", + "@oxlint/binding-linux-riscv64-gnu": "1.93.0", + "@oxlint/binding-linux-riscv64-musl": "1.93.0", + "@oxlint/binding-linux-s390x-gnu": "1.93.0", + "@oxlint/binding-linux-x64-gnu": "1.93.0", + "@oxlint/binding-linux-x64-musl": "1.93.0", + "@oxlint/binding-openharmony-arm64": "1.93.0", + "@oxlint/binding-win32-arm64-msvc": "1.93.0", + "@oxlint/binding-win32-ia32-msvc": "1.93.0", + "@oxlint/binding-win32-x64-msvc": "1.93.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2005", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -6045,12 +6520,12 @@ "license": "MIT" }, "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.1.1.tgz", + "integrity": "sha512-EcR/6WDcU7b2N3vQSxKALCk9lXGtUMQxXio008OEMBE0kavu/7JLfBEeFPzi43nMqVDSbEn3QCK+BMCc9zO//w==", "license": "MIT", "dependencies": { - "entities": "^6.0.0" + "entities": "^9.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -6146,9 +6621,9 @@ } }, "node_modules/postcss": { - "version": "8.5.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", - "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "version": "8.5.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.33.tgz", + "integrity": "sha512-RVHEU9/Pp1Fnjr/MpAJsZoADukCKhfIi15OunEYiGktIkYLkPVvC1sioWolGDS7XByMTx9m/IGbro0Js13zhnQ==", "funding": [ { "type": "opencollective", @@ -6566,9 +7041,9 @@ } }, "node_modules/rolldown": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.16.tgz", - "integrity": "sha512-bWvlMpFKiTfDKfnK7FC2MPgPeWCf/Kpwsq49ApdW+FQaBZb6O6dZ4MAPbPUjp7hYQrw/RobqCzn64BRrQecGHw==", + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.19.tgz", + "integrity": "sha512-QIiV1J9d0pzN4jeeTH4sjPy0AZ6cAy2cG0ExeoE1+U8Rvj+hR3gYkokx0YbeUNGlZqiQJJ2utcETe0oKXwzsuA==", "license": "MIT", "dependencies": { "@oxc-project/types": "=0.156.0", @@ -6581,21 +7056,22 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm-eabi": "1.2.16", - "@rolldown/binding-android-arm64": "1.2.16", - "@rolldown/binding-darwin-arm64": "1.2.16", - "@rolldown/binding-darwin-x64": "1.2.16", - "@rolldown/binding-freebsd-x64": "1.2.16", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.16", - "@rolldown/binding-linux-arm64-gnu": "1.2.16", - "@rolldown/binding-linux-arm64-musl": "1.2.16", - "@rolldown/binding-linux-ppc64-gnu": "1.2.16", - "@rolldown/binding-linux-s390x-gnu": "1.2.16", - "@rolldown/binding-linux-x64-gnu": "1.2.16", - "@rolldown/binding-linux-x64-musl": "1.2.16", - "@rolldown/binding-openharmony-arm64": "1.2.16", - "@rolldown/binding-win32-arm64-msvc": "1.2.16", - "@rolldown/binding-win32-x64-msvc": "1.2.16" + "@rolldown/binding-android-arm-eabi": "1.2.19", + "@rolldown/binding-android-arm64": "1.2.19", + "@rolldown/binding-darwin-arm64": "1.2.19", + "@rolldown/binding-darwin-x64": "1.2.19", + "@rolldown/binding-freebsd-x64": "1.2.19", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.19", + "@rolldown/binding-linux-arm64-gnu": "1.2.19", + "@rolldown/binding-linux-arm64-musl": "1.2.19", + "@rolldown/binding-linux-ppc64-gnu": "1.2.19", + "@rolldown/binding-linux-s390x-gnu": "1.2.19", + "@rolldown/binding-linux-x64-gnu": "1.2.19", + "@rolldown/binding-linux-x64-musl": "1.2.19", + "@rolldown/binding-openharmony-arm64": "1.2.19", + "@rolldown/binding-wasm32-wasi": "1.2.19", + "@rolldown/binding-win32-arm64-msvc": "1.2.19", + "@rolldown/binding-win32-x64-msvc": "1.2.19" } }, "node_modules/safe-buffer": { @@ -6606,9 +7082,9 @@ "license": "MIT" }, "node_modules/sanitize-filename": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", - "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.5.tgz", + "integrity": "sha512-+dVApt46/oAFWRP0KBSnYNLRw1peY7o/b12tmpllPNFpjfwkPuZut1+DiKaD9zYx5r2SHwc+XMLSBaDiyUJw3g==", "dev": true, "license": "WTFPL OR ISC", "dependencies": { @@ -6631,9 +7107,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.6.tgz", + "integrity": "sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==", "dev": true, "license": "ISC", "bin": { @@ -6812,22 +7288,13 @@ "node": ">=8" } }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-2.0.2.tgz", + "integrity": "sha512-QmbXPUylqU80HAgTOJeEOdbA40W9KtxmjMhQy+tq+b8F2aoh3aQLuy82hh2jm2qsC9hR9eI0+eQidTMydKcqRA==", "license": "MIT", "dependencies": { - "inline-style-parser": "0.2.7" + "inline-style-parser": "0.2.9" } }, "node_modules/sumchecker": { @@ -6857,15 +7324,15 @@ } }, "node_modules/tailwindcss": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.4.tgz", - "integrity": "sha512-PCJZTFFdG+sL3gvnP1Do9o6GbkvH2q0p04xSs3f7cSvmAeK+sttG+/7ncK1fdRwKFfC7n+wDDJaOMBSefkI9ow==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.5.tgz", + "integrity": "sha512-FcQk9BuffmEHJP2G0XmB2SplU1aLTI7j07Kunk+U74Ny+XiWOtzXz9aujFZ/avntw1e2cOv/3PkgzflQ7gX2vQ==", "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.4.tgz", + "integrity": "sha512-NHFiWnAbMJxNNpBwiO7jJVGDuqMxT9/oV8v57RR+fgBQShkJil5inGD8/xWy0hjLyHQyX1tu27QANhlz3KBqCg==", "license": "MIT", "engines": { "node": ">=6" @@ -6876,9 +7343,9 @@ } }, "node_modules/tar": { - "version": "7.5.22", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", - "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "version": "7.5.23", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.23.tgz", + "integrity": "sha512-lkukzdoGngZHJZXxW4S/E4Fp9ghTDlyZXrQGRNoJ/LXvDKcXUTq2sg5yQe74po7Voea6n/IN09+wE/QvJMiifA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -6939,9 +7406,9 @@ } }, "node_modules/tiny-async-pool/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.3.tgz", + "integrity": "sha512-kM18BPOqTi0+TB68703/GJRpAML5Me+IDQsR1J26YB3EFKKNEyHIVZuWDX7+WKQB3tHwDwgk24BfhdpWUbytvQ==", "dev": true, "license": "ISC", "bin": { @@ -6955,9 +7422,9 @@ "license": "MIT" }, "node_modules/tinybench": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", - "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.3.tgz", + "integrity": "sha512-Al01C2JIBnO2CXCAPbpAl6sUiOZHeThGtHYNTa51JRw9PyWOCdr+9Ong05R4qg/Sk3+H645E8ov1h2c58ityNw==", "dev": true, "license": "MIT", "engines": { @@ -7041,10 +7508,10 @@ } }, "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", + "devOptional": true, "license": "0BSD" }, "node_modules/type-fest": { @@ -7097,9 +7564,9 @@ } }, "node_modules/undici": { - "version": "7.30.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", - "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", + "version": "7.30.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.2.tgz", + "integrity": "sha512-mLyPQnHwUxaM/rideoK/+KL8DaAJLRl4aBCfmX444aCSLnIaomqPLtNRe2jdCYcsk5wZzXrDIQC8ZeYNRAYyIA==", "dev": true, "license": "MIT", "optional": true, @@ -7173,14 +7640,14 @@ } }, "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.2.0.tgz", + "integrity": "sha512-fpsYs/VVoZxfGY9/j3FQpZmN8wdLJaV1Pt38AaJNZu9uKIHcFU3halCdBveEq1HGKO1OEfJY65w1zvoFV1b3qQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" + "unist-util-visit-parents": "^6.1.0" }, "funding": { "type": "opencollective", @@ -7188,9 +7655,9 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.1.0.tgz", + "integrity": "sha512-vKXxz6O38sKDj1mtx/k2cWvkkFBuYGx6uGrlzBkVeWCRF/F8sdI0j3Wbkz/5rVNRRuVc8UrgRdbCxNhmYXgP3w==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -7240,9 +7707,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", - "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", "dev": true, "funding": [ { @@ -7299,9 +7766,9 @@ } }, "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.4.tgz", + "integrity": "sha512-vEvxatYZCIpNvzGk2UD+u4I/sZkSKoOsdRCbkOIsN73QpJbriJ1qvvBAL1PeI0y55Leoqf7pORbaSPpG/smmOg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -7313,9 +7780,9 @@ } }, "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.4.tgz", + "integrity": "sha512-66ycRswIkNmHH9PHku7ADtv5RVwgIrOcuv3J8Svz6h4qgGRrKVa+9/neuKyJZUXyOpYwLTvS+whZ4R2urHHgQg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -7327,15 +7794,15 @@ } }, "node_modules/vite": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.6.tgz", - "integrity": "sha512-UI7dK29NnS54Fk+mmo7o+jI6U8Fp7hDl+X5Q1k45WH+d4dSlbLVUETGQ+hfkNgvLQGyoElPj837nZyv4MBc0VA==", + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.8.tgz", + "integrity": "sha512-LzOn3h9rG/0vhLdXrV4yJ4QFA9eEoqCl+Yx+kGJd7aB9w6nfNgVHKSYwLPZUHrz4ZGa+t+kXjV9itvcHDLQouA==", "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.32", - "rolldown": "~1.2.16", + "rolldown": "~1.2.18", "tinyglobby": "^0.2.17" }, "bin": { @@ -7634,9 +8101,9 @@ } }, "node_modules/zustand": { - "version": "5.0.16", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.16.tgz", - "integrity": "sha512-Uv7e327ihlCfayfja3hDc0M64Upev1LWBKpRobdK4NyovDDPeeX4imWXc/wzC1yc6fkYPoAY/ncKVPAs2jrtQw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.1.0.tgz", + "integrity": "sha512-ciJZlP48M1PTQbVQq81CGVFxHTLpZNrFjKqwjtCU34AWuXTZT/jrF3p9+fI84Cf1eSCMLapxqBy/SNTfwOBuqA==", "license": "MIT", "engines": { "node": ">=12.20.0" diff --git a/GitWeb/package.json b/GitWeb/package.json index b8f47ee..0ea8677 100644 --- a/GitWeb/package.json +++ b/GitWeb/package.json @@ -15,6 +15,7 @@ "dev": "electron-vite dev", "build": "electron-vite build", "start": "electron-vite preview", + "lint": "oxlint", "typecheck": "tsc --noEmit -p tsconfig.node.json && tsc --noEmit -p tsconfig.web.json", "test": "vitest run", "build:win": "npm run build && electron-builder --win", @@ -29,18 +30,18 @@ "@fontsource/inter": "^5.3.0", "@fontsource/jetbrains-mono": "^5.3.0", "@icons-pack/react-simple-icons": "^13.15.1", - "@tailwindcss/vite": "^4.3.4", + "@tailwindcss/vite": "^4.3.5", "@tanstack/react-virtual": "^3.14.26", "electron-updater": "^6.8.11", - "lucide-react": "^1.69.0", + "lucide-react": "^1.73.0", "react": "^19.3.1", "react-dom": "^19.3.1", "react-markdown": "^10.1.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", - "tailwindcss": "^4.3.4", - "zustand": "^5.0.16" + "tailwindcss": "^4.3.5", + "zustand": "^5.1.0" }, "overrides": { "@electron/get": { @@ -51,14 +52,15 @@ }, "devDependencies": { "@types/node": "^26.6.6", - "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", - "@vitejs/plugin-react": "^6.2.1", - "electron": "^45.2.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@vitejs/plugin-react": "^6.3.0", + "electron": "^45.3.2", "electron-builder": "^26.18.0", "electron-vite": "^6.0.0", + "oxlint": "^1.93.0", "typescript": "^7.0.2", - "vite": "^8.3.6", + "vite": "^8.3.8", "vitest": "^5.1.0" } } diff --git a/GitWeb/src/main/credentials.ts b/GitWeb/src/main/credentials.ts index 73e6f6e..6df2a5e 100644 --- a/GitWeb/src/main/credentials.ts +++ b/GitWeb/src/main/credentials.ts @@ -83,7 +83,8 @@ export async function getSecret(key: string): Promise { decrypted = await safeStorage.decryptStringAsync(Buffer.from(blob, 'base64')) } catch (err) { throw new Error( - `Failed to decrypt secret "${key}": ${err instanceof Error ? err.message : String(err)}` + `Failed to decrypt secret "${key}": ${err instanceof Error ? err.message : String(err)}`, + { cause: err } ) } // The OS key rotated (or a stronger one exists): store the secret again diff --git a/GitWeb/src/main/gitProcess.ts b/GitWeb/src/main/gitProcess.ts index 2cf5640..b688cdc 100644 --- a/GitWeb/src/main/gitProcess.ts +++ b/GitWeb/src/main/gitProcess.ts @@ -464,7 +464,7 @@ export function parseStatusBranchHeader(header: string): { } { let branch: string | null = null let upstream: string | null = null - if (!/^## HEAD \(no branch\)/.test(header)) { + if (!header.startsWith('## HEAD (no branch)')) { const headerMatch = header.match(/^## (?:No commits yet on )?(.+?)(?:\.\.\.(\S+))?(?:\s|$)/) if (headerMatch) { branch = headerMatch[1]?.trim() || null @@ -1064,13 +1064,26 @@ async function compareWithDefault( return { vsHead, hasLocalDefault, vsLocalDefault: hasLocalDefault ? vsLocalDefaultRaw : null } } +/** HEAD's subject and committer date, or null on an unborn branch. */ +async function readLastCommit(cwd: string): Promise<{ subject: string; date: string } | null> { + const result = await readGit(['log', '-1', '--format=%cI%x00%s', 'HEAD', '--'], cwd) + if (result.exitCode !== 0) return null + const [date, subject] = result.stdout.trim().split('\0') + return date ? { date, subject: subject ?? '' } : null +} + export async function getRepoUpdateStatus( cwd: string, options?: { fetch?: boolean } ): Promise { // Both are read-only and skip the repo queue, so they can overlap. - const [status, remoteUrl] = await Promise.all([getStatus(cwd), getRemoteUrlForPolling(cwd)]) + const [status, remoteUrl, lastCommit] = await Promise.all([ + getStatus(cwd), + getRemoteUrlForPolling(cwd), + readLastCommit(cwd) + ]) const common = { + lastCommit, branch: status.branch, defaultBranch: null as string | null, behindDefault: 0, @@ -1296,6 +1309,8 @@ export async function getBranchIntegration( // A few at a time: a repo with dozens of branches would otherwise spawn // several git processes per branch all at once. + // Pre-sized on purpose: parallel workers write their result into its own slot. + // oxlint-disable-next-line unicorn/no-new-array const branches: BranchIntegration[] = new Array(candidates.length) let next = 0 const worker = async (): Promise => { @@ -1390,7 +1405,7 @@ export async function deleteIntegratedBranches( const protectedNames = new Set(report.protectedNames) const results: BranchDeletionResult[] = [] - for (const name of [...new Set(names.map((n) => n.trim()).filter(Boolean))]) { + for (const name of new Set(names.map((n) => n.trim()).filter(Boolean))) { const info = local.get(name) let reason: string | undefined if (protectedNames.has(name.toLowerCase())) reason = t.protectedBranch(name) diff --git a/GitWeb/src/main/httpFetch.ts b/GitWeb/src/main/httpFetch.ts index f1de951..c93fdb0 100644 --- a/GitWeb/src/main/httpFetch.ts +++ b/GitWeb/src/main/httpFetch.ts @@ -224,7 +224,7 @@ export async function httpFetch( // Checked on the signal itself: net.fetch reports both a timeout and a // user cancel as a plain AbortError, and only the latter is "cancelled". if (timeoutSignal.aborted && !init?.signal?.aborted) { - throw new Error(tMain().network.requestTimedOut(originLabel(url), timeoutMs / 1000)) + throw new Error(tMain().network.requestTimedOut(originLabel(url), timeoutMs / 1000), { cause: err }) } if (err instanceof Error && err.name === 'AbortError') throw err lastError = err diff --git a/GitWeb/src/main/publishRepository.ts b/GitWeb/src/main/publishRepository.ts index 5f3c588..4e1df88 100644 --- a/GitWeb/src/main/publishRepository.ts +++ b/GitWeb/src/main/publishRepository.ts @@ -43,7 +43,7 @@ export async function publishRepository(cwd: string, input: PublishRepositoryInp await pushBranch(cwd, branch, true) } catch (err) { const reason = (err instanceof Error ? err.message : String(err)).split('\n')[0].trim() - throw new Error(t.pushFailed(repo.htmlUrl, reason.endsWith('.') ? reason : `${reason}.`)) + throw new Error(t.pushFailed(repo.htmlUrl, reason.endsWith('.') ? reason : `${reason}.`), { cause: err }) } return { fullName: repo.fullName, htmlUrl: repo.htmlUrl, branch } } diff --git a/GitWeb/src/main/recentRepositories.ts b/GitWeb/src/main/recentRepositories.ts index 0c2abf4..2d0e5ff 100644 --- a/GitWeb/src/main/recentRepositories.ts +++ b/GitWeb/src/main/recentRepositories.ts @@ -2,7 +2,7 @@ import { existsSync } from 'fs' import { randomUUID } from 'crypto' import { basename, isAbsolute, relative, resolve } from 'path' import { readJson, updateJson } from './appDataStore' -import type { RecentReposData, RecentRepository, RepoGroup } from '../shared/git' +import type { RecentReposData, RepoGroup } from '../shared/git' const FILE_NAME = 'recent-repos.json' diff --git a/GitWeb/src/renderer/src/browserApiStub.ts b/GitWeb/src/renderer/src/browserApiStub.ts index b7a738a..a87d117 100644 --- a/GitWeb/src/renderer/src/browserApiStub.ts +++ b/GitWeb/src/renderer/src/browserApiStub.ts @@ -242,7 +242,14 @@ const recent: RecentRepository[] = [ // One recent repository per state the start screen can draw. function demoRepoStatus(path: string): RepoUpdateStatus { - const base = { ...demoUpdateStatus, localChanges: 0, ahead: 0, behind: 0, refreshing: false } + const base = { + ...demoUpdateStatus, + localChanges: 0, + ahead: 0, + behind: 0, + refreshing: false, + lastCommit: { subject: 'Ajusta o espaçamento do cabeçalho', date: new Date(Date.now() - 3 * 3600_000).toISOString() } + } if (path.endsWith('api-pagamentos')) { return { ...base, state: 'diverged', branch: 'develop', defaultBranch: 'main', localDefaultAhead: 2, localDefaultBehind: 5, behindDefault: 5, canFastForward: false, message: 'main local divergiu de origin/main.' } } diff --git a/GitWeb/src/renderer/src/components/AboutGraph.tsx b/GitWeb/src/renderer/src/components/AboutGraph.tsx new file mode 100644 index 0000000..38253fe --- /dev/null +++ b/GitWeb/src/renderer/src/components/AboutGraph.tsx @@ -0,0 +1,209 @@ +import { useId } from 'react' +import type { UpdateStatus } from '../../../shared/updates' +import { LaneMark } from './RepoLane' + +/** + * About's hero: OpenGitDesktop drawn as one of the repositories it manages. + * History runs in from the left — commits fading with distance, two branches + * that forked and merged back — into HEAD, which is the app's own mark: the + * installed version. To the right is origin/main, and it tells the update + * state in the lanes' own vocabulary: a release on the remote forks off in + * Attention Orange (commits waiting on the remote, as on the start screen), + * the download pulls it in by filling that branch in Command Blue, and a + * finished download turns the tip Success Green — ready to fast-forward. + * + * The words live in the update panel below; this drawing is its twin, so it + * carries one summary label for screen readers and nothing else. + */ + +const VW = 1400 +const VH = 230 +const HX = 700 +const HY = 115 +const EDGE = 40 // half the emblem tile plus its ring, where lanes stop short +const BRANCH = 54 + +// Trunk commits, nearest to HEAD first, one step apart. +const HISTORY = [630, 566, 502, 438, 374, 310, 246, 182, 118] + +/** Nearer HEAD reads stronger; the far past fades into the window edge. */ +function fade(x: number): number { + return 0.15 + 0.75 * (x / HISTORY[0]) +} + +function mergedBranch(from: number, to: number, y: number): string { + return `M ${from} ${HY} C ${from + 24} ${HY}, ${from + 24} ${y}, ${from + 48} ${y} L ${to - 48} ${y} C ${to - 24} ${y}, ${to - 24} ${HY}, ${to} ${HY}` +} + +function Node({ x, y, color, opacity, delay, r = 4 }: { x: number; y: number; color: string; opacity: number; delay: number; r?: number }): JSX.Element { + return ( + + ) +} + +function AboutGraph({ + state, + percent, + targetVersion, + label +}: { + state: UpdateStatus['state'] + percent: number + targetVersion: string | null + label: string +}): JSX.Element { + const id = useId().replace(/:/g, '') + const past = `about-past-${id}` + const future = `about-future-${id}` + + const inFlow = state === 'available' || state === 'downloading' || state === 'downloaded' + const ready = state === 'downloaded' + const tipX = HX + 310 + const tipY = HY - BRANCH + const forkPath = `M ${HX + EDGE} ${HY} C ${HX + 76} ${HY}, ${HX + 76} ${tipY}, ${HX + 112} ${tipY} L ${tipX - 10} ${tipY}` + + return ( +
+ + + + + + + + + + + + + {/* History: the trunk and two branches that came home. */} + + + + + + + {HISTORY.map((x, i) => ( + + ))} + + {/* The remote side, one shape per update state. */} + {inFlow ? ( + <> + {/* Local main stays where it is until the pull lands. */} + + + + + + origin/main + + + {targetVersion ? `v${targetVersion}` : '…'} + + + ) : state === 'not-available' ? ( + <> + {/* HEAD and origin/main are the same commit: one line, no fork. */} + + + origin/main + + + ) : ( + <> + {/* Not known yet (or not reachable): the remote is only a promise. */} + + + origin/main + + + )} + + + {/* HEAD: the app's own mark, sitting on its trunk. */} + {/* Positioned by the wrapper, animated by the tile: rise-in owns the + tile's transform, so it must not also carry the centring translate. */} + + + + + +
+ ) +} + +export default AboutGraph diff --git a/GitWeb/src/renderer/src/components/ActivitySky.tsx b/GitWeb/src/renderer/src/components/ActivitySky.tsx index 6312bb7..1898c68 100644 --- a/GitWeb/src/renderer/src/components/ActivitySky.tsx +++ b/GitWeb/src/renderer/src/components/ActivitySky.tsx @@ -50,6 +50,7 @@ function ActivitySky({ items, needsYou, newStarsSince, activeKey, onActivate, ch const sky = useMemo(() => layoutSky(items, width, HEIGHT), [items, width]) const active = sky.points.find((point) => point.key === activeKey) + // oxlint-disable-next-line react/purity -- the sky reads the clock on every render so ages and the "now" mark stay current const now = Date.now() return ( diff --git a/GitWeb/src/renderer/src/components/AiSettingsForm.tsx b/GitWeb/src/renderer/src/components/AiSettingsForm.tsx index d087f0d..cad4faf 100644 --- a/GitWeb/src/renderer/src/components/AiSettingsForm.tsx +++ b/GitWeb/src/renderer/src/components/AiSettingsForm.tsx @@ -259,13 +259,16 @@ function AiSettingsForm(): JSX.Element | null { }, [settings, loadSettings]) useEffect(() => { + // oxlint-disable-next-line react/set-state-in-effect -- seeds the editable draft once settings arrive from the store if (settings) setDraft((current) => current ?? settings) }, [settings]) + // Keyed on the provider alone: the key reloads when it changes, not on every edit to the draft. + const activeProvider = draft?.activeProvider useEffect(() => { - if (!draft) return - window.api.secrets.get(credentialKey(draft.activeProvider)).then((key) => setApiKeyDraft(key ?? '')) - }, [draft?.activeProvider]) + if (!activeProvider) return + window.api.secrets.get(credentialKey(activeProvider)).then((key) => setApiKeyDraft(key ?? '')) + }, [activeProvider]) if (!draft) return null @@ -439,16 +442,6 @@ function AiSettingsForm(): JSX.Element | null { )} -
- - -
- {testResult &&

{testResult}

} -
@@ -478,6 +471,17 @@ function AiSettingsForm(): JSX.Element | null {
+ + {/* Last, under everything they act on: Save keeps the whole form. */} +
+ + +
+ {testResult &&

{testResult}

} ) } diff --git a/GitWeb/src/renderer/src/components/BenchPulse.tsx b/GitWeb/src/renderer/src/components/BenchPulse.tsx new file mode 100644 index 0000000..68e6d5f --- /dev/null +++ b/GitWeb/src/renderer/src/components/BenchPulse.tsx @@ -0,0 +1,105 @@ +import { useState } from 'react' +import type { Locale } from '../../../shared/i18n' +import { relativeTime } from '../lib/relativeTime' + +/** + * The signed-in user as HEAD: a branch leaves the bottom of their avatar and + * runs right, carrying one node per repository at the age of its last commit — + * near the avatar is now, the far end is WINDOW_DAYS ago (older ones rest + * there, faded). The same gesture as the app's mark and the row lanes, so the + * footer reads as part of the bench rather than as a profile widget. + * + * Monochrome on purpose: hue on this screen means repository state, and age is + * not a state. Recency is carried by opacity and the newest node's size. + */ + +const W = 292 +const H = 30 +const TRUNK = 18 // avatar centre, in the footer's own coordinates +const LANE_Y = 20 +const X0 = 44 +const X1 = W - 58 +const DAY = 86_400_000 +export const WINDOW_DAYS = 60 + +export interface PulsePoint { + name: string + date: string +} + +function ageRatio(iso: string, now: number): number { + const days = Math.max(0, (now - Date.parse(iso)) / DAY) + // sqrt spreads the recent end, where most of the interest is. + return Math.min(1, Math.sqrt(days / WINDOW_DAYS)) +} + +function BenchPulse({ + points, + locale, + windowLabel, + title +}: { + points: PulsePoint[] + locale: Locale + windowLabel: string + title: string +}): JSX.Element | null { + const [now] = useState(Date.now) + const placed = points + .filter((p) => Number.isFinite(Date.parse(p.date))) + .map((p) => ({ ...p, t: ageRatio(p.date, now) })) + // Oldest first, so the newest nodes paint on top where they overlap. + .sort((a, b) => b.t - a.t) + if (placed.length === 0) return null + const newest = placed[placed.length - 1] + + return ( + + {title} + {/* Out of the avatar, bend, and away — the branch gesture. */} + + {/* Past the window: the lane fades out instead of ending on a cap. */} + + {placed.map((p, i) => { + const isNewest = p === newest + return ( + = 1 ? 2 : 2.75} + fill="var(--color-primary-text)" + fillOpacity={isNewest ? 1 : 0.85 - 0.6 * p.t} + stroke="var(--color-obsidian-panel)" + strokeWidth={1.5} + className="lane-node" + style={{ animationDelay: `${240 + p.t * 360}ms` }} + > + {`${p.name} · ${relativeTime(locale, p.date, now)}`} + + ) + })} + + {windowLabel} + + + ) +} + +export default BenchPulse diff --git a/GitWeb/src/renderer/src/components/CloneDestinationField.tsx b/GitWeb/src/renderer/src/components/CloneDestinationField.tsx index 92e144a..a6ae235 100644 --- a/GitWeb/src/renderer/src/components/CloneDestinationField.tsx +++ b/GitWeb/src/renderer/src/components/CloneDestinationField.tsx @@ -37,6 +37,7 @@ function CloneDestinationField({ useEffect(() => { if (!open) return const selectedIndex = baseFolders.findIndex((folder) => pathsEqual(folder, selectedBase)) + // oxlint-disable-next-line react/set-state-in-effect -- re-highlights the chosen folder each time the list opens setHighlighted(Math.max(0, selectedIndex)) }, [open, baseFolders, selectedBase]) diff --git a/GitWeb/src/renderer/src/components/CloneModal.tsx b/GitWeb/src/renderer/src/components/CloneModal.tsx index 7965767..7228a8f 100644 --- a/GitWeb/src/renderer/src/components/CloneModal.tsx +++ b/GitWeb/src/renderer/src/components/CloneModal.tsx @@ -25,6 +25,7 @@ import CloneDestinationField from './CloneDestinationField' import { useCloneProgress } from '../lib/useCloneProgress' import { useFormatters, useLocale, useT } from '../lib/i18n' import { relativeTime } from '../lib/relativeTime' +import SegmentThumb from './SegmentThumb' type CloneTab = 'github' | 'url' | 'token' @@ -611,7 +612,14 @@ function CloneModal({ open, onClose }: CloneModalProps): JSX.Element | null { ] return ( -
+
{ + e.stopPropagation() + if (e.target === e.currentTarget) onClose() + }} + >
e.stopPropagation()} > {/* Header: title, method switch and — on the GitHub tab — whose repos these are. */}
@@ -644,6 +651,7 @@ function CloneModal({ open, onClose }: CloneModalProps): JSX.Element | null {
+ {tabs.map((tab) => ( @@ -137,7 +142,7 @@ function RepoGroupSection({ )} {id !== null && !editing && ( - + )} +
{!collapsed && diff --git a/GitWeb/src/renderer/src/components/RepoInsights.tsx b/GitWeb/src/renderer/src/components/RepoInsights.tsx index 2eb85e8..4ff9045 100644 --- a/GitWeb/src/renderer/src/components/RepoInsights.tsx +++ b/GitWeb/src/renderer/src/components/RepoInsights.tsx @@ -63,6 +63,8 @@ export function ChurnChart({ points, bins }: { points: ChurnPoint[]; bins: Perio const compact = useCompact() const fmt = useFormatters() const [active, setActive] = useState(null) + // Measured on hover, not read from the ref while rendering. + const [wrapWidth, setWrapWidth] = useState(0) const wrapRef = useRef(null) const series = useMemo(() => { @@ -104,7 +106,10 @@ export function ChurnChart({ points, bins }: { points: ChurnPoint[]; bins: Perio
setActive(i)} + onPointerEnter={() => { + setActive(i) + setWrapWidth(wrapRef.current?.clientWidth ?? 0) + }} >
2 && {bins[Math.floor(bins.length / 2)]?.label}} {bins[bins.length - 1]?.label}
- {active !== null && bins[active] && wrapRef.current && ( - + {active !== null && bins[active] && wrapWidth > 0 && ( +

{bins[active].title}

+{fmt.number(series[active].additions)}{' '} diff --git a/GitWeb/src/renderer/src/components/RepoPickerModal.tsx b/GitWeb/src/renderer/src/components/RepoPickerModal.tsx index 7e701ef..d6a2437 100644 --- a/GitWeb/src/renderer/src/components/RepoPickerModal.tsx +++ b/GitWeb/src/renderer/src/components/RepoPickerModal.tsx @@ -20,6 +20,7 @@ function RepoPickerModal({ open, repoPaths, onConfirm, onCancel }: RepoPickerMod useFocusTrap(panelRef, open, { onEscape: onCancel }) useEffect(() => { + // oxlint-disable-next-line react/set-state-in-effect -- preselects every repository found, each time the picker opens if (open) setSelected(new Set(repoPaths)) }, [open, repoPaths]) @@ -37,14 +38,20 @@ function RepoPickerModal({ open, repoPaths, onConfirm, onCancel }: RepoPickerMod const allSelected = selected.size === repoPaths.length return ( -

+
{ + e.stopPropagation() + if (e.target === e.currentTarget) onCancel() + }} + >
e.stopPropagation()} >

{t.reposFound(repoPaths.length)} diff --git a/GitWeb/src/renderer/src/components/SegmentThumb.tsx b/GitWeb/src/renderer/src/components/SegmentThumb.tsx new file mode 100644 index 0000000..8c59612 --- /dev/null +++ b/GitWeb/src/renderer/src/components/SegmentThumb.tsx @@ -0,0 +1,81 @@ +import { useLayoutEffect, useRef } from 'react' + +/** + * The raised pill behind the active option of a `.clone-segmented` control, + * sliding from the old option to the new one instead of the background just + * blinking across. Drop it in as the control's first child. + * + * It finds the active option by its ARIA state (`aria-selected` on tabs, + * `aria-checked` on radios) and writes position and width straight onto its + * own element: no React state, so a click costs one render, and a resize of + * the control (the PR list is draggable) only moves the pill. + * + * It leaves on press, not on release: a click only fires on mouseup, and + * waiting for it made every switch read as "click, then slide". If the press + * ends anywhere but on that option, the pill goes back to the real selection. + */ +function SegmentThumb({ activeKey }: { activeKey: string }): JSX.Element { + const ref = useRef(null) + + useLayoutEffect(() => { + const thumb = ref.current + const control = thumb?.parentElement + if (!thumb || !control) return + // Which option it was last sent to; also what makes a new choice re-place it. + thumb.dataset.option = activeKey + + function moveTo(option: HTMLElement | null): void { + if (!thumb) return + if (!option) { + thumb.style.opacity = '0' + return + } + thumb.style.opacity = '1' + thumb.style.width = `${option.offsetWidth}px` + thumb.style.transform = `translateX(${option.offsetLeft}px)` + } + + function selected(): HTMLElement | null { + return control?.querySelector('[aria-selected="true"], [aria-checked="true"]') ?? null + } + + function place(): void { + moveTo(selected()) + } + + function onPointerDown(e: PointerEvent): void { + if (e.button !== 0) return + const option = (e.target as HTMLElement).closest('.clone-segment') + if (option && !option.matches(':disabled') && control?.contains(option)) moveTo(option) + } + + // A press released elsewhere (or cancelled) never clicks: go back home. + // A real click re-renders first, and its layout effect places the pill + // on the new selection before this frame runs. + function onPointerEnd(): void { + requestAnimationFrame(place) + } + + place() + // First placement lands without motion; only later moves slide. + const frame = requestAnimationFrame(() => { + thumb.dataset.ready = 'true' + }) + const observer = new ResizeObserver(place) + observer.observe(control) + control.addEventListener('pointerdown', onPointerDown) + window.addEventListener('pointerup', onPointerEnd) + window.addEventListener('pointercancel', onPointerEnd) + return () => { + cancelAnimationFrame(frame) + observer.disconnect() + control.removeEventListener('pointerdown', onPointerDown) + window.removeEventListener('pointerup', onPointerEnd) + window.removeEventListener('pointercancel', onPointerEnd) + } + }, [activeKey]) + + return +} + +export default SegmentThumb diff --git a/GitWeb/src/renderer/src/components/SensitiveFilesDialog.tsx b/GitWeb/src/renderer/src/components/SensitiveFilesDialog.tsx index c4b6b44..42d385b 100644 --- a/GitWeb/src/renderer/src/components/SensitiveFilesDialog.tsx +++ b/GitWeb/src/renderer/src/components/SensitiveFilesDialog.tsx @@ -54,7 +54,14 @@ export default function SensitiveFilesDialog({ const Icon = blocked ? ShieldBan : ShieldAlert return createPortal( -
+
{ + e.stopPropagation() + if (e.target === e.currentTarget) onCancel() + }} + >
e.stopPropagation()} >
window.matchMedia('(prefers-color-scheme: dark)').matches) - useEffect(() => { - const query = window.matchMedia('(prefers-color-scheme: dark)') - const sync = (): void => setDark(query.matches) - query.addEventListener('change', sync) - return () => query.removeEventListener('change', sync) - }, []) - return dark -} - -/** - * One click between light and dark. It writes an explicit preference (the - * Settings view still offers "follow the system"), so the choice survives a - * restart. The icon shows where the click takes you: a sun in the dark, a - * moon in the light — and the two swap with a quarter turn. - */ -function ThemeToggle({ className = '' }: { className?: string }): JSX.Element { - const preference = useAppSettingsStore((s) => s.settings?.colorScheme ?? 'system') - const updateSettings = useAppSettingsStore((s) => s.updateSettings) - const systemDark = useSystemDark() - const t = useT().topBar - const dark = preference === 'system' ? systemDark : preference === 'dark' - const label = dark ? t.themeToLight : t.themeToDark - - return ( - - ) -} - -export default ThemeToggle diff --git a/GitWeb/src/renderer/src/components/TopBar.tsx b/GitWeb/src/renderer/src/components/TopBar.tsx index b3bc212..4e0f759 100644 --- a/GitWeb/src/renderer/src/components/TopBar.tsx +++ b/GitWeb/src/renderer/src/components/TopBar.tsx @@ -38,7 +38,6 @@ import Github from './icons/Github' import { openSettings } from '../lib/openAiSettings' import { useT } from '../lib/i18n' import type { Messages } from '../../../shared/i18n' -import ThemeToggle from './ThemeToggle' import PublishRepositoryDialog from './PublishRepositoryDialog' function timeAgo(t: Messages['topBar'], timestamp: number | null): string { @@ -304,7 +303,7 @@ function BranchCard({ compact }: { compact: boolean }): JSX.Element { className="field mt-1.5 w-full py-1.5" /> )} -
+
{visibleLocal.length === 0 && visibleRemote.length === 0 && (

{t.noBranchFound}

)} @@ -505,6 +504,7 @@ function SyncCard({ compact }: { compact: boolean }): JSX.Element { return found }, [currentRepoPath]) useEffect(() => { + // oxlint-disable-next-line react/set-state-in-effect -- asks main over IPC whether an origin exists once the branch has no upstream if (noUpstream) void checkOrigin() }, [noUpstream, checkOrigin]) const needsRepository = noUpstream && !hasOrigin @@ -514,6 +514,7 @@ function SyncCard({ compact }: { compact: boolean }): JSX.Element { useEffect(() => { if (!publishPrompt || !currentRepoPath) return clearPublishPrompt() + // oxlint-disable-next-line react/set-state-in-effect -- opens the publish dialog the start screen asked for, then clears the request setPublishOpen(true) }, [publishPrompt, currentRepoPath, clearPublishPrompt]) // Re-checked on click and when the dialog closes: an origin added outside @@ -786,7 +787,6 @@ function TopBar(): JSX.Element {
- diff --git a/GitWeb/src/renderer/src/components/UserSearchField.tsx b/GitWeb/src/renderer/src/components/UserSearchField.tsx index 331555f..6d31869 100644 --- a/GitWeb/src/renderer/src/components/UserSearchField.tsx +++ b/GitWeb/src/renderer/src/components/UserSearchField.tsx @@ -46,6 +46,7 @@ function UserSearchField({ id, host, value, onChange, statusOf, invalid, describ const showList = open && searchable useEffect(() => { + // oxlint-disable-next-line react/set-state-in-effect -- resets results as the query changes; the search itself runs over IPC setHighlighted(-1) if (!open || !searchable) { setResults([]) diff --git a/GitWeb/src/renderer/src/env.d.ts b/GitWeb/src/renderer/src/env.d.ts index 75d5bb8..cc4fd45 100644 --- a/GitWeb/src/renderer/src/env.d.ts +++ b/GitWeb/src/renderer/src/env.d.ts @@ -1,3 +1,5 @@ /// declare const __BUILD_TIME__: string +/** Short hash of the commit the build came from; '' when built outside a git checkout. */ +declare const __BUILD_COMMIT__: string diff --git a/GitWeb/src/renderer/src/global-jsx.d.ts b/GitWeb/src/renderer/src/global-jsx.d.ts index ecc466a..8567d1d 100644 --- a/GitWeb/src/renderer/src/global-jsx.d.ts +++ b/GitWeb/src/renderer/src/global-jsx.d.ts @@ -15,4 +15,6 @@ declare global { } } +// Required: it makes this file a module, so `declare global` is allowed. +// oxlint-disable-next-line unicorn/require-module-specifiers export {} diff --git a/GitWeb/src/renderer/src/lib/ai/commitInvestigation.ts b/GitWeb/src/renderer/src/lib/ai/commitInvestigation.ts index aefd458..c85b663 100644 --- a/GitWeb/src/renderer/src/lib/ai/commitInvestigation.ts +++ b/GitWeb/src/renderer/src/lib/ai/commitInvestigation.ts @@ -462,7 +462,7 @@ export async function investigateCommitWithWorkers(input: CommitInvestigationInp ) const chunk = chunks[index] findings.push(...parseFindings(response.content).map((finding) => ({ ...finding, chunk }))) - } catch (error) { + } catch { if (input.isCancelled()) return { path: file.path, coverage: 'partial', findings } coverage = 'partial' } finally { diff --git a/GitWeb/src/renderer/src/lib/ai/proposeFix.ts b/GitWeb/src/renderer/src/lib/ai/proposeFix.ts index cc89d91..0abb5b7 100644 --- a/GitWeb/src/renderer/src/lib/ai/proposeFix.ts +++ b/GitWeb/src/renderer/src/lib/ai/proposeFix.ts @@ -5,7 +5,6 @@ import { proposeFixUserMessage, type AiLanguage } from '../../../../shared/aiPrompts' -import type { AiSettings } from '../../../../shared/ai' import { sendChatRequest } from './providers' import { unifiedPreview } from '../../../../shared/patch' import type { ResolvedAiConfig } from '../../../../shared/ai' diff --git a/GitWeb/src/renderer/src/lib/ai/tools.ts b/GitWeb/src/renderer/src/lib/ai/tools.ts index 80c5468..d3b0b39 100644 --- a/GitWeb/src/renderer/src/lib/ai/tools.ts +++ b/GitWeb/src/renderer/src/lib/ai/tools.ts @@ -1248,7 +1248,7 @@ async function prepareToolCallInner(call: ToolCallRequest): Promise c.hash)) if (!validation.ok) throw new Error(`${describeRebaseProblem(validation.problem)}\n\n${listing}`) @@ -1585,7 +1585,8 @@ export async function executeToolCall(call: ToolCallRequest): Promise 0 ? t.pathMissingSimilar(a.path, formatFileList(similar, 20)) - : t.pathMissing(a.path) + : t.pathMissing(a.path), + { cause: err } ) } throw err diff --git a/GitWeb/src/renderer/src/lib/branchName.ts b/GitWeb/src/renderer/src/lib/branchName.ts index f27172e..d680e04 100644 --- a/GitWeb/src/renderer/src/lib/branchName.ts +++ b/GitWeb/src/renderer/src/lib/branchName.ts @@ -14,6 +14,7 @@ export function validateBranchName(name: string, existing: string[] = []): strin const t = getT().stores if (name.startsWith('-')) return t.branchStartsWithDash if (name === '@') return t.branchIsAt + // oxlint-disable-next-line no-control-regex -- git refuses control characters in ref names if (/[~^:?*[\\\x00-\x1f\x7f]/.test(name)) return t.branchControlChars if (name.includes('..')) return t.branchDoubleDot if (name.includes('@{')) return t.branchAtBrace diff --git a/GitWeb/src/renderer/src/lib/disintegrate.ts b/GitWeb/src/renderer/src/lib/disintegrate.ts index bc54670..89521b8 100644 --- a/GitWeb/src/renderer/src/lib/disintegrate.ts +++ b/GitWeb/src/renderer/src/lib/disintegrate.ts @@ -46,8 +46,8 @@ export async function captureDust(rect: DOMRect, options?: DustOptions): Promise function loadImage(src: string): Promise { return new Promise((resolve, reject) => { const image = new Image() - image.onload = () => resolve(image) - image.onerror = () => reject(new Error('Falha ao carregar a captura.')) + image.addEventListener('load', () => resolve(image), { once: true }) + image.addEventListener('error', () => reject(new Error('Falha ao carregar a captura.')), { once: true }) image.src = src }) } diff --git a/GitWeb/src/renderer/src/lib/useCloneProgress.ts b/GitWeb/src/renderer/src/lib/useCloneProgress.ts index 662b2c8..31e4fcf 100644 --- a/GitWeb/src/renderer/src/lib/useCloneProgress.ts +++ b/GitWeb/src/renderer/src/lib/useCloneProgress.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useLayoutEffect, useRef, useState } from 'react' import type { CloneProgress } from '../../../shared/git' interface CloneProgressState { @@ -21,10 +21,12 @@ interface CloneProgressState { export function useCloneProgress(destination: string | null): CloneProgressState { const [progress, setProgress] = useState(null) const [stalledFor, setStalledFor] = useState(0) - const lastEventAt = useRef(Date.now()) + const lastEventAt = useRef(0) // Read inside the listener so the subscription survives destination changes. const watched = useRef(destination) - watched.current = destination + useLayoutEffect(() => { + watched.current = destination + }) useEffect(() => { return window.api.git.onCloneProgress((next) => { diff --git a/GitWeb/src/renderer/src/lib/useFocusTrap.ts b/GitWeb/src/renderer/src/lib/useFocusTrap.ts index 2fe8504..81a2a93 100644 --- a/GitWeb/src/renderer/src/lib/useFocusTrap.ts +++ b/GitWeb/src/renderer/src/lib/useFocusTrap.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react' +import { useEffect, useLayoutEffect, useRef } from 'react' import type { RefObject } from 'react' const FOCUSABLE = @@ -28,7 +28,9 @@ export function useFocusTrap( // keeps the effect from re-running mid-dialog and yanking focus back to the // initial element. const onEscapeRef = useRef(onEscape) - onEscapeRef.current = onEscape + useLayoutEffect(() => { + onEscapeRef.current = onEscape + }) useEffect(() => { if (!active) return diff --git a/GitWeb/src/renderer/src/store/aiStore.ts b/GitWeb/src/renderer/src/store/aiStore.ts index d6574c3..5946ba0 100644 --- a/GitWeb/src/renderer/src/store/aiStore.ts +++ b/GitWeb/src/renderer/src/store/aiStore.ts @@ -713,7 +713,6 @@ export const useAiStore = create((set, get) => ({ applyPendingFix: async () => { const pending = get().pendingFix const repoPath = useRepoStore.getState().currentRepoPath - const { settings, apiKey } = get() if (!pending || pending.status !== 'ready' || !repoPath) return const language = getLocale() set({ pendingFix: { ...pending, status: 'applying' }, isBusy: true, error: null }) diff --git a/GitWeb/src/renderer/src/store/repoStore.ts b/GitWeb/src/renderer/src/store/repoStore.ts index 02082e1..f1b2975 100644 --- a/GitWeb/src/renderer/src/store/repoStore.ts +++ b/GitWeb/src/renderer/src/store/repoStore.ts @@ -612,7 +612,6 @@ export const useRepoStore = create((set, get) => ({ window.api.git.remoteBranches(path).catch(() => [] as string[]) ]) if (generation !== repoOpenGeneration || get().currentRepoPath !== path) return - const local = new Set(branches.map((b) => b.name)) set({ branches, remoteBranches: remoteBranchesToOffer(remote, branches.map((b) => b.name)) }) } catch (err) { if (generation !== repoOpenGeneration || get().currentRepoPath !== path) return diff --git a/GitWeb/src/renderer/src/styles/global.css b/GitWeb/src/renderer/src/styles/global.css index fcde006..8ed4f63 100644 --- a/GitWeb/src/renderer/src/styles/global.css +++ b/GitWeb/src/renderer/src/styles/global.css @@ -360,6 +360,16 @@ — the investigation's active filename, and the chat's own busy labels. The surrounding row stays stable, so the motion signals work without making the whole progress line visually noisy. */ +@keyframes agent-draft-ripple { + 0%, + 100% { + transform: scaleY(1); + } + 45% { + transform: scaleY(0.35); + } +} + @keyframes agent-busy-shimmer { 0% { background-position: 160% 0; @@ -537,6 +547,30 @@ } } +/* New PR wizard: the step that just became current sends one ring out. */ +@keyframes wizard-node-ping { + 0% { + box-shadow: + 0 0 0 4px var(--color-command-blue-soft), + 0 0 0 0 color-mix(in oklab, var(--color-command-blue) 45%, transparent); + } + 100% { + box-shadow: + 0 0 0 4px var(--color-command-blue-soft), + 0 0 0 14px transparent; + } +} +@keyframes wizard-card-in { + 0% { + opacity: 0; + transform: translateY(8px) scale(0.985); + } + 100% { + opacity: 1; + transform: none; + } +} + @keyframes wizard-field-flash { 0% { box-shadow: 0 0 0 3px var(--color-command-blue-soft); @@ -977,6 +1011,59 @@ and the sparkle turns, so the AI entry point feels alive without competing with the primary action. While generating, the label uses the agent shimmer instead. */ + /* "Redigir pelo diff": the PR draft button. Its mark is a little diff stat + (green adds, red deletes) in a recessed well — what the agent reads — + instead of the generic AI sparkle. Hover ripples the bars once around; + while the agent reads, they keep rippling. */ + .agent-draft { + @apply inline-flex h-8 items-center gap-2 rounded-button border border-default-border bg-coal-raised pl-1 pr-3 text-caption font-medium text-primary-text active:scale-[0.97]; + box-shadow: var(--shadow-raised); + transition: + border-color 180ms ease-out, + background-color 180ms ease-out, + transform 150ms var(--ease-out-expo); + } + .agent-draft:hover:not(:disabled) { + border-color: color-mix(in oklab, var(--color-command-blue) 45%, transparent); + background-color: color-mix(in oklab, var(--color-command-blue) 8%, var(--color-coal-raised)); + } + .agent-draft:disabled { + cursor: progress; + } + .agent-draft-glyph { + @apply flex h-6 w-9 items-end justify-center gap-[2px] rounded-md border border-default-border bg-midnight-base px-1.5 pb-[5px]; + } + .agent-draft-glyph > svg { + align-self: center; + } + .agent-draft-glyph > i { + width: 3px; + border-radius: 1px; + transform-origin: bottom; + } + .agent-draft-glyph > i.is-add { + background: var(--color-diff-add-ink); + } + .agent-draft-glyph > i.is-del { + background: var(--color-danger-red); + } + .agent-draft:hover:not(:disabled) .agent-draft-glyph > i { + animation: agent-draft-ripple 560ms var(--ease-out-expo) 1 backwards; + } + .agent-draft.is-reading .agent-draft-glyph { + border-color: color-mix(in oklab, var(--color-command-blue) 45%, transparent); + } + .agent-draft.is-reading .agent-draft-glyph > i { + animation: agent-draft-ripple 900ms ease-in-out infinite; + } + @media (prefers-reduced-motion: reduce) { + .agent-draft-glyph > i, + .agent-draft:hover:not(:disabled) .agent-draft-glyph > i, + .agent-draft.is-reading .agent-draft-glyph > i { + animation: none; + } + } + .btn-agent { @apply relative inline-flex h-7 items-center gap-1.5 overflow-hidden rounded-button border border-command-blue/30 bg-command-blue-soft px-3 text-caption font-medium text-command-blue active:scale-[0.97]; box-shadow: var(--shadow-raised); @@ -1103,6 +1190,51 @@ /* Empty-state medallion: one icon on a raised tile — used for every "nothing here yet" state so they share a single voice. */ + .wizard-node-active { + animation: wizard-node-ping 900ms var(--ease-out-expo) 1; + } + /* The rail's leading edge glows while it grows, then rests. */ + .wizard-rail-fill { + box-shadow: 0 0 8px color-mix(in oklab, var(--color-command-blue) 60%, transparent); + } + .wizard-card { + animation: wizard-card-in 360ms var(--ease-out-expo) backwards; + transition: + background-color 180ms ease-out, + border-color 180ms ease-out, + box-shadow 220ms var(--ease-out-expo), + transform 220ms var(--ease-out-expo); + } + .wizard-card:not([data-selected='true']):not([data-disabled='true']):hover { + transform: translateY(-2px); + box-shadow: var(--shadow-raised); + } + .wizard-card[data-selected='true'] { + box-shadow: + 0 0 0 3px var(--color-command-blue-soft), + var(--shadow-raised); + } + .wizard-card .empty-medallion { + transition: + color 180ms ease-out, + border-color 180ms ease-out, + background-color 180ms ease-out; + } + .wizard-card[data-selected='true'] .empty-medallion { + color: var(--color-command-blue); + border-color: color-mix(in oklab, var(--color-command-blue) 40%, transparent); + background-color: var(--color-command-blue-soft); + } + @media (prefers-reduced-motion: reduce) { + .wizard-node-active, + .wizard-card { + animation: none; + } + .wizard-card:hover { + transform: none; + } + } + .empty-medallion { @apply flex size-11 items-center justify-center rounded-md border border-default-border bg-coal-raised text-secondary-text; box-shadow: var(--shadow-raised); @@ -1396,6 +1528,47 @@ background-color: var(--color-obsidian-panel); border-color: var(--color-default-border); } + /* Same leading pill as .list-row, so the start screen's rows answer the + pointer in the vocabulary every other list in the app already uses. */ + .workbench-row::before { + content: ''; + position: absolute; + left: -1px; + top: 50%; + width: 3px; + height: 46%; + max-height: 24px; + border-radius: 99px; + background-color: var(--color-primary-text); + transform: translateY(-50%) scaleY(0); + opacity: 0; + transition: + transform 220ms var(--ease-out-expo), + opacity 150ms ease-out; + pointer-events: none; + } + .workbench-row:hover::before, + .workbench-row:focus-within::before { + opacity: 0.22; + transform: translateY(-50%) scaleY(1); + } + /* Hover actions float over the row's tail instead of reserving an empty + column for themselves; the fade lets the text under them recede. */ + .workbench-row-actions { + background: linear-gradient(to right, transparent, var(--color-obsidian-panel) 28px); + } + /* First paint: the list settles in a short cascade, once per mount. */ + .workbench-row-enter { + animation: rise-in 360ms var(--ease-out-expo) backwards; + } + @media (prefers-reduced-motion: reduce) { + .workbench-row-enter { + animation: none; + } + .workbench-row::before { + transition: none; + } + } [data-theme='light'] .workbench-row:hover, [data-theme='light'] .workbench-row:focus-within, [data-theme='light-hc'] .workbench-row:hover, @@ -1447,6 +1620,8 @@ } .clone-segmented { + position: relative; + isolation: isolate; display: inline-flex; gap: 2px; padding: 3px; @@ -1455,6 +1630,8 @@ background: var(--color-midnight-base); } .clone-segment { + position: relative; + z-index: 1; display: inline-flex; align-items: center; gap: 6px; @@ -1469,17 +1646,47 @@ color 150ms var(--ease-out-expo), box-shadow 150ms var(--ease-out-expo); } + /* Hover only lifts the label: a hover surface under the pointer read as a + second selection arriving before the sliding thumb did. */ .clone-segment:hover { color: var(--color-primary-text); - background: var(--color-hover-overlay); } - .clone-segment[aria-selected='true'] { + /* The active option's surface is the sliding thumb below, so the option + itself only brightens its label. */ + .clone-segment[aria-selected='true'], + .clone-segment[aria-checked='true'], + .clone-segment[aria-selected='true']:hover, + .clone-segment[aria-checked='true']:hover { color: var(--color-primary-text); + background: transparent; + } + .clone-segment[aria-selected='true'] svg, + .clone-segment[aria-checked='true'] svg { + color: var(--color-command-blue); + } + .clone-segment-thumb { + position: absolute; + top: 3px; + bottom: 3px; + left: 0; + z-index: 0; + width: 0; + border-radius: 6px; background: var(--color-obsidian-panel); box-shadow: var(--shadow-raised); + opacity: 0; + pointer-events: none; } - .clone-segment[aria-selected='true'] svg { - color: var(--color-command-blue); + .clone-segment-thumb[data-ready='true'] { + transition: + transform 280ms cubic-bezier(0.32, 0.72, 0, 1), + width 280ms cubic-bezier(0.32, 0.72, 0, 1), + opacity 150ms ease-out; + } + @media (prefers-reduced-motion: reduce) { + .clone-segment-thumb[data-ready='true'] { + transition: none; + } } .clone-monogram { @@ -2151,6 +2358,40 @@ /* ── Settings: grouped rows, choice tiles, theme miniatures ──────────────── */ @layer components { + /* Collaborators: rows settle in, warm under the pointer, and keep their + destructive action out of sight until the row is pointed at or focused. */ + .collab-row { + animation: rise-in 360ms var(--ease-out-expo) backwards; + transition: background-color 150ms ease-out; + } + .collab-row:hover, + .collab-row:focus-within { + background-color: var(--color-hover-overlay); + } + /* The people list keeps overflow visible (for the role menus), so the + hover fill rounds its own outer corners to stay inside the card. */ + .collab-row:first-child { + border-top-left-radius: calc(var(--radius-lg) - 1px); + border-top-right-radius: calc(var(--radius-lg) - 1px); + } + .collab-row:last-child { + border-bottom-left-radius: calc(var(--radius-lg) - 1px); + border-bottom-right-radius: calc(var(--radius-lg) - 1px); + } + .collab-row .collab-action { + opacity: 0; + transition: opacity 150ms ease-out; + } + .collab-row:hover .collab-action, + .collab-row:focus-within .collab-action { + opacity: 1; + } + @media (prefers-reduced-motion: reduce) { + .collab-row { + animation: none; + } + } + .settings-heading { display: flex; align-items: center; diff --git a/GitWeb/src/renderer/src/views/AboutView.tsx b/GitWeb/src/renderer/src/views/AboutView.tsx index 058e055..5d10820 100644 --- a/GitWeb/src/renderer/src/views/AboutView.tsx +++ b/GitWeb/src/renderer/src/views/AboutView.tsx @@ -1,13 +1,17 @@ import { useEffect, useState, type CSSProperties } from 'react' -import { AlertCircle, ArrowLeft, CheckCircle2, Download, FolderGit2, Loader2, RefreshCw, RotateCcw } from 'lucide-react' +import { AlertCircle, ArrowLeft, Bug, CheckCircle2, Download, Loader2, RefreshCw, RotateCcw, Scale, Tag } from 'lucide-react' import { useRepoStore } from '../store/repoStore' import Markdown from '../components/Markdown' +import AboutGraph from '../components/AboutGraph' +import Github from '../components/icons/Github' import type { UpdateStatus } from '../../../shared/updates' import { allTranslations, type Locale, type Messages } from '../../../shared/i18n' import { useLocale, useT } from '../lib/i18n' type AboutMessages = Messages['about'] +const REPO_URL = 'https://github.com/CariSoftwareBR/OpenGitDesktop' + function formatBuildTime(locale: Locale, iso: string): string { const date = new Date(iso) if (Number.isNaN(date.getTime())) return iso @@ -126,6 +130,7 @@ function AboutView(): JSX.Element { (updateStatus.state === 'downloading' && updateStatus.version) ) { const next = { version: updateStatus.version ?? '', notes: updateStatus.releaseNotes ?? null } + // oxlint-disable-next-line react/set-state-in-effect -- remembers the incoming version and notes across update events from main setTarget((prev) => (prev?.version === next.version && prev.notes === next.notes ? prev : next)) } }, [updateStatus]) @@ -161,9 +166,20 @@ function AboutView(): JSX.Element { : 0 const notes = inFlow ? (target?.notes ?? null) : null + // What each update action would be, said in git — a footnote, never the label. + const gitHint = state === 'downloaded' ? 'git merge --ff-only' : downloading ? 'git pull' : 'git fetch' + const remoteRef = + state === 'downloaded' + ? { text: `origin/main · v${target?.version ?? ''}`, tone: 'border-success-green/25 bg-success-green-soft text-success-green' } + : downloading + ? { text: `origin/main · v${target?.version ?? '…'}`, tone: 'border-attention-orange/25 bg-attention-orange-soft text-attention-orange' } + : state === 'not-available' + ? { text: 'origin/main', tone: 'border-default-border bg-coal-raised text-secondary-text' } + : null + return (
-
+
)} + +
diff --git a/GitWeb/src/renderer/src/views/AiChatView.tsx b/GitWeb/src/renderer/src/views/AiChatView.tsx index 3f2a071..6b2961c 100644 --- a/GitWeb/src/renderer/src/views/AiChatView.tsx +++ b/GitWeb/src/renderer/src/views/AiChatView.tsx @@ -228,13 +228,13 @@ function ToolCallCard({ messageId, uiCall }: { messageId: string; uiCall: import ) } -function ChatAvatar({ role }: { role: 'user' | 'assistant' }): JSX.Element { +function ChatAvatar({ speaker }: { speaker: 'user' | 'assistant' }): JSX.Element { const avatarUrl = useGithubAuthStore((s) => s.user?.avatarUrl) // A signed-in user sees their own GitHub photo here, the same identity the // top bar shows; the generic icon stays the fallback for signed-out sessions // and for a photo that fails to load. const [avatarFailed, setAvatarFailed] = useState(false) - const isUser = role === 'user' + const isUser = speaker === 'user' if (isUser && avatarUrl && !avatarFailed) { return ( @@ -266,7 +266,7 @@ function ThinkingBubble(): JSX.Element { const ui = useAiUi() return (
- +
{ui.chatThinking} @@ -367,7 +367,9 @@ function AiChatView(): JSX.Element { return () => cancelAnimationFrame(frame) } + // oxlint-disable-next-line react/exhaustive-effect-dependencies -- runs when the ghost appears; dustAndSwap is a per-render helper, not a trigger useEffect(() => (ghost ? dustAndSwap(() => setGhost(null)) : undefined), [ghost]) + // oxlint-disable-next-line react/exhaustive-effect-dependencies -- runs when the welcome dusts off; dustAndSwap is a per-render helper, not a trigger useEffect(() => (welcomeDusting ? dustAndSwap(() => setWelcomeDusting(false)) : undefined), [welcomeDusting]) // Only real approval gates count here. Read-only tools run without asking @@ -407,6 +409,7 @@ function AiChatView(): JSX.Element { } } bottomRef.current?.scrollIntoView({ behavior: 'auto' }) + // oxlint-disable-next-line react/exhaustive-effect-dependencies -- these are the triggers for following the conversation to its end }, [messages, phase, lastMessage]) function handleChatScroll(): void { @@ -664,7 +667,7 @@ function AiChatView(): JSX.Element { // full column) so the group card lines up with the replies // and tool cards above and below it.
- +
- +
(null) const triggerRef = useRef(null) const openRef = useRef(false) - openRef.current = open + useLayoutEffect(() => { + openRef.current = open + }) const close = useCallback(() => { if (!openRef.current) return setOpen(false) @@ -713,7 +715,7 @@ function ChangesView(): JSX.Element { if (!first) return const isStaged = staged.some((e) => e.path === first.path) && !conflicted.some((e) => e.path === first.path) void selectFile(first.path, isStaged) - }, [narrow, totalCount, selectedFile, conflicted, staged, unstaged, selectFile]) + }, [narrow, totalCount, selectedFile, conflicted, staged, unstaged, selectFile, clearSelectedFile]) useEffect(() => { if (!shouldVirtualize) return @@ -1096,10 +1098,11 @@ function ChangesView(): JSX.Element { {conflicted.map((entry) => { const flatIdx = flatEntries.findIndex((f) => f.kind === 'conflict' && f.entry.path === entry.path) return ( -
  • void openFileContextMenu(e, entry)}> +
  • + {shownFieldError ? ( + + ) : ( +

    + {t.addHint} +

    + )} +
  • + + + ) : ( +
    +
    + + + +

    {t.readOnly}

    +
    +
    + ) + return (
    -
    -
    -
    -

    +
    +
    +
    +

    + {t.title} {data && data.collaborators.length > 0 && {data.collaborators.length}}

    -

    {repoName}

    +
    + + + {repoName} + + {roleCounts.length > 0 && ( + + {roleCounts.map(({ role, count }) => { + const Icon = ROLE_ICONS[role] + return ( + + + {count} + {t.roles[role]} + + ) + })} + + )} +
    +
    +
    + + + {t.manageOnGithub} + +
    -
    {loadError ? ( @@ -144,159 +294,106 @@ function CollaboratorsPanel({ remote, user }: { remote: RemoteInfo; user: GitHub

    {t.noAccess}

    ) : ( - <> - {data.canAdminister ? ( -
    -

    - {t.addTitle} -

    - {/* Visible overflow: the group would clip the permission list otherwise. */} -
    void handleAdd(event)} className="settings-group overflow-visible"> -
    -
    -
    - - { - setUsername(value) - if (shownFieldError) clearFieldError() - }} - statusOf={accessStatus} - placeholder={t.usernamePlaceholder} - invalid={Boolean(shownFieldError)} - describedBy={`${baseId}-hint`} - /> -
    - {isOrg && ( -
    -

    {t.roleLabel}

    - setNewRole(value as RepositoryRole)} - options={roleOptions()} - ariaLabel={t.roleLabel} - disabled={isSaving} - className="mt-1.5" - /> -
    - )} - -
    - {shownFieldError ? ( - - ) : ( -

    - {t.addHint} -

    - )} -
    -
    -
    - ) : ( -
    -
    - - - -

    {t.readOnly}

    -
    -
    - )} + // Wide: people on the left, the invite card held beside them. Narrow: + // one column, inviting first. +
    + - {data.invitationsError && ( -

    - - {t.invitationsFailed(data.invitationsError)} -

    - )} +
    + {data.invitationsError && ( +

    + + {t.invitationsFailed(data.invitationsError)} +

    + )} - {data.invitations.length > 0 && ( -
    -

    - {t.pendingTitle} - {data.invitations.length} -

    -
      - {data.invitations.map((invitation) => { - const name = invitation.login ?? t.unknownInvitee - return ( -
    • - -
      -

      {name}

      -

      - {roleLabel(invitation.role)} · {t.invitedAt(relativeTime(locale, invitation.createdAt))} -

      -
      -
      - - - {invitation.expired ? t.expiredChip : t.pendingChip} - - -
      -
    • - ) - })} -
    -
    - )} + {data.invitations.length > 0 && ( +
    +

    + + {t.pendingTitle} + {data.invitations.length} +

    +
      + {data.invitations.map((invitation, index) => { + const name = invitation.login ?? t.unknownInvitee + return ( +
    • + +
      +

      {name}

      +

      {t.invitedAt(relativeTime(locale, invitation.createdAt))}

      +
      +
      + + + + {invitation.expired ? t.expiredChip : t.pendingChip} + + +
      +
    • + ) + })} +
    +
    + )} -
    -

    - {t.peopleTitle} -

    - {data.collaborators.length === 0 ? ( - } title={t.empty} /> - ) : ( -
      - {data.collaborators.map((collaborator) => { - const { login, role, direct } = collaborator - const manageable = canManageCollaborator(collaborator, { - canAdminister: data.canAdminister, - owner: data.ownerLogin, - viewer: user.login - }) - const roleEditable = manageable && isOrg - const details = [roleEditable ? null : roleLabel(role), isOrg ? (direct ? t.direct : t.inherited) : null] - .filter(Boolean) - .join(' · ') - return ( -
    • - -
      -

      - {login} - {login.toLowerCase() === user.login.toLowerCase() && {t.you}} - {login.toLowerCase() === data.ownerLogin.toLowerCase() && {t.owner}} -

      -

      - {details} -

      -
      - {manageable && ( +
      +

      + + {t.peopleTitle} +

      + {data.collaborators.length === 0 ? ( + } title={t.empty} /> + ) : ( +
        + {data.collaborators.map((collaborator, index) => { + const { login, role, direct } = collaborator + const manageable = canManageCollaborator(collaborator, { + canAdminister: data.canAdminister, + owner: data.ownerLogin, + viewer: user.login + }) + const roleEditable = manageable && isOrg + return ( +
      • + +
        +

        + {login} + {login.toLowerCase() === user.login.toLowerCase() && {t.you}} + {login.toLowerCase() === data.ownerLogin.toLowerCase() && {t.owner}} +

        + {isOrg && ( +

        + {direct ? : } + {direct ? t.direct : t.inherited} +

        + )} +
        - {roleEditable && ( + {roleEditable ? ( { @@ -307,26 +404,30 @@ function CollaboratorsPanel({ remote, user }: { remote: RemoteInfo; user: GitHub disabled={isSaving} className="w-36" /> + ) : ( + + )} + {manageable && ( + )} -
        - )} -
      • - ) - })} -
      - )} -
      - +
    • + ) + })} +
    + )} +
    +
    +
    )}
    diff --git a/GitWeb/src/renderer/src/views/DashboardView.tsx b/GitWeb/src/renderer/src/views/DashboardView.tsx index a786559..ef85e76 100644 --- a/GitWeb/src/renderer/src/views/DashboardView.tsx +++ b/GitWeb/src/renderer/src/views/DashboardView.tsx @@ -175,6 +175,8 @@ function buildBins(t: DashboardMessages, times: number[], period: Period, now: n if ((end.getFullYear() - cursor.getFullYear()) * 12 + end.getMonth() - cursor.getMonth() < 2) { cursor.setMonth(cursor.getMonth() - 1) } + // cursor advances through setMonth() at the bottom, which the rule can't see. + // oxlint-disable-next-line no-unmodified-loop-condition while (cursor < end) { const start = cursor.getTime() const next = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1) @@ -497,11 +499,15 @@ function ActivityChart({ bins }: { bins: Bin[] }): JSX.Element { return (
    {width > 0 && ( + // Explorable chart: focus it and the arrow keys move the readout. Still + // announced as one picture with its summary, hence role="img". + // oxlint-disable-next-line jsx-a11y/no-noninteractive-element-interactions pick(e.clientX, e.currentTarget.getBoundingClientRect())} @@ -846,10 +852,12 @@ function DashboardView(): JSX.Element { useEffect(() => { const cached = cache && cache.path === currentRepoPath ? cache : null + // oxlint-disable-next-line react/set-state-in-effect -- shows the cached data for this repository at once, then refreshes over IPC setData(cached ? cached.data : null) if (!cached || Date.now() - cached.at > CACHE_TTL) void load() }, [load, currentRepoPath]) + // oxlint-disable-next-line react/memo-dependencies, react/purity -- "now" is re-read only when new data arrives, so period stats hold still between loads const now = useMemo(() => Date.now(), [data]) // eslint-disable-line react-hooks/exhaustive-deps const stats = useMemo( () => (data ? computeStats(t, data.commits, period, now, data.commits.length >= LOG_LIMIT) : null), @@ -863,6 +871,7 @@ function DashboardView(): JSX.Element { const [churn, setChurn] = useState(() => churnCache.get(churnKey) ?? null) useEffect(() => { if (!currentRepoPath || !data) return + // oxlint-disable-next-line react/set-state-in-effect -- shows the cached churn for this period at once, then refreshes over IPC setChurn(churnCache.get(churnKey) ?? null) let alive = true window.api.git diff --git a/GitWeb/src/renderer/src/views/HistoryView.tsx b/GitWeb/src/renderer/src/views/HistoryView.tsx index 5dfacb5..bd6f088 100644 --- a/GitWeb/src/renderer/src/views/HistoryView.tsx +++ b/GitWeb/src/renderer/src/views/HistoryView.tsx @@ -224,6 +224,7 @@ function ContainedIn({ hash }: { hash: string }): JSX.Element { useEffect(() => { if (!currentRepoPath) return let cancelled = false + // oxlint-disable-next-line react/set-state-in-effect -- drops the previous commit's refs while the new ones load over IPC setRefs(null) window.api.git .refsContaining(currentRepoPath, hash) @@ -327,6 +328,7 @@ function HistoryView(): JSX.Element { // A focus request means "show me this commit in the graph": a search would hide it. useEffect(() => { + // oxlint-disable-next-line react/set-state-in-effect -- a focus request clears the search so the focused commit shows in the graph if (historyFocus) setQuery('') }, [historyFocus]) @@ -403,14 +405,15 @@ function HistoryView(): JSX.Element { return count }, [graph, log]) - const [page, setPage] = useState(1) - const pageCount = Math.max(1, Math.ceil(filteredLog.length / HISTORY_PAGE_SIZE)) - useEffect(() => { + const [requestedPage, setPage] = useState(1) + const pageKey = `${query}\0${logPath ?? ''}` + const [pageKeyShown, setPageKeyShown] = useState(pageKey) + if (pageKeyShown !== pageKey) { + setPageKeyShown(pageKey) setPage(1) - }, [query, logPath]) - useEffect(() => { - setPage((p) => Math.min(p, pageCount)) - }, [pageCount]) + } + const pageCount = Math.max(1, Math.ceil(filteredLog.length / HISTORY_PAGE_SIZE)) + const page = Math.min(requestedPage, pageCount) // The graph is one continuous, virtualized canvas; paging would cut its lanes. const visibleLog = graphMode ? filteredLog : filteredLog.slice((page - 1) * HISTORY_PAGE_SIZE, page * HISTORY_PAGE_SIZE) diff --git a/GitWeb/src/renderer/src/views/InitialView.tsx b/GitWeb/src/renderer/src/views/InitialView.tsx index 8097d2b..770c9c8 100644 --- a/GitWeb/src/renderer/src/views/InitialView.tsx +++ b/GitWeb/src/renderer/src/views/InitialView.tsx @@ -12,6 +12,7 @@ import { Folder, FolderInput, GitBranch, + GitCommitHorizontal, Pencil, CloudOff, DownloadCloud, @@ -44,9 +45,10 @@ import type { RepoUpdateStatus } from '../../../shared/git' import type { Messages } from '../../../shared/i18n' -import { useT } from '../lib/i18n' -import ThemeToggle from '../components/ThemeToggle' +import { useLocale, useT } from '../lib/i18n' +import { relativeTime } from '../lib/relativeTime' import RepoLane, { LaneMark } from '../components/RepoLane' +import BenchPulse, { WINDOW_DAYS } from '../components/BenchPulse' type InitialMessages = Messages['initial'] @@ -77,6 +79,17 @@ function FetchCheckBadge({ check }: { check?: RepoUpdateStatus }): JSX.Element | } } +/** The header summary's dot colours, for one repo: the same legend, reused. */ +function railTone(check?: RepoUpdateStatus): string { + if (!check || check.state === 'checking') return 'bg-tertiary-text/50' + if (check.state === 'failed') return 'bg-danger-red' + if (check.state === 'diverged') return 'bg-warning-amber' + if (check.state === 'behind') return 'bg-attention-orange' + if (check.localChanges > 0) return 'bg-command-blue' + if (check.state === 'upToDate') return 'bg-success-green' + return 'bg-tertiary-text/50' +} + interface StatusChip { icon: LucideIcon value: string @@ -197,6 +210,7 @@ function MoveToGroupMenu({ ref={ref} className="absolute right-2 top-10 z-20 min-w-44 rounded-md border border-default-border bg-obsidian-panel p-1 shadow-floating" role="menu" + tabIndex={-1} onKeyDown={(e) => e.key === 'Escape' && onClose()} > {options.map((option) => ( @@ -253,6 +267,7 @@ function InitialView(): JSX.Element { const setView = useRepoStore((s) => s.setView) const messages = useT() const t = messages.initial + const locale = useLocale() // Single source of truth for "is the user signed in to GitHub" — shared // with CloneModal's GitHub tab, so cloning reflects an existing login @@ -408,14 +423,18 @@ function InitialView(): JSX.Element { return { behind, diverged, changes, ok, failed, checking } })() - function renderRepoCard(repo: RecentRepository): JSX.Element { + function renderRepoCard(repo: RecentRepository, index: number): JSX.Element { const check = repo.missing ? undefined : recentRepoChecks[repo.path] const result = batchUpdateResults[repo.path] const selectable = Boolean(check?.canFastForward && !check.refreshing) const chips = check && check.state !== 'checking' ? repoStatusChips(t, check) : [] const laneTitle = chips.length > 0 ? chips.map((c) => c.title).join('\n') : check?.message + const lastCommit = check?.lastCommit ?? null return ( + // The row click is a pointer convenience only: keyboard users open the + // repo through the button inside it, which stays the accessible target. + // oxlint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
  • setDraggingPath(null)} - className={`workbench-row group flex cursor-grab items-center gap-1 py-1.5 pl-2 pr-1.5 ${repo.missing ? 'opacity-60' : ''}`} + // The whole row reads as one target (it highlights on hover), so a click + // anywhere on it opens the repo — except on its own controls and menu. + onClick={(e) => { + if ((e.target as HTMLElement).closest('button, input, a, [role="menu"]')) return + if (!isBusy && !repo.missing) openRepository(repo.path) + }} + style={{ animationDelay: `${Math.min(index, 12) * 28}ms` }} + className={`workbench-row workbench-row-enter group flex ${repo.missing ? 'cursor-grab' : 'cursor-pointer'} items-center gap-1 py-1.5 pl-2 pr-1.5 ${repo.missing ? 'opacity-60' : ''}`} > {showSelection && ( @@ -474,33 +500,51 @@ function InitialView(): JSX.Element { - {/* State, said in numbers: the lane draws it, these count it. */} + {/* State, said in numbers: the lane draws it, these count it. A healthy + repo has nothing to count, so the column tells what last happened + in it instead — the one thing that tells two green rows apart. */} {!repo.missing && ( - + {chips.length > 0 ? ( - chips.map((chip) => ( - - - {chip.value} - - )) - ) : ( + + {chips.map((chip) => ( + + + {chip.value} + + ))} + + ) : check?.state === 'failed' || !lastCommit ? ( {check?.message ?? ''} + ) : ( + + + {lastCommit.subject} + + )} + {lastCommit && ( + )} )} - + {/* Keyboard-reachable equivalent of dragging the row. */} +
  • + ) + })} + + + )}
    -
    - {user && ( -
    - - - - - - {user.name || user.login} - - @{user.login} · {t.githubConnected} + {/* Who is signed in — drawn as HEAD, with the bench's recent commits + trailing off its branch. The three places outside the bench ride + quietly at the end of that same row instead of a block of their own. */} +
    +
    +
    + {user && ( + + + + + + + {user.name || user.login} + + + {user.login} + · {t.githubConnected} + + - + )} +
    - )} -
    - {messagesLink} - - + {user && ( + + )}
    @@ -705,7 +784,6 @@ function InitialView(): JSX.Element {

    )}
    -

    {/* While cloning, the modal shows this same error inside itself. */} @@ -818,12 +896,12 @@ function InitialView(): JSX.Element { )} {/* Narrow windows hide the rail's footer; its links live here instead. */} -