diff --git a/.editorconfig b/.editorconfig index 8c626c425..cdacd2d2d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -36,12 +36,24 @@ indent_size = 2 [*.js] indent_size = 2 +[*.ts] +indent_size = 2 + [*.json] indent_size = 2 insert_final_newline = ignore +[*.map] +indent_size = ignore +insert_final_newline = ignore + # Minified JavaScript files shouldn't be changed -[**.min.js] +[*.min.js] +indent_style = ignore +insert_final_newline = ignore + +# Minified CSS files shouldn't be changed +[*.min.css] indent_style = ignore insert_final_newline = ignore diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 0dde34f66..8aa40bc3d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -92,5 +92,8 @@ jobs: - name: Setup venv run: make V=1 install + - name: Lint + run: make themes.lint + - name: Build run: make themes.all diff --git a/.nvmrc b/.nvmrc index 696e6df35..a45fd52cc 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v23.5 \ No newline at end of file +24 diff --git a/.tool-versions b/.tool-versions index 1b928297d..5548f7707 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,4 @@ -nodejs 23.5.0 +nodejs 24.3.0 python 3.13.1 shellcheck 0.10.0 -sqlite 3.47.2 \ No newline at end of file +sqlite 3.47.2 diff --git a/client/simple/biome.json b/client/simple/biome.json index ebe7ee7dc..5f27799c1 100644 --- a/client/simple/biome.json +++ b/client/simple/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json", + "$schema": "https://biomejs.dev/schemas/2.1.1/schema.json", "files": { "includes": ["**", "!dist/**", "!node_modules/**"], "ignoreUnknown": true diff --git a/client/simple/package-lock.json b/client/simple/package-lock.json index a910835ea..e04f87f5f 100644 --- a/client/simple/package-lock.json +++ b/client/simple/package-lock.json @@ -7,20 +7,25 @@ "name": "simple", "license": "AGPL-3.0", "devDependencies": { - "@biomejs/biome": "~2.0.6", + "@biomejs/biome": "~2.1.0", + "@types/node": "^24.0.0", + "browserslist": "^4.25.1", + "browserslist-to-esbuild": "^2.1.1", "edge.js": "^6.2.1", "ionicons": "^8.0.10", - "leaflet": "^1.9.4", "less": "^4.3.0", + "lightningcss": "^1.30.1", "normalize.css": "^8.0.1", - "sharp": "^0.34.2", + "ol": "^10.6.1", + "sharp": "^0.34.3", "sort-package-json": "^3.4.0", "stylelint": "^16.21.1", "stylelint-config-standard-less": "^3.0.1", "stylelint-prettier": "^5.0.3", "svgo": "^4.0.0", "swiped-events": "^1.2.0", - "vite": "^7.0.2", + "typescript": "~5.8.3", + "vite": "npm:rolldown-vite@~7.0.8", "vite-plugin-static-copy": "^3.1.0" } }, @@ -50,9 +55,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.0.6.tgz", - "integrity": "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.1.tgz", + "integrity": "sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -66,20 +71,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.0.6", - "@biomejs/cli-darwin-x64": "2.0.6", - "@biomejs/cli-linux-arm64": "2.0.6", - "@biomejs/cli-linux-arm64-musl": "2.0.6", - "@biomejs/cli-linux-x64": "2.0.6", - "@biomejs/cli-linux-x64-musl": "2.0.6", - "@biomejs/cli-win32-arm64": "2.0.6", - "@biomejs/cli-win32-x64": "2.0.6" + "@biomejs/cli-darwin-arm64": "2.1.1", + "@biomejs/cli-darwin-x64": "2.1.1", + "@biomejs/cli-linux-arm64": "2.1.1", + "@biomejs/cli-linux-arm64-musl": "2.1.1", + "@biomejs/cli-linux-x64": "2.1.1", + "@biomejs/cli-linux-x64-musl": "2.1.1", + "@biomejs/cli-win32-arm64": "2.1.1", + "@biomejs/cli-win32-x64": "2.1.1" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.6.tgz", - "integrity": "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.1.tgz", + "integrity": "sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ==", "cpu": [ "arm64" ], @@ -94,9 +99,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.6.tgz", - "integrity": "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.1.tgz", + "integrity": "sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA==", "cpu": [ "x64" ], @@ -111,9 +116,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.6.tgz", - "integrity": "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.1.tgz", + "integrity": "sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g==", "cpu": [ "arm64" ], @@ -128,9 +133,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.6.tgz", - "integrity": "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.1.tgz", + "integrity": "sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A==", "cpu": [ "arm64" ], @@ -145,9 +150,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.6.tgz", - "integrity": "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.1.tgz", + "integrity": "sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q==", "cpu": [ "x64" ], @@ -162,9 +167,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.6.tgz", - "integrity": "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.1.tgz", + "integrity": "sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g==", "cpu": [ "x64" ], @@ -179,9 +184,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.6.tgz", - "integrity": "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.1.tgz", + "integrity": "sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q==", "cpu": [ "arm64" ], @@ -196,9 +201,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.6.tgz", - "integrity": "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.1.tgz", + "integrity": "sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg==", "cpu": [ "x64" ], @@ -313,10 +318,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/@emnapi/core": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", + "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.3", + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", "dev": true, "license": "MIT", "optional": true, @@ -324,435 +341,21 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", - "cpu": [ - "ppc64" - ], + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", + "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -769,13 +372,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -792,13 +395,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -813,9 +416,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -830,9 +433,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -847,9 +450,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -864,9 +467,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -881,9 +484,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -898,9 +501,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -915,9 +518,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -932,9 +535,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -949,9 +552,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -968,13 +571,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -991,13 +594,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -1014,13 +640,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -1037,13 +663,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -1060,13 +686,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -1083,13 +709,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -1097,7 +723,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.3" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1107,9 +733,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -1127,9 +753,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -1147,9 +773,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -1186,6 +812,19 @@ "node": ">=8" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1224,16 +863,40 @@ "node": ">= 8" } }, - "node_modules/@poppinss/exception": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.1.tgz", - "integrity": "sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A==", + "node_modules/@oxc-project/runtime": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.76.0.tgz", + "integrity": "sha512-17iezP/BukiovZZR7lp6fZZjNTOmodCWQKkI7sn2sOB1TiccRWzO2bpxnE94jhg8l+nBRMrwnM/cjFCr23winw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=6.9.0" } }, + "node_modules/@oxc-project/types": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.76.0.tgz", + "integrity": "sha512-CH3THIrSViKal8yV/Wh3FK0pFhp40nzW1MUDCik9fNuid2D/7JJXKJnfFOAvMxInGXDlvmgT6ACAzrl47TqzkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@petamoriken/float16": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz", + "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", + "dev": true, + "license": "MIT" + }, + "node_modules/@poppinss/exception": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.2.tgz", + "integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==", + "dev": true, + "license": "MIT" + }, "node_modules/@poppinss/inspect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@poppinss/inspect/-/inspect-1.0.1.tgz", @@ -1245,14 +908,11 @@ } }, "node_modules/@poppinss/macroable": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@poppinss/macroable/-/macroable-1.0.4.tgz", - "integrity": "sha512-ct43jurbe7lsUX5eIrj4ijO3j/6zIPp7CDnFWXDs7UPAbw1Pu1iH3oAmFdP4jcskKJBURH5M9oTtyeiUXyHX8Q==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@poppinss/macroable/-/macroable-1.0.5.tgz", + "integrity": "sha512-6u61y1HHd090MEk1Av0/1btDmm2Hh/+XoJj+HgFYRh9koUPI822ybJbwLHuqjLNCiY+o1gRykg2igEqOf/VBZw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.16.0" - } + "license": "MIT" }, "node_modules/@poppinss/object-builder": { "version": "1.1.0", @@ -1265,9 +925,9 @@ } }, "node_modules/@poppinss/string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@poppinss/string/-/string-1.6.0.tgz", - "integrity": "sha512-HfAf9VqTvo31BsruwgwEauQ316RNODdryk6QgYZo4qTV50s0h1H9HmIr+QjwwI3u4Sz7r4Q1dd1EVaLB7pWlaw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@poppinss/string/-/string-1.7.0.tgz", + "integrity": "sha512-IuCtWaUwmJeAdby0n1a5cTYsBLe7fPymdc4oNTTl1b6l+Ok+14XpSX0ILOEU6UtZ9D2XI3f4TVUh4Titkk1xgw==", "dev": true, "license": "MIT", "dependencies": { @@ -1299,24 +959,10 @@ "node": ">=18.16.0" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", - "integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", - "integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.26.tgz", + "integrity": "sha512-I73Ej+PVoCJiYQHpy45CHKkLgFqrYv9O1CUJs6TIav6f8f9WAVeN/k0YXrs0tgMO20AfsyEN8zenz2wprVWOYQ==", "cpu": [ "arm64" ], @@ -1324,9 +970,173 @@ "license": "MIT", "optional": true, "os": [ - "android" + "darwin" ] }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.26.tgz", + "integrity": "sha512-IcXzfO2/9bnm6WfCNmGxBiD1kQQdA0pTjjGcjvglUub8H6RlEY0tz+IIQxUirsl/++84S0PkCuafAxZi8Am8fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.26.tgz", + "integrity": "sha512-foLJNqEFdvwFm2MXDFxgywxJMic+wovbpEyszlz5K/sUbN7sP2+NJ7MZAUMHuggiswB4Rt1HqRLYKy26zJev8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.26.tgz", + "integrity": "sha512-1BWDpLtujfZCvWAcfIamqHGWo2+VnPWvpZQR0DL5qNit6cu3FC0sRZ+bZzTUK0QWDTA7nUy5RR9fUTL2PQxH2g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.26.tgz", + "integrity": "sha512-lg6DVwciFb7sIw0ONDHeLhRuFQl/wz+J26bxfVOVzVoQ7Zgl07gDklv7q96W7SRDAjlG/20flBOexdiPim/I3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.26.tgz", + "integrity": "sha512-0X14trOBVtU13Y0XYeb8EvOvb3/TxJVOmalDakEID/UUX9qkvOmlU0fvoDVmsnhH6yx23bDlpmOj0f8V3BCgIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.26.tgz", + "integrity": "sha512-stb8XloM+N3hSKUs6kS5tNqrlTGsCoYuh9emFZtTovfFzzdFYevgXoOdeGoXv9KkPh5B7MOMl4/7c+WaX46Opg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.26.tgz", + "integrity": "sha512-5udEpAS5IUy2t74d/m40JUYyk3Ga8QXQDvK7eGqDDOwz8/7Piq0kCwmNuLnpSRiqbXNP8mnVlvtIcASJUEtRPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.26.tgz", + "integrity": "sha512-Is5tTdScXXQzslj7+jCFncPoRNARJ/+fYt/C9+Yx0QQ67/m8pGPLFoCzIKmJQZ8QHzOfq5ML4CQlMgBbCFlZqQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.10" + }, + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.26.tgz", + "integrity": "sha512-bH+TB+/8Z/95cxGws0fH995HsbsopVYdGcuM1Z/Hnqe7KPLkhqkubsambHQYd1V/QNbLzAgJ0nMAFLyBrwFZZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rolldown/binding-win32-ia32-msvc": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.26.tgz", + "integrity": "sha512-Nsg7ZzfwLHwKGneuNHEpqdBekmZA5pzVOuFx5R8EVyva8dg+sgtDHQRmiVSVYe25YYISNFXDSuHKwNhrWI4HWA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.26.tgz", + "integrity": "sha512-NE5Btf10Fu3IbpHxrlRkgcO/d05iEpbIiP/XdMYW7Lc9BGSgE4f8njUHnM0V2XJKyXkC1fqv/uHSEw2dCNgzxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.26.tgz", + "integrity": "sha512-r/5po89voz/QRPDmoErL10+hVuTAuz1SHvokx+yWBlOIPB5C41jC7QhLqq9kaebx/+EHyoV3z22/qBfX81Ns8A==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", @@ -1355,62 +1165,6 @@ "darwin" ] }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", - "integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", - "integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", - "integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", - "integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", @@ -1439,76 +1193,6 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", - "integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", - "integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", - "integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", - "integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", - "integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", @@ -1551,20 +1235,6 @@ "win32" ] }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", - "integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", @@ -1580,9 +1250,9 @@ ] }, "node_modules/@stencil/core": { - "version": "4.35.1", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.35.1.tgz", - "integrity": "sha512-u65m3TbzOtpn679gUV4Yvi8YpInhRJ62js30a7YtXief9Ej/vzrhwDE22U0w4DMWJOYwAsJl133BUaZkWwnmzg==", + "version": "4.35.3", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.35.3.tgz", + "integrity": "sha512-RH5/I+amV31QI8TMXhXkAkjzs2eod6Y07jkUYTl9kMB+X7c5wUpv95Y/2LtcAx0Rqdhh4SHbJiwpr0ApBZmv0g==", "dev": true, "license": "MIT", "bin": { @@ -1603,6 +1273,17 @@ "@rollup/rollup-win32-x64-msvc": "4.34.9" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/bytes": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.5.tgz", @@ -1610,12 +1291,15 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/@types/node": { + "version": "24.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", + "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } }, "node_modules/@types/pluralize": { "version": "0.0.33", @@ -1624,6 +1308,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/rbush": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz", + "integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1680,6 +1371,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansis": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1792,6 +1493,58 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/browserslist-to-esbuild": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browserslist-to-esbuild/-/browserslist-to-esbuild-2.1.1.tgz", + "integrity": "sha512-KN+mty6C3e9AN8Z5dI1xeN15ExcRNeISoC3g7V0Kax/MMF9MSoYA2G7lkTTcVUFntiEjkpI0HNgqJC1NjdyNUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "browserslist-to-esbuild": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "browserslist": "*" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -1828,14 +1581,14 @@ } }, "node_modules/cacheable": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.0.tgz", - "integrity": "sha512-SSgQTAnhd7WlJXnGlIi4jJJOiHzgnM5wRMEPaXAU4kECTAMpBoYKoZ9i5zHmclIEZbxcu3j7yY/CF8DTmwIsHg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.1.tgz", + "integrity": "sha512-Fa2BZY0CS9F0PFc/6aVA6tgpOdw+hmv9dkZOlHXII5v5Hw+meJBIWDcPrG9q/dXxGcNbym5t77fzmawrBQfTmQ==", "dev": true, "license": "MIT", "dependencies": { - "hookified": "^1.8.2", - "keyv": "^5.3.3" + "hookified": "^1.10.0", + "keyv": "^5.3.4" } }, "node_modules/callsites": { @@ -1848,6 +1601,27 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/case-anything": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-3.1.2.tgz", @@ -2221,6 +1995,13 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/earcut": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz", + "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==", + "dev": true, + "license": "ISC" + }, "node_modules/edge-error": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/edge-error/-/edge-error-4.0.2.tgz", @@ -2285,6 +2066,13 @@ "node": ">=18.16.0" } }, + "node_modules/electron-to-chromium": { + "version": "1.5.182", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", + "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2346,45 +2134,14 @@ "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "node": ">=6" } }, "node_modules/escape-goat": { @@ -2492,15 +2249,15 @@ } }, "node_modules/flat-cache": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.10.tgz", - "integrity": "sha512-B6/v1f0NwjxzmeOhzfXPGWpKBVA207LS7lehaVKQnFrVktcFRfkzjZZ2gwj2i1TkEUMQht7ZMJbABUT5N+V1Nw==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.11.tgz", + "integrity": "sha512-zfOAns94mp7bHG/vCn9Ru2eDCmIxVQ5dELUHKjHfDEOJmHNzE+uGa6208kfkgmtym4a0FFjEuFksCXFacbVhSg==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^1.10.0", + "cacheable": "^1.10.1", "flatted": "^3.3.3", - "hookified": "^1.9.1" + "hookified": "^1.10.0" } }, "node_modules/flatted": { @@ -2557,6 +2314,26 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/geotiff": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.3.tgz", + "integrity": "sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2", + "zstddec": "^0.1.0" + }, + "engines": { + "node": ">=10.19" + } + }, "node_modules/git-hooks-list": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-4.1.1.tgz", @@ -2674,9 +2451,9 @@ } }, "node_modules/hookified": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.9.1.tgz", - "integrity": "sha512-u3pxtGhKjcSXnGm1CX6aXS9xew535j3lkOCegbA6jdyh0BaAjTbXI4aslKstCr6zUNtoCxFGFKwjbSHdGrMB8g==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.10.0.tgz", + "integrity": "sha512-dJw0492Iddsj56U1JsSTm9E/0B/29a1AuoSLRAte8vQg/kaTGF3IgjEWT8c8yG4cC10+HisE1x5QAwR0Xwc+DA==", "dev": true, "license": "MIT" }, @@ -2797,12 +2574,13 @@ "license": "ISC" }, "node_modules/ionicons": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-8.0.10.tgz", - "integrity": "sha512-w+6VmjcRwlAtryXzM+BOlIHKyJYlrfbIIYWW4cU0BM8OECoTn/KF8ecOE5j4401z5/FcmHf/yXol1xinKuPM8g==", + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-8.0.13.tgz", + "integrity": "sha512-2QQVyG2P4wszne79jemMjWYLp0DBbDhr4/yFroPCxvPP1wtMxgdIV3l5n+XZ5E9mgoXU79w7yTWpm2XzJsISxQ==", "dev": true, + "license": "MIT", "dependencies": { - "@stencil/core": "^4.30.0" + "@stencil/core": "^4.35.3" } }, "node_modules/is-arrayish": { @@ -2986,12 +2764,12 @@ "dev": true, "license": "MIT" }, - "node_modules/leaflet": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", - "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==", "dev": true, - "license": "BSD-2-Clause" + "license": "Apache-2.0" }, "node_modules/less": { "version": "4.3.0", @@ -3020,6 +2798,245 @@ "source-map": "~0.6.0" } }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -3162,6 +3179,13 @@ "node": ">= 4.4.x" } }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3192,6 +3216,24 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/ol": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/ol/-/ol-10.6.1.tgz", + "integrity": "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/rbush": "4.0.0", + "earcut": "^3.0.0", + "geotiff": "^2.1.3", + "pbf": "4.0.1", + "rbush": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openlayers" + } + }, "node_modules/p-map": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", @@ -3205,6 +3247,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3218,6 +3267,13 @@ "node": ">=6" } }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "dev": true, + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -3257,6 +3313,19 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", + "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3436,6 +3505,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "dev": true, + "license": "MIT" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -3465,6 +3541,36 @@ ], "license": "MIT" }, + "node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "dev": true, + "license": "ISC" + }, + "node_modules/rbush": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-4.0.1.tgz", + "integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quickselect": "^3.0.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3498,6 +3604,16 @@ "node": ">=8" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -3509,158 +3625,36 @@ "node": ">=0.10.0" } }, - "node_modules/rollup": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", - "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", + "node_modules/rolldown": { + "version": "1.0.0-beta.26", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.26.tgz", + "integrity": "sha512-2rad1JDFst/GD1J86RuqN1SIP8O8Xv4UbqNyKaVayXTjgF0D6HpvTnUZ1RQ6tANpZweGmq4v6Ay0uyRNEycFPw==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/runtime": "=0.76.0", + "@oxc-project/types": "=0.76.0", + "@rolldown/pluginutils": "1.0.0-beta.26", + "ansis": "^4.0.0" }, "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "rolldown": "bin/cli.mjs" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.1", - "@rollup/rollup-android-arm64": "4.44.1", - "@rollup/rollup-darwin-arm64": "4.44.1", - "@rollup/rollup-darwin-x64": "4.44.1", - "@rollup/rollup-freebsd-arm64": "4.44.1", - "@rollup/rollup-freebsd-x64": "4.44.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", - "@rollup/rollup-linux-arm-musleabihf": "4.44.1", - "@rollup/rollup-linux-arm64-gnu": "4.44.1", - "@rollup/rollup-linux-arm64-musl": "4.44.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-musl": "4.44.1", - "@rollup/rollup-linux-s390x-gnu": "4.44.1", - "@rollup/rollup-linux-x64-gnu": "4.44.1", - "@rollup/rollup-linux-x64-musl": "4.44.1", - "@rollup/rollup-win32-arm64-msvc": "4.44.1", - "@rollup/rollup-win32-ia32-msvc": "4.44.1", - "@rollup/rollup-win32-x64-msvc": "4.44.1", - "fsevents": "~2.3.2" + "@rolldown/binding-darwin-arm64": "1.0.0-beta.26", + "@rolldown/binding-darwin-x64": "1.0.0-beta.26", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.26", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.26", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.26", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.26", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.26", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.26", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.26", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.26", + "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.26", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.26" } }, - "node_modules/rollup/node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", - "integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", - "integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", - "integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", - "integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", - "integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", - "integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", - "integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", - "integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -3739,9 +3733,9 @@ } }, "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -3757,27 +3751,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/sharp/node_modules/semver": { @@ -3866,6 +3861,7 @@ "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.4.0.tgz", "integrity": "sha512-97oFRRMM2/Js4oEA9LJhjyMlde+2ewpZQf53pgue27UkbEXfHJnDzHlUxQ/DWUkzqmp7DFwJp8D+wi/TYeQhpA==", "dev": true, + "license": "MIT", "dependencies": { "detect-indent": "^7.0.1", "detect-newline": "^4.0.1", @@ -3975,6 +3971,7 @@ "url": "https://github.com/sponsors/stylelint" } ], + "license": "MIT", "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", @@ -4292,6 +4289,27 @@ "dev": true, "license": "0BSD" }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -4302,6 +4320,37 @@ "node": ">= 10.0.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4310,16 +4359,18 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.2.tgz", - "integrity": "sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw==", + "name": "rolldown-vite", + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.0.8.tgz", + "integrity": "sha512-JvCftqO4+el0CujpfkakzI+yJDK03/6tUU/2THl7QfBvxItEC5zDJ7Y8cz0HB7vu6WM82G/emndyY+kUAYLPUg==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", "fdir": "^6.4.6", + "lightningcss": "^1.30.1", "picomatch": "^4.0.2", "postcss": "^8.5.6", - "rollup": "^4.40.0", + "rolldown": "1.0.0-beta.26", "tinyglobby": "^0.2.14" }, "bin": { @@ -4336,9 +4387,9 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "esbuild": "^0.25.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -4351,15 +4402,15 @@ "@types/node": { "optional": true }, + "esbuild": { + "optional": true + }, "jiti": { "optional": true }, "less": { "optional": true }, - "lightningcss": { - "optional": true - }, "sass": { "optional": true }, @@ -4431,6 +4482,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -4457,6 +4515,20 @@ "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } + }, + "node_modules/xml-utils": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz", + "integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/zstddec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", + "integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==", + "dev": true, + "license": "MIT AND BSD-3-Clause" } } } diff --git a/client/simple/package.json b/client/simple/package.json index 65d29b737..bdc19c8f8 100644 --- a/client/simple/package.json +++ b/client/simple/package.json @@ -6,31 +6,43 @@ "type": "module", "scripts": { "build": "npm run build:icons && npm run build:vite", - "build:icons": "node theme_icons.js", + "build:icons": "node theme_icons.ts", "build:vite": "vite build", "clean": "rm -Rf node_modules", "fix": "npm run fix:stylelint && npm run fix:biome && npm run fix:package", "fix:biome": "biome check --write", "fix:package": "sort-package-json --quiet", "fix:stylelint": "stylelint --fix strict 'src/**/*.{scss,sass,less,styl}'", - "lint": "npm run lint:biome", - "lint:biome": "biome lint" + "lint": "npm run lint:biome && npm run lint:tsc", + "lint:biome": "biome lint", + "lint:tsc": "tsc --noEmit" }, + "browserslist": [ + "Chrome >= 93", + "Firefox >= 92", + "Safari >= 15.4", + "not dead" + ], "devDependencies": { - "@biomejs/biome": "~2.0.6", + "@biomejs/biome": "~2.1.0", + "@types/node": "^24.0.0", + "browserslist": "^4.25.1", + "browserslist-to-esbuild": "^2.1.1", "edge.js": "^6.2.1", "ionicons": "^8.0.10", - "leaflet": "^1.9.4", "less": "^4.3.0", + "lightningcss": "^1.30.1", "normalize.css": "^8.0.1", - "sharp": "^0.34.2", + "ol": "^10.6.1", + "sharp": "^0.34.3", "sort-package-json": "^3.4.0", "stylelint": "^16.21.1", "stylelint-config-standard-less": "^3.0.1", "stylelint-prettier": "^5.0.3", "svgo": "^4.0.0", "swiped-events": "^1.2.0", - "vite": "^7.0.2", + "typescript": "~5.8.3", + "vite": "npm:rolldown-vite@~7.0.8", "vite-plugin-static-copy": "^3.1.0" } } diff --git a/client/simple/src/js/head/00_init.js b/client/simple/src/js/head/00_init.js deleted file mode 100644 index 7aec676a4..000000000 --- a/client/simple/src/js/head/00_init.js +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -((w, d) => { - // add data- properties - const getLastScriptElement = () => { - const scripts = d.getElementsByTagName("script"); - return scripts[scripts.length - 1]; - }; - - const script = d.currentScript || getLastScriptElement(); - - w.searxng = { - settings: JSON.parse(atob(script.getAttribute("client_settings"))) - }; - - // update the css - const htmlElement = d.getElementsByTagName("html")[0]; - htmlElement.classList.remove("no-js"); - htmlElement.classList.add("js"); -})(window, document); diff --git a/client/simple/src/js/main/00_toolkit.js b/client/simple/src/js/main/00_toolkit.js deleted file mode 100644 index 81e5071f9..000000000 --- a/client/simple/src/js/main/00_toolkit.js +++ /dev/null @@ -1,178 +0,0 @@ -/** - * @license - * (C) Copyright Contributors to the SearXNG project. - * (C) Copyright Contributors to the searx project (2014 - 2021). - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -window.searxng = ((w, d) => { - // not invented here toolkit with bugs fixed elsewhere - // purposes : be just good enough and as small as possible - - // from https://plainjs.com/javascript/events/live-binding-event-handlers-14/ - if (w.Element) { - ((ElementPrototype) => { - ElementPrototype.matches = - ElementPrototype.matches || - ElementPrototype.matchesSelector || - ElementPrototype.webkitMatchesSelector || - ElementPrototype.msMatchesSelector || - function (selector) { - const nodes = (this.parentNode || this.document).querySelectorAll(selector); - let i = -1; - while (nodes[++i] && nodes[i] !== this); - return !!nodes[i]; - }; - })(Element.prototype); - } - - function callbackSafe(callback, el, e) { - try { - callback.call(el, e); - } catch (exception) { - console.log(exception); - } - } - - const searxng = window.searxng || {}; - - searxng.on = (obj, eventType, callback, useCapture) => { - useCapture = useCapture || false; - if (typeof obj !== "string") { - // obj HTMLElement, HTMLDocument - obj.addEventListener(eventType, callback, useCapture); - } else { - // obj is a selector - d.addEventListener( - eventType, - (e) => { - let el = e.target || e.srcElement; - let found = false; - - while (el?.matches && el !== d) { - found = el.matches(obj); - - if (found) break; - - el = el.parentElement; - } - - if (found) { - callbackSafe(callback, el, e); - } - }, - useCapture - ); - } - }; - - searxng.ready = (callback) => { - if (document.readyState !== "loading") { - callback.call(w); - } else { - w.addEventListener("DOMContentLoaded", callback.bind(w)); - } - }; - - searxng.http = (method, url, data = null) => - new Promise((resolve, reject) => { - try { - const req = new XMLHttpRequest(); - req.open(method, url, true); - req.timeout = 20000; - - // On load - req.onload = () => { - if (req.status === 200) { - resolve(req.response, req.responseType); - } else { - reject(Error(req.statusText)); - } - }; - - // Handle network errors - req.onerror = () => { - reject(Error("Network Error")); - }; - - req.onabort = () => { - reject(Error("Transaction is aborted")); - }; - - req.ontimeout = () => { - reject(Error("Timeout")); - }; - - // Make the request - if (data) { - req.send(data); - } else { - req.send(); - } - } catch (ex) { - reject(ex); - } - }); - - searxng.loadStyle = (src) => { - const path = `${searxng.settings.theme_static_path}/${src}`; - const id = `style_${src.replace(".", "_")}`; - let s = d.getElementById(id); - if (s === null) { - s = d.createElement("link"); - s.setAttribute("id", id); - s.setAttribute("rel", "stylesheet"); - s.setAttribute("type", "text/css"); - s.setAttribute("href", path); - d.body.appendChild(s); - } - }; - - searxng.loadScript = (src, callback) => { - const path = `${searxng.settings.theme_static_path}/${src}`; - const id = `script_${src.replace(".", "_")}`; - let s = d.getElementById(id); - if (s === null) { - s = d.createElement("script"); - s.setAttribute("id", id); - s.setAttribute("src", path); - s.onload = callback; - s.onerror = () => { - s.setAttribute("error", "1"); - }; - d.body.appendChild(s); - } else if (!s.hasAttribute("error")) { - try { - callback.apply(s, []); - } catch (exception) { - console.log(exception); - } - } else { - console.log(`callback not executed : script '${path}' not loaded.`); - } - }; - - searxng.insertBefore = (newNode, referenceNode) => { - referenceNode.parentNode.insertBefore(newNode, referenceNode); - }; - - searxng.insertAfter = (newNode, referenceNode) => { - referenceNode.parentNode.insertAfter(newNode, referenceNode.nextSibling); - }; - - searxng.on(".close", "click", function () { - this.parentNode.classList.add("invisible"); - }); - - function getEndpoint() { - for (const className of d.getElementsByTagName("body")[0].classList.values()) { - if (className.endsWith("_endpoint")) { - return className.split("_")[0]; - } - } - return ""; - } - - searxng.endpoint = getEndpoint(); - - return searxng; -})(window, document); diff --git a/client/simple/src/js/main/00_toolkit.ts b/client/simple/src/js/main/00_toolkit.ts new file mode 100644 index 000000000..05cfc4b6b --- /dev/null +++ b/client/simple/src/js/main/00_toolkit.ts @@ -0,0 +1,118 @@ +import type { KeyBindingLayout } from "./keyboard.ts"; + +type Settings = { + theme_static_path?: string; + method?: string; + hotkeys?: KeyBindingLayout; + infinite_scroll?: boolean; + autocomplete?: boolean; + autocomplete_min?: number; + search_on_category_select?: boolean; + translations?: Record; + [key: string]: unknown; +}; + +type ReadyOptions = { + // all values must be truthy for the callback to be executed + on?: (boolean | undefined)[]; +}; + +const getEndpoint = (): string => { + const endpointClass = Array.from(document.body.classList).find((className) => className.endsWith("_endpoint")); + return endpointClass?.split("_")[0] ?? ""; +}; + +const getSettings = (): Settings => { + const settings = document.querySelector("script[client_settings]")?.getAttribute("client_settings"); + if (!settings) return {}; + + try { + return JSON.parse(atob(settings)); + } catch (error) { + console.error("Failed to load client_settings:", error); + return {}; + } +}; + +type AssertElement = (element?: Element | null) => asserts element is Element; +export const assertElement: AssertElement = (element?: Element | null): asserts element is Element => { + if (!element) { + throw new Error("Bad assertion: DOM element not found"); + } +}; + +export const searxng = { + // dynamic functions + closeDetail: undefined as (() => void) | undefined, + scrollPageToSelected: undefined as (() => void) | undefined, + selectImage: undefined as ((resultElement: Element) => void) | undefined, + selectNext: undefined as ((openDetailView?: boolean) => void) | undefined, + selectPrevious: undefined as ((openDetailView?: boolean) => void) | undefined, + + endpoint: getEndpoint(), + + http: async (method: string, url: string | URL, data?: BodyInit): Promise => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); + + const res = await fetch(url, { + body: data, + method, + signal: controller.signal + }).finally(() => clearTimeout(timeoutId)); + if (!res.ok) { + throw new Error(res.statusText); + } + + return res; + }, + + listen: ( + type: string | K, + target: string | Document | E, + listener: (this: E, event: DocumentEventMap[K]) => void, + options?: AddEventListenerOptions + ): void => { + if (typeof target !== "string") { + target.addEventListener(type, listener as EventListener, options); + return; + } + + document.addEventListener( + type, + (event: Event) => { + for (const node of event.composedPath()) { + if (node instanceof Element && node.matches(target)) { + try { + listener.call(node as E, event as DocumentEventMap[K]); + } catch (error) { + console.error(error); + } + break; + } + } + }, + options + ); + }, + + ready: (callback: () => void, options?: ReadyOptions): void => { + for (const condition of options?.on ?? []) { + if (!condition) { + return; + } + } + + if (document.readyState !== "loading") { + callback(); + } else { + searxng.listen("DOMContentLoaded", document, callback, { once: true }); + } + }, + + settings: getSettings() +}; + +searxng.listen("click", ".close", function (this: Element) { + (this.parentNode as Element)?.classList.add("invisible"); +}); diff --git a/client/simple/src/js/main/index.ts b/client/simple/src/js/main/index.ts new file mode 100644 index 000000000..4dc86b63b --- /dev/null +++ b/client/simple/src/js/main/index.ts @@ -0,0 +1,13 @@ +/** + * @preserve (C) Copyright Contributors to the SearXNG project. + * @preserve (C) Copyright Contributors to the searx project (2014 - 2021). + * @license AGPL-3.0-or-later + */ + +import "./00_toolkit.ts"; +import "./infinite_scroll.ts"; +import "./keyboard.ts"; +import "./mapresult.ts"; +import "./preferences.ts"; +import "./results.ts"; +import "./search.ts"; diff --git a/client/simple/src/js/main/infinite_scroll.js b/client/simple/src/js/main/infinite_scroll.js deleted file mode 100644 index 12ecd83be..000000000 --- a/client/simple/src/js/main/infinite_scroll.js +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -/* global searxng */ - -searxng.ready(() => { - searxng.infinite_scroll_supported = - "IntersectionObserver" in window && - "IntersectionObserverEntry" in window && - "intersectionRatio" in window.IntersectionObserverEntry.prototype; - - if (searxng.endpoint !== "results") { - return; - } - - if (!searxng.infinite_scroll_supported) { - console.log("IntersectionObserver not supported"); - return; - } - - const d = document; - const onlyImages = d.getElementById("results").classList.contains("only_template_images"); - - function newLoadSpinner() { - const loader = d.createElement("div"); - loader.classList.add("loader"); - return loader; - } - - function replaceChildrenWith(element, children) { - element.textContent = ""; - children.forEach((child) => element.appendChild(child)); - } - - function loadNextPage(callback) { - const form = d.querySelector("#pagination form.next_page"); - if (!form) { - return; - } - replaceChildrenWith(d.querySelector("#pagination"), [newLoadSpinner()]); - const formData = new FormData(form); - searxng - .http("POST", d.querySelector("#search").getAttribute("action"), formData) - .then((response) => { - const nextPageDoc = new DOMParser().parseFromString(response, "text/html"); - const articleList = nextPageDoc.querySelectorAll("#urls article"); - const paginationElement = nextPageDoc.querySelector("#pagination"); - d.querySelector("#pagination").remove(); - if (articleList.length > 0 && !onlyImages) { - // do not add
element when there are only images - d.querySelector("#urls").appendChild(d.createElement("hr")); - } - articleList.forEach((articleElement) => { - d.querySelector("#urls").appendChild(articleElement); - }); - if (paginationElement) { - d.querySelector("#results").appendChild(paginationElement); - callback(); - } - }) - .catch((err) => { - console.log(err); - const e = d.createElement("div"); - e.textContent = searxng.settings.translations.error_loading_next_page; - e.classList.add("dialog-error"); - e.setAttribute("role", "alert"); - replaceChildrenWith(d.querySelector("#pagination"), [e]); - }); - } - - if (searxng.settings.infinite_scroll && searxng.infinite_scroll_supported) { - const intersectionObserveOptions = { - rootMargin: "20rem" - }; - const observedSelector = "article.result:last-child"; - const observer = new IntersectionObserver((entries) => { - const paginationEntry = entries[0]; - if (paginationEntry.isIntersecting) { - observer.unobserve(paginationEntry.target); - loadNextPage(() => observer.observe(d.querySelector(observedSelector), intersectionObserveOptions)); - } - }); - observer.observe(d.querySelector(observedSelector), intersectionObserveOptions); - } -}); diff --git a/client/simple/src/js/main/infinite_scroll.ts b/client/simple/src/js/main/infinite_scroll.ts new file mode 100644 index 000000000..e9f931e51 --- /dev/null +++ b/client/simple/src/js/main/infinite_scroll.ts @@ -0,0 +1,108 @@ +import { assertElement, searxng } from "./00_toolkit"; + +const newLoadSpinner = (): HTMLDivElement => { + return Object.assign(document.createElement("div"), { + className: "loader" + }); +}; + +const loadNextPage = async (onlyImages: boolean, callback: () => void): Promise => { + const searchForm = document.querySelector("#search"); + assertElement(searchForm); + + const form = document.querySelector("#pagination form.next_page"); + assertElement(form); + + const formData = new FormData(form); + + const action = searchForm.getAttribute("action"); + if (!action) { + console.error("Form action not found"); + return; + } + + const paginationElement = document.querySelector("#pagination"); + assertElement(paginationElement); + + paginationElement.replaceChildren(newLoadSpinner()); + + try { + const res = await searxng.http("POST", action, formData); + const nextPage = await res.text(); + if (!nextPage) return; + + const nextPageDoc = new DOMParser().parseFromString(nextPage, "text/html"); + const articleList = nextPageDoc.querySelectorAll("#urls article"); + const nextPaginationElement = nextPageDoc.querySelector("#pagination"); + + document.querySelector("#pagination")?.remove(); + + const urlsElement = document.querySelector("#urls"); + if (!urlsElement) { + console.error("URLs element not found"); + return; + } + + if (articleList.length > 0 && !onlyImages) { + // do not add
element when there are only images + urlsElement.appendChild(document.createElement("hr")); + } + + urlsElement.append(...Array.from(articleList)); + + if (nextPaginationElement) { + const results = document.querySelector("#results"); + results?.appendChild(nextPaginationElement); + callback(); + } + } catch (error) { + console.error("Error loading next page:", error); + + const errorElement = Object.assign(document.createElement("div"), { + textContent: searxng.settings.translations?.error_loading_next_page ?? "Error loading next page", + className: "dialog-error" + }); + errorElement.setAttribute("role", "alert"); + document.querySelector("#pagination")?.replaceChildren(errorElement); + } +}; + +searxng.ready( + () => { + const resultsElement = document.getElementById("results"); + if (!resultsElement) { + console.error("Results element not found"); + return; + } + + const onlyImages = resultsElement.classList.contains("only_template_images"); + const observedSelector = "article.result:last-child"; + + const intersectionObserveOptions: IntersectionObserverInit = { + rootMargin: "320px" + }; + + const observer = new IntersectionObserver(async (entries: IntersectionObserverEntry[]) => { + const [paginationEntry] = entries; + + if (paginationEntry?.isIntersecting) { + observer.unobserve(paginationEntry.target); + + await loadNextPage(onlyImages, () => { + const nextObservedElement = document.querySelector(observedSelector); + if (nextObservedElement) { + observer.observe(nextObservedElement); + } + }); + } + }, intersectionObserveOptions); + + const initialObservedElement = document.querySelector(observedSelector); + if (initialObservedElement) { + observer.observe(initialObservedElement); + } + }, + { + on: [searxng.endpoint === "results", searxng.settings.infinite_scroll] + } +); diff --git a/client/simple/src/js/main/keyboard.js b/client/simple/src/js/main/keyboard.js deleted file mode 100644 index e707c397f..000000000 --- a/client/simple/src/js/main/keyboard.js +++ /dev/null @@ -1,473 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -/* global searxng */ - -searxng.ready(() => { - function isElementInDetail(el) { - while (el !== undefined) { - if (el.classList.contains("detail")) { - return true; - } - if (el.classList.contains("result")) { - // we found a result, no need to go to the root of the document: - // el is not inside a
element - return false; - } - el = el.parentNode; - } - return false; - } - - function getResultElement(el) { - while (el !== undefined) { - if (el.classList.contains("result")) { - return el; - } - el = el.parentNode; - } - return undefined; - } - - function isImageResult(resultElement) { - return resultElement?.classList.contains("result-images"); - } - - searxng.on(".result", "click", function (e) { - if (!isElementInDetail(e.target)) { - highlightResult(this)(true, true); - const resultElement = getResultElement(e.target); - if (isImageResult(resultElement)) { - e.preventDefault(); - searxng.selectImage(resultElement); - } - } - }); - - searxng.on( - ".result a", - "focus", - (e) => { - if (!isElementInDetail(e.target)) { - const resultElement = getResultElement(e.target); - if (resultElement && resultElement.getAttribute("data-vim-selected") === null) { - highlightResult(resultElement)(true); - } - if (isImageResult(resultElement)) { - searxng.selectImage(resultElement); - } - } - }, - true - ); - - /* common base for layouts */ - const baseKeyBinding = { - Escape: { - key: "ESC", - fun: removeFocus, - des: "remove focus from the focused input", - cat: "Control" - }, - c: { - key: "c", - fun: copyURLToClipboard, - des: "copy url of the selected result to the clipboard", - cat: "Results" - }, - h: { - key: "h", - fun: toggleHelp, - des: "toggle help window", - cat: "Other" - }, - i: { - key: "i", - fun: searchInputFocus, - des: "focus on the search input", - cat: "Control" - }, - n: { - key: "n", - fun: GoToNextPage(), - des: "go to next page", - cat: "Results" - }, - o: { - key: "o", - fun: openResult(false), - des: "open search result", - cat: "Results" - }, - p: { - key: "p", - fun: GoToPreviousPage(), - des: "go to previous page", - cat: "Results" - }, - r: { - key: "r", - fun: reloadPage, - des: "reload page from the server", - cat: "Control" - }, - t: { - key: "t", - fun: openResult(true), - des: "open the result in a new tab", - cat: "Results" - } - }; - const keyBindingLayouts = { - default: Object.assign( - { - /* SearXNG layout */ - ArrowLeft: { - key: "←", - fun: highlightResult("up"), - des: "select previous search result", - cat: "Results" - }, - ArrowRight: { - key: "→", - fun: highlightResult("down"), - des: "select next search result", - cat: "Results" - } - }, - baseKeyBinding - ), - - vim: Object.assign( - { - /* Vim-like Key Layout. */ - b: { - key: "b", - fun: scrollPage(-window.innerHeight), - des: "scroll one page up", - cat: "Navigation" - }, - f: { - key: "f", - fun: scrollPage(window.innerHeight), - des: "scroll one page down", - cat: "Navigation" - }, - u: { - key: "u", - fun: scrollPage(-window.innerHeight / 2), - des: "scroll half a page up", - cat: "Navigation" - }, - d: { - key: "d", - fun: scrollPage(window.innerHeight / 2), - des: "scroll half a page down", - cat: "Navigation" - }, - g: { - key: "g", - fun: scrollPageTo(-document.body.scrollHeight, "top"), - des: "scroll to the top of the page", - cat: "Navigation" - }, - v: { - key: "v", - fun: scrollPageTo(document.body.scrollHeight, "bottom"), - des: "scroll to the bottom of the page", - cat: "Navigation" - }, - k: { - key: "k", - fun: highlightResult("up"), - des: "select previous search result", - cat: "Results" - }, - j: { - key: "j", - fun: highlightResult("down"), - des: "select next search result", - cat: "Results" - }, - y: { - key: "y", - fun: copyURLToClipboard, - des: "copy url of the selected result to the clipboard", - cat: "Results" - } - }, - baseKeyBinding - ) - }; - - const keyBindings = keyBindingLayouts[searxng.settings.hotkeys] || keyBindingLayouts.default; - - searxng.on(document, "keydown", (e) => { - // check for modifiers so we don't break browser's hotkeys - if ( - // biome-ignore lint/suspicious/noPrototypeBuiltins: FIXME: support for Chromium 93-87, Firefox 92-78, Safari 15.4-14 - Object.prototype.hasOwnProperty.call(keyBindings, e.key) && - !e.ctrlKey && - !e.altKey && - !e.shiftKey && - !e.metaKey - ) { - const tagName = e.target.tagName.toLowerCase(); - if (e.key === "Escape") { - keyBindings[e.key].fun(e); - } else { - if (e.target === document.body || tagName === "a" || tagName === "button") { - e.preventDefault(); - keyBindings[e.key].fun(); - } - } - } - }); - - function highlightResult(which) { - return (noScroll, keepFocus) => { - let current = document.querySelector(".result[data-vim-selected]"), - effectiveWhich = which; - if (current === null) { - // no selection : choose the first one - current = document.querySelector(".result"); - if (current === null) { - // no first one : there are no results - return; - } - // replace up/down actions by selecting first one - if (which === "down" || which === "up") { - effectiveWhich = current; - } - } - - let next, - results = document.querySelectorAll(".result"); - results = Array.from(results); // convert NodeList to Array for further use - - if (typeof effectiveWhich !== "string") { - next = effectiveWhich; - } else { - switch (effectiveWhich) { - case "visible": { - const top = document.documentElement.scrollTop || document.body.scrollTop; - const bot = top + document.documentElement.clientHeight; - - for (let i = 0; i < results.length; i++) { - next = results[i]; - const etop = next.offsetTop; - const ebot = etop + next.clientHeight; - - if (ebot <= bot && etop > top) { - break; - } - } - break; - } - case "down": - next = results[results.indexOf(current) + 1] || current; - break; - case "up": - next = results[results.indexOf(current) - 1] || current; - break; - case "bottom": - next = results[results.length - 1]; - break; - // biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended - case "top": - /* falls through */ - default: - next = results[0]; - } - } - - if (next) { - current.removeAttribute("data-vim-selected"); - next.setAttribute("data-vim-selected", "true"); - if (!keepFocus) { - const link = next.querySelector("h3 a") || next.querySelector("a"); - if (link !== null) { - link.focus(); - } - } - if (!noScroll) { - scrollPageToSelected(); - } - } - }; - } - - function reloadPage() { - document.location.reload(true); - } - - function removeFocus(e) { - const tagName = e.target.tagName.toLowerCase(); - if (document.activeElement && (tagName === "input" || tagName === "select" || tagName === "textarea")) { - document.activeElement.blur(); - } else { - searxng.closeDetail(); - } - } - - function pageButtonClick(css_selector) { - return () => { - const button = document.querySelector(css_selector); - if (button) { - button.click(); - } - }; - } - - function GoToNextPage() { - return pageButtonClick('nav#pagination .next_page button[type="submit"]'); - } - - function GoToPreviousPage() { - return pageButtonClick('nav#pagination .previous_page button[type="submit"]'); - } - - function scrollPageToSelected() { - const sel = document.querySelector(".result[data-vim-selected]"); - if (sel === null) { - return; - } - const wtop = document.documentElement.scrollTop || document.body.scrollTop, - wheight = document.documentElement.clientHeight, - etop = sel.offsetTop, - ebot = etop + sel.clientHeight, - offset = 120; - // first element ? - if (sel.previousElementSibling === null && ebot < wheight) { - // set to the top of page if the first element - // is fully included in the viewport - window.scroll(window.scrollX, 0); - return; - } - if (wtop > etop - offset) { - window.scroll(window.scrollX, etop - offset); - } else { - const wbot = wtop + wheight; - if (wbot < ebot + offset) { - window.scroll(window.scrollX, ebot - wheight + offset); - } - } - } - - function scrollPage(amount) { - return () => { - window.scrollBy(0, amount); - highlightResult("visible")(); - }; - } - - function scrollPageTo(position, nav) { - return () => { - window.scrollTo(0, position); - highlightResult(nav)(); - }; - } - - function searchInputFocus() { - window.scrollTo(0, 0); - const q = document.querySelector("#q"); - q.focus(); - if (q.setSelectionRange) { - const len = q.value.length; - q.setSelectionRange(len, len); - } - } - - function openResult(newTab) { - return () => { - let link = document.querySelector(".result[data-vim-selected] h3 a"); - if (link === null) { - link = document.querySelector(".result[data-vim-selected] > a"); - } - if (link !== null) { - const url = link.getAttribute("href"); - if (newTab) { - window.open(url); - } else { - window.location.href = url; - } - } - }; - } - - function initHelpContent(divElement) { - const categories = {}; - - for (const k in keyBindings) { - const key = keyBindings[k]; - categories[key.cat] = categories[key.cat] || []; - categories[key.cat].push(key); - } - - const sorted = Object.keys(categories).sort((a, b) => categories[b].length - categories[a].length); - - if (sorted.length === 0) { - return; - } - - let html = '×'; - html += "

How to navigate SearXNG with hotkeys

"; - html += ""; - - for (let i = 0; i < sorted.length; i++) { - const cat = categories[sorted[i]]; - - const lastCategory = i === sorted.length - 1; - const first = i % 2 === 0; - - if (first) { - html += ""; - } - html += ""; // col-sm-* - - if (!first || lastCategory) { - html += ""; // row - } - } - - html += "
"; - - html += `

${cat[0].cat}

`; - html += '
    '; - - for (const cj in cat) { - html += `
  • ${cat[cj].key} ${cat[cj].des}
  • `; - } - - html += "
"; - html += "
"; - - divElement.innerHTML = html; - } - - function toggleHelp() { - let helpPanel = document.querySelector("#vim-hotkeys-help"); - if (helpPanel === undefined || helpPanel === null) { - // first call - helpPanel = document.createElement("div"); - helpPanel.id = "vim-hotkeys-help"; - helpPanel.className = "dialog-modal"; - initHelpContent(helpPanel); - const body = document.getElementsByTagName("body")[0]; - body.appendChild(helpPanel); - } else { - // toggle hidden - helpPanel.classList.toggle("invisible"); - } - } - - function copyURLToClipboard() { - const currentUrlElement = document.querySelector(".result[data-vim-selected] h3 a"); - if (currentUrlElement === null) return; - - const url = currentUrlElement.getAttribute("href"); - navigator.clipboard.writeText(url); - } - - searxng.scrollPageToSelected = scrollPageToSelected; - searxng.selectNext = highlightResult("down"); - searxng.selectPrevious = highlightResult("up"); -}); diff --git a/client/simple/src/js/main/keyboard.ts b/client/simple/src/js/main/keyboard.ts new file mode 100644 index 000000000..67dd1b3a0 --- /dev/null +++ b/client/simple/src/js/main/keyboard.ts @@ -0,0 +1,469 @@ +import { assertElement, searxng } from "./00_toolkit.ts"; + +export type KeyBindingLayout = "default" | "vim"; + +type KeyBinding = { + key: string; + fun: (event: KeyboardEvent) => void; + des: string; + cat: string; +}; + +/* common base for layouts */ +const baseKeyBinding: Record = { + Escape: { + key: "ESC", + fun: (event) => removeFocus(event), + des: "remove focus from the focused input", + cat: "Control" + }, + c: { + key: "c", + fun: () => copyURLToClipboard(), + des: "copy url of the selected result to the clipboard", + cat: "Results" + }, + h: { + key: "h", + fun: () => toggleHelp(keyBindings), + des: "toggle help window", + cat: "Other" + }, + i: { + key: "i", + fun: () => searchInputFocus(), + des: "focus on the search input", + cat: "Control" + }, + n: { + key: "n", + fun: () => GoToNextPage(), + des: "go to next page", + cat: "Results" + }, + o: { + key: "o", + fun: () => openResult(false), + des: "open search result", + cat: "Results" + }, + p: { + key: "p", + fun: () => GoToPreviousPage(), + des: "go to previous page", + cat: "Results" + }, + r: { + key: "r", + fun: () => reloadPage(), + des: "reload page from the server", + cat: "Control" + }, + t: { + key: "t", + fun: () => openResult(true), + des: "open the result in a new tab", + cat: "Results" + } +}; + +const keyBindingLayouts: Record> = { + // SearXNG layout + default: { + ArrowLeft: { + key: "←", + fun: () => highlightResult("up"), + des: "select previous search result", + cat: "Results" + }, + ArrowRight: { + key: "→", + fun: () => highlightResult("down"), + des: "select next search result", + cat: "Results" + }, + ...baseKeyBinding + }, + + // Vim-like keyboard layout + vim: { + b: { + key: "b", + fun: () => scrollPage(-window.innerHeight), + des: "scroll one page up", + cat: "Navigation" + }, + d: { + key: "d", + fun: () => scrollPage(window.innerHeight / 2), + des: "scroll half a page down", + cat: "Navigation" + }, + f: { + key: "f", + fun: () => scrollPage(window.innerHeight), + des: "scroll one page down", + cat: "Navigation" + }, + g: { + key: "g", + fun: () => scrollPageTo(-document.body.scrollHeight, "top"), + des: "scroll to the top of the page", + cat: "Navigation" + }, + j: { + key: "j", + fun: () => highlightResult("down"), + des: "select next search result", + cat: "Results" + }, + k: { + key: "k", + fun: () => highlightResult("up"), + des: "select previous search result", + cat: "Results" + }, + u: { + key: "u", + fun: () => scrollPage(-window.innerHeight / 2), + des: "scroll half a page up", + cat: "Navigation" + }, + v: { + key: "v", + fun: () => scrollPageTo(document.body.scrollHeight, "bottom"), + des: "scroll to the bottom of the page", + cat: "Navigation" + }, + y: { + key: "y", + fun: () => copyURLToClipboard(), + des: "copy url of the selected result to the clipboard", + cat: "Results" + }, + ...baseKeyBinding + } +}; + +const keyBindings = + searxng.settings.hotkeys && searxng.settings.hotkeys in keyBindingLayouts + ? keyBindingLayouts[searxng.settings.hotkeys] + : keyBindingLayouts.default; + +const isElementInDetail = (element?: Element): boolean => { + const ancestor = element?.closest(".detail, .result"); + return ancestor?.classList.contains("detail") ?? false; +}; + +const getResultElement = (element?: Element): Element | undefined => { + return element?.closest(".result") ?? undefined; +}; + +const isImageResult = (resultElement?: Element): boolean => { + return resultElement?.classList.contains("result-images") ?? false; +}; + +const highlightResult = + (which: string | Element) => + (noScroll?: boolean, keepFocus?: boolean): void => { + let current = document.querySelector(".result[data-vim-selected]"); + let effectiveWhich = which; + if (!current) { + // no selection : choose the first one + current = document.querySelector(".result"); + if (!current) { + // no first one : there are no results + return; + } + // replace up/down actions by selecting first one + if (which === "down" || which === "up") { + effectiveWhich = current; + } + } + + let next: Element | null | undefined = null; + const results = Array.from(document.querySelectorAll(".result")); + + if (typeof effectiveWhich !== "string") { + next = effectiveWhich; + } else { + switch (effectiveWhich) { + case "visible": { + const top = document.documentElement.scrollTop || document.body.scrollTop; + const bot = top + document.documentElement.clientHeight; + + for (let i = 0; i < results.length; i++) { + const element = results[i] as HTMLElement; + next = element; + + const etop = element.offsetTop; + const ebot = etop + element.clientHeight; + + if (ebot <= bot && etop > top) { + break; + } + } + break; + } + case "down": + next = results[results.indexOf(current) + 1] || current; + break; + case "up": + next = results[results.indexOf(current) - 1] || current; + break; + case "bottom": + next = results[results.length - 1]; + break; + // biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended + case "top": + default: + next = results[0]; + } + } + + if (next && current) { + current.removeAttribute("data-vim-selected"); + next.setAttribute("data-vim-selected", "true"); + if (!keepFocus) { + const link = next.querySelector("h3 a") || next.querySelector("a"); + if (link) { + link.focus(); + } + } + if (!noScroll) { + scrollPageToSelected(); + } + } + }; + +const reloadPage = (): void => { + document.location.reload(); +}; + +const removeFocus = (event: KeyboardEvent): void => { + const target = event.target as HTMLElement; + const tagName = target?.tagName?.toLowerCase(); + + if (document.activeElement && (tagName === "input" || tagName === "select" || tagName === "textarea")) { + (document.activeElement as HTMLElement).blur(); + } else { + searxng.closeDetail?.(); + } +}; + +const pageButtonClick = (css_selector: string): void => { + const button = document.querySelector(css_selector); + if (button) { + button.click(); + } +}; + +const GoToNextPage = () => { + pageButtonClick('nav#pagination .next_page button[type="submit"]'); +}; + +const GoToPreviousPage = () => { + pageButtonClick('nav#pagination .previous_page button[type="submit"]'); +}; + +const scrollPageToSelected = (): void => { + const sel = document.querySelector(".result[data-vim-selected]"); + if (!sel) return; + + const wtop = document.documentElement.scrollTop || document.body.scrollTop, + height = document.documentElement.clientHeight, + etop = sel.offsetTop, + ebot = etop + sel.clientHeight, + offset = 120; + + // first element ? + if (!sel.previousElementSibling && ebot < height) { + // set to the top of page if the first element + // is fully included in the viewport + window.scroll(window.scrollX, 0); + return; + } + + if (wtop > etop - offset) { + window.scroll(window.scrollX, etop - offset); + } else { + const wbot = wtop + height; + if (wbot < ebot + offset) { + window.scroll(window.scrollX, ebot - height + offset); + } + } +}; + +const scrollPage = (amount: number): void => { + window.scrollBy(0, amount); + highlightResult("visible")(); +}; + +const scrollPageTo = (position: number, nav: string): void => { + window.scrollTo(0, position); + highlightResult(nav)(); +}; + +const searchInputFocus = (): void => { + window.scrollTo(0, 0); + + const q = document.querySelector("#q"); + if (q) { + q.focus(); + + if (q.setSelectionRange) { + const len = q.value.length; + + q.setSelectionRange(len, len); + } + } +}; + +const openResult = (newTab: boolean): void => { + let link = document.querySelector(".result[data-vim-selected] h3 a"); + if (!link) { + link = document.querySelector(".result[data-vim-selected] > a"); + } + if (!link) return; + + const url = link.getAttribute("href"); + if (url) { + if (newTab) { + window.open(url); + } else { + window.location.href = url; + } + } +}; + +const initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBinding): void => { + const categories: Record = {}; + + for (const binding of Object.values(keyBindings)) { + const cat = binding.cat; + categories[cat] ??= []; + categories[cat].push(binding); + } + + const sortedCategoryKeys = Object.keys(categories).sort( + (a, b) => (categories[b]?.length ?? 0) - (categories[a]?.length ?? 0) + ); + + let html = '×'; + html += "

How to navigate SearXNG with hotkeys

"; + html += ""; + + for (const [i, categoryKey] of sortedCategoryKeys.entries()) { + const bindings = categories[categoryKey]; + if (!bindings || bindings.length === 0) continue; + + const isFirst = i % 2 === 0; + const isLast = i === sortedCategoryKeys.length - 1; + + if (isFirst) { + html += ""; + } + + html += ""; + + if (!isFirst || isLast) { + html += ""; + } + } + + html += "
"; + html += `

${categoryKey}

`; + html += '
    '; + + for (const binding of bindings) { + html += `
  • ${binding.key} ${binding.des}
  • `; + } + + html += "
"; + html += "
"; + + divElement.innerHTML = html; +}; + +const toggleHelp = (keyBindings: typeof baseKeyBinding): void => { + let helpPanel = document.querySelector("#vim-hotkeys-help"); + if (!helpPanel) { + // first call + helpPanel = Object.assign(document.createElement("div"), { + id: "vim-hotkeys-help", + className: "dialog-modal" + }); + initHelpContent(helpPanel, keyBindings); + const body = document.getElementsByTagName("body")[0]; + if (body) { + body.appendChild(helpPanel); + } + } else { + // toggle hidden + helpPanel.classList.toggle("invisible"); + } +}; + +const copyURLToClipboard = async (): Promise => { + const currentUrlElement = document.querySelector(".result[data-vim-selected] h3 a"); + assertElement(currentUrlElement); + + const url = currentUrlElement.getAttribute("href"); + if (url) { + await navigator.clipboard.writeText(url); + } +}; + +searxng.ready(() => { + searxng.listen("click", ".result", function (this: Element, event: Event) { + if (!isElementInDetail(event.target as Element)) { + highlightResult(this)(true, true); + + const resultElement = getResultElement(event.target as Element); + + if (resultElement && isImageResult(resultElement)) { + event.preventDefault(); + searxng.selectImage?.(resultElement); + } + } + }); + + searxng.listen( + "focus", + ".result a", + (event: Event) => { + if (!isElementInDetail(event.target as Element)) { + const resultElement = getResultElement(event.target as Element); + + if (resultElement && !resultElement.getAttribute("data-vim-selected")) { + highlightResult(resultElement)(true); + } + + if (resultElement && isImageResult(resultElement)) { + searxng.selectImage?.(resultElement); + } + } + }, + { capture: true } + ); + + searxng.listen("keydown", document, (event: KeyboardEvent) => { + // check for modifiers so we don't break browser's hotkeys + if (Object.hasOwn(keyBindings, event.key) && !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey) { + const tagName = (event.target as Element)?.tagName?.toLowerCase(); + + if (event.key === "Escape") { + keyBindings[event.key]?.fun(event); + } else { + if (event.target === document.body || tagName === "a" || tagName === "button") { + event.preventDefault(); + keyBindings[event.key]?.fun(event); + } + } + } + }); + + searxng.scrollPageToSelected = scrollPageToSelected; + searxng.selectNext = highlightResult("down"); + searxng.selectPrevious = highlightResult("up"); +}); diff --git a/client/simple/src/js/main/mapresult.js b/client/simple/src/js/main/mapresult.js deleted file mode 100644 index 3f2d06548..000000000 --- a/client/simple/src/js/main/mapresult.js +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -/* global L */ -((_w, _d, searxng) => { - searxng.ready(() => { - searxng.on(".searxng_init_map", "click", function (event) { - // no more request - this.classList.remove("searxng_init_map"); - - // - const leaflet_target = this.dataset.leafletTarget; - const map_lon = parseFloat(this.dataset.mapLon); - const map_lat = parseFloat(this.dataset.mapLat); - const map_zoom = parseFloat(this.dataset.mapZoom); - const map_boundingbox = JSON.parse(this.dataset.mapBoundingbox); - const map_geojson = JSON.parse(this.dataset.mapGeojson); - - searxng.loadStyle("css/leaflet.css"); - searxng.loadScript("js/leaflet.js", () => { - let map_bounds = null; - if (map_boundingbox) { - const southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]); - const northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]); - map_bounds = L.latLngBounds(southWest, northEast); - } - - // init map - const map = L.map(leaflet_target); - // create the tile layer with correct attribution - const osmMapnikUrl = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"; - const osmMapnikAttrib = 'Map data © OpenStreetMap contributors'; - const osmMapnik = new L.TileLayer(osmMapnikUrl, { minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib }); - const osmWikimediaUrl = "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png"; - const osmWikimediaAttrib = - 'Wikimedia maps | Maps data © OpenStreetMap contributors'; - const osmWikimedia = new L.TileLayer(osmWikimediaUrl, { - minZoom: 1, - maxZoom: 19, - attribution: osmWikimediaAttrib - }); - // init map view - if (map_bounds) { - // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021 - // Still useful ? - setTimeout(() => { - map.fitBounds(map_bounds, { - maxZoom: 17 - }); - }, 0); - } else if (map_lon && map_lat) { - if (map_zoom) { - map.setView(new L.LatLng(map_lat, map_lon), map_zoom); - } else { - map.setView(new L.LatLng(map_lat, map_lon), 8); - } - } - - map.addLayer(osmMapnik); - - const baseLayers = { - "OSM Mapnik": osmMapnik, - "OSM Wikimedia": osmWikimedia - }; - - L.control.layers(baseLayers).addTo(map); - - if (map_geojson) { - L.geoJson(map_geojson).addTo(map); - } /* else if(map_bounds) { - L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map); - } */ - }); - - // this event occur only once per element - event.preventDefault(); - }); - }); -})(window, document, window.searxng); diff --git a/client/simple/src/js/main/mapresult.ts b/client/simple/src/js/main/mapresult.ts new file mode 100644 index 000000000..421b41f77 --- /dev/null +++ b/client/simple/src/js/main/mapresult.ts @@ -0,0 +1,89 @@ +import { searxng } from "./00_toolkit.ts"; + +searxng.ready( + () => { + searxng.listen("click", ".searxng_init_map", async function (this: HTMLElement, event: Event) { + event.preventDefault(); + this.classList.remove("searxng_init_map"); + + const { + View, + OlMap, + TileLayer, + VectorLayer, + OSM, + VectorSource, + Style, + Stroke, + Fill, + Circle, + fromLonLat, + GeoJSON, + Feature, + Point + } = await import("../pkg/ol.ts"); + import("ol/ol.css"); + + const { leafletTarget: target, mapLon, mapLat, mapGeojson } = this.dataset; + + const lon = parseFloat(mapLon || "0"); + const lat = parseFloat(mapLat || "0"); + const view = new View({ maxZoom: 16, enableRotation: false }); + const map = new OlMap({ + target, + layers: [new TileLayer({ source: new OSM({ maxZoom: 16 }) })], + view + }); + + try { + const markerSource = new VectorSource({ + features: [ + new Feature({ + geometry: new Point(fromLonLat([lon, lat])) + }) + ] + }); + + const markerLayer = new VectorLayer({ + source: markerSource, + style: new Style({ + image: new Circle({ + radius: 6, + fill: new Fill({ color: "#3050ff" }) + }) + }) + }); + + map.addLayer(markerLayer); + } catch (error) { + console.error("Failed to create marker layer:", error); + } + + if (mapGeojson) { + try { + const geoSource = new VectorSource({ + features: new GeoJSON().readFeatures(JSON.parse(mapGeojson), { + dataProjection: "EPSG:4326", + featureProjection: "EPSG:3857" + }) + }); + + const geoLayer = new VectorLayer({ + source: geoSource, + style: new Style({ + stroke: new Stroke({ color: "#3050ff", width: 2 }), + fill: new Fill({ color: "#3050ff33" }) + }) + }); + + map.addLayer(geoLayer); + + view.fit(geoSource.getExtent(), { padding: [20, 20, 20, 20] }); + } catch (error) { + console.error("Failed to create GeoJSON layer:", error); + } + } + }); + }, + { on: [searxng.endpoint === "results"] } +); diff --git a/client/simple/src/js/main/preferences.js b/client/simple/src/js/main/preferences.js deleted file mode 100644 index ac080e290..000000000 --- a/client/simple/src/js/main/preferences.js +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -((_w, d, searxng) => { - if (searxng.endpoint !== "preferences") { - return; - } - - searxng.ready(() => { - let engine_descriptions = null; - - function load_engine_descriptions() { - if (engine_descriptions == null) { - searxng.http("GET", "engine_descriptions.json").then((content) => { - engine_descriptions = JSON.parse(content); - for (const [engine_name, description] of Object.entries(engine_descriptions)) { - const elements = d.querySelectorAll(`[data-engine-name="${engine_name}"] .engine-description`); - for (const element of elements) { - const source = ` (${searxng.settings.translations.Source}: ${description[1]})`; - element.innerHTML = description[0] + source; - } - } - }); - } - } - - for (const el of d.querySelectorAll("[data-engine-name]")) { - searxng.on(el, "mouseenter", load_engine_descriptions); - } - - const enableAllEngines = d.querySelectorAll(".enable-all-engines"); - const disableAllEngines = d.querySelectorAll(".disable-all-engines"); - const engineToggles = d.querySelectorAll("tbody input[type=checkbox][class~=checkbox-onoff]"); - const toggleEngines = (enable) => { - for (const el of engineToggles) { - // check if element visible, so that only engines of the current category are modified - if (el.offsetParent !== null) el.checked = !enable; - } - }; - for (const el of enableAllEngines) { - searxng.on(el, "click", () => toggleEngines(true)); - } - for (const el of disableAllEngines) { - searxng.on(el, "click", () => toggleEngines(false)); - } - - const copyHashButton = d.querySelector("#copy-hash"); - searxng.on(copyHashButton, "click", (e) => { - e.preventDefault(); - navigator.clipboard.writeText(copyHashButton.dataset.hash); - copyHashButton.innerText = copyHashButton.dataset.copiedText; - }); - }); -})(window, document, window.searxng); diff --git a/client/simple/src/js/main/preferences.ts b/client/simple/src/js/main/preferences.ts new file mode 100644 index 000000000..6c66018a6 --- /dev/null +++ b/client/simple/src/js/main/preferences.ts @@ -0,0 +1,71 @@ +import { searxng } from "./00_toolkit.ts"; + +const loadEngineDescriptions = async (): Promise => { + let engineDescriptions: Record | null = null; + try { + const res = await searxng.http("GET", "engine_descriptions.json"); + engineDescriptions = await res.json(); + } catch (error) { + console.error("Error fetching engineDescriptions:", error); + } + if (!engineDescriptions) return; + + for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) { + const elements = document.querySelectorAll(`[data-engine-name="${engine_name}"] .engine-description`); + const sourceText = ` (${searxng.settings.translations?.Source}: ${source})`; + + for (const element of elements) { + element.innerHTML = description + sourceText; + } + } +}; + +const toggleEngines = (enable: boolean, engineToggles: NodeListOf): void => { + for (const engineToggle of engineToggles) { + // check if element visible, so that only engines of the current category are modified + if (engineToggle.offsetParent) { + engineToggle.checked = !enable; + } + } +}; + +searxng.ready( + () => { + const engineElements = document.querySelectorAll("[data-engine-name]"); + for (const engineElement of engineElements) { + searxng.listen("mouseenter", engineElement, loadEngineDescriptions); + } + + const engineToggles = document.querySelectorAll( + "tbody input[type=checkbox][class~=checkbox-onoff]" + ); + + const enableAllEngines = document.querySelectorAll(".enable-all-engines"); + for (const engine of enableAllEngines) { + searxng.listen("click", engine, () => toggleEngines(true, engineToggles)); + } + + const disableAllEngines = document.querySelectorAll(".disable-all-engines"); + for (const engine of disableAllEngines) { + searxng.listen("click", engine, () => toggleEngines(false, engineToggles)); + } + + const copyHashButton = document.querySelector("#copy-hash"); + if (copyHashButton) { + searxng.listen("click", copyHashButton, async (event: Event) => { + event.preventDefault(); + + const { copiedText, hash } = copyHashButton.dataset; + if (!copiedText || !hash) return; + + try { + await navigator.clipboard.writeText(hash); + copyHashButton.innerText = copiedText; + } catch (error) { + console.error("Failed to copy hash:", error); + } + }); + } + }, + { on: [searxng.endpoint === "preferences"] } +); diff --git a/client/simple/src/js/main/results.js b/client/simple/src/js/main/results.js deleted file mode 100644 index cf7829912..000000000 --- a/client/simple/src/js/main/results.js +++ /dev/null @@ -1,182 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ - -import "../../../node_modules/swiped-events/src/swiped-events.js"; - -((w, d, searxng) => { - if (searxng.endpoint !== "results") { - return; - } - - searxng.ready(() => { - d.querySelectorAll("#urls img").forEach((img) => - img.addEventListener( - "error", - () => { - // console.log("ERROR can't load: " + img.src); - img.src = `${window.searxng.settings.theme_static_path}/img/img_load_error.svg`; - }, - { once: true } - ) - ); - - if (d.querySelector("#search_url button#copy_url")) { - d.querySelector("#search_url button#copy_url").style.display = "block"; - } - - searxng.on(".btn-collapse", "click", function () { - const btnLabelCollapsed = this.getAttribute("data-btn-text-collapsed"); - const btnLabelNotCollapsed = this.getAttribute("data-btn-text-not-collapsed"); - const target = this.getAttribute("data-target"); - const targetElement = d.querySelector(target); - let html = this.innerHTML; - if (this.classList.contains("collapsed")) { - html = html.replace(btnLabelCollapsed, btnLabelNotCollapsed); - } else { - html = html.replace(btnLabelNotCollapsed, btnLabelCollapsed); - } - this.innerHTML = html; - this.classList.toggle("collapsed"); - targetElement.classList.toggle("invisible"); - }); - - searxng.on(".media-loader", "click", function () { - const target = this.getAttribute("data-target"); - const iframe_load = d.querySelector(`${target} > iframe`); - const srctest = iframe_load.getAttribute("src"); - if (srctest === null || srctest === undefined || srctest === false) { - iframe_load.setAttribute("src", iframe_load.getAttribute("data-src")); - } - }); - - searxng.on("#copy_url", "click", function () { - const target = this.parentElement.querySelector("pre"); - navigator.clipboard.writeText(target.innerText); - this.innerText = this.dataset.copiedText; - }); - - // searxng.selectImage (gallery) - // ----------------------------- - - // setTimeout() ID, needed to cancel *last* loadImage - let imgTimeoutID; - - // progress spinner, while an image is loading - const imgLoaderSpinner = d.createElement("div"); - imgLoaderSpinner.classList.add("loader"); - - // singleton image object, which is used for all loading processes of a - // detailed image - const imgLoader = new Image(); - - const loadImage = (imgSrc, onSuccess) => { - // if defered image load exists, stop defered task. - if (imgTimeoutID) clearTimeout(imgTimeoutID); - - // defer load of the detail image for 1 sec - imgTimeoutID = setTimeout(() => { - imgLoader.src = imgSrc; - }, 1000); - - // set handlers in the on-properties - imgLoader.onload = () => { - onSuccess(); - imgLoaderSpinner.remove(); - }; - imgLoader.onerror = () => { - imgLoaderSpinner.remove(); - }; - }; - - searxng.selectImage = (resultElement) => { - // add a class that can be evaluated in the CSS and indicates that the - // detail view is open - d.getElementById("results").classList.add("image-detail-open"); - - // add a hash to the browser history so that pressing back doesn't return - // to the previous page this allows us to dismiss the image details on - // pressing the back button on mobile devices - window.location.hash = "#image-viewer"; - - searxng.scrollPageToSelected(); - - // if there is none element given by the caller, stop here - if (!resultElement) return; - - // find object in the element, if there is none, stop here. - const img = resultElement.querySelector(".result-images-source img"); - if (!img) return; - - // - const src = img.getAttribute("data-src"); - - // already loaded high-res image or no high-res image available - if (!src) return; - - // use the image thumbnail until the image is fully loaded - const thumbnail = resultElement.querySelector(".image_thumbnail"); - img.src = thumbnail.src; - - // show a progress spinner - const detailElement = resultElement.querySelector(".detail"); - detailElement.appendChild(imgLoaderSpinner); - - // load full size image in background - loadImage(src, () => { - // after the singelton loadImage has loaded the detail image into the - // cache, it can be used in the origin as src property. - img.src = src; - img.removeAttribute("data-src"); - }); - }; - - searxng.closeDetail = () => { - d.getElementById("results").classList.remove("image-detail-open"); - // remove #image-viewer hash from url by navigating back - if (window.location.hash === "#image-viewer") window.history.back(); - searxng.scrollPageToSelected(); - }; - searxng.on(".result-detail-close", "click", (e) => { - e.preventDefault(); - searxng.closeDetail(); - }); - searxng.on(".result-detail-previous", "click", (e) => { - e.preventDefault(); - searxng.selectPrevious(false); - }); - searxng.on(".result-detail-next", "click", (e) => { - e.preventDefault(); - searxng.selectNext(false); - }); - - // listen for the back button to be pressed and dismiss the image details when called - window.addEventListener("hashchange", () => { - if (window.location.hash !== "#image-viewer") searxng.closeDetail(); - }); - - d.querySelectorAll(".swipe-horizontal").forEach((obj) => { - obj.addEventListener("swiped-left", () => { - searxng.selectNext(false); - }); - obj.addEventListener("swiped-right", () => { - searxng.selectPrevious(false); - }); - }); - - w.addEventListener( - "scroll", - () => { - const e = d.getElementById("backToTop"), - scrollTop = document.documentElement.scrollTop || document.body.scrollTop, - results = d.getElementById("results"); - if (e !== null) { - if (scrollTop >= 100) { - results.classList.add("scrolling"); - } else { - results.classList.remove("scrolling"); - } - } - }, - true - ); - }); -})(window, document, window.searxng); diff --git a/client/simple/src/js/main/results.ts b/client/simple/src/js/main/results.ts new file mode 100644 index 000000000..e278c894a --- /dev/null +++ b/client/simple/src/js/main/results.ts @@ -0,0 +1,181 @@ +import "../../../node_modules/swiped-events/src/swiped-events.js"; +import { assertElement, searxng } from "./00_toolkit.ts"; + +const loadImage = (imgSrc: string, onSuccess: () => void): void => { + // singleton image object, which is used for all loading processes of a detailed image + const imgLoader = new Image(); + + // set handlers in the on-properties + imgLoader.onload = () => { + onSuccess(); + }; + + imgLoader.src = imgSrc; +}; + +searxng.ready( + () => { + const imageThumbnails = document.querySelectorAll("#urls img.image_thumbnail"); + for (const thumbnail of imageThumbnails) { + if (thumbnail.complete && thumbnail.naturalWidth === 0) { + thumbnail.src = `${searxng.settings.theme_static_path}/img/img_load_error.svg`; + } + + thumbnail.onerror = () => { + thumbnail.src = `${searxng.settings.theme_static_path}/img/img_load_error.svg`; + }; + } + + const copyUrlButton = document.querySelector("#search_url button#copy_url"); + copyUrlButton?.style.setProperty("display", "block"); + + searxng.listen("click", ".btn-collapse", function (this: HTMLElement) { + const btnLabelCollapsed = this.getAttribute("data-btn-text-collapsed"); + const btnLabelNotCollapsed = this.getAttribute("data-btn-text-not-collapsed"); + const target = this.getAttribute("data-target"); + + if (!target || !btnLabelCollapsed || !btnLabelNotCollapsed) return; + + const targetElement = document.querySelector(target); + assertElement(targetElement); + + const isCollapsed = this.classList.contains("collapsed"); + const newLabel = isCollapsed ? btnLabelNotCollapsed : btnLabelCollapsed; + const oldLabel = isCollapsed ? btnLabelCollapsed : btnLabelNotCollapsed; + + this.innerHTML = this.innerHTML.replace(oldLabel, newLabel); + this.classList.toggle("collapsed"); + + targetElement.classList.toggle("invisible"); + }); + + searxng.listen("click", ".media-loader", function (this: HTMLElement) { + const target = this.getAttribute("data-target"); + if (!target) return; + + const iframeLoad = document.querySelector(`${target} > iframe`); + assertElement(iframeLoad); + + const srctest = iframeLoad.getAttribute("src"); + if (!srctest) { + const dataSrc = iframeLoad.getAttribute("data-src"); + if (dataSrc) { + iframeLoad.setAttribute("src", dataSrc); + } + } + }); + + searxng.listen("click", "#copy_url", async function (this: HTMLElement) { + const target = this.parentElement?.querySelector("pre"); + assertElement(target); + + await navigator.clipboard.writeText(target.innerText); + const copiedText = this.dataset.copiedText; + if (copiedText) { + this.innerText = copiedText; + } + }); + + searxng.selectImage = (resultElement: Element): void => { + // add a class that can be evaluated in the CSS and indicates that the + // detail view is open + const resultsElement = document.getElementById("results"); + resultsElement?.classList.add("image-detail-open"); + + // add a hash to the browser history so that pressing back doesn't return + // to the previous page this allows us to dismiss the image details on + // pressing the back button on mobile devices + window.location.hash = "#image-viewer"; + + searxng.scrollPageToSelected?.(); + + // if there is no element given by the caller, stop here + if (!resultElement) return; + + // find image element, if there is none, stop here + const img = resultElement.querySelector(".result-images-source img"); + if (!img) return; + + // + const src = img.getAttribute("data-src"); + if (!src) return; + + // use thumbnail until full image loads + const thumbnail = resultElement.querySelector(".image_thumbnail"); + if (thumbnail) { + img.src = thumbnail.src; + } + + // load full size image + loadImage(src, () => { + img.src = src; + img.onerror = () => { + img.src = `${searxng.settings.theme_static_path}/img/img_load_error.svg`; + }; + + img.removeAttribute("data-src"); + }); + }; + + searxng.closeDetail = (): void => { + const resultsElement = document.getElementById("results"); + resultsElement?.classList.remove("image-detail-open"); + + // remove #image-viewer hash from url by navigating back + if (window.location.hash === "#image-viewer") { + window.history.back(); + } + + searxng.scrollPageToSelected?.(); + }; + + searxng.listen("click", ".result-detail-close", (event: Event) => { + event.preventDefault(); + searxng.closeDetail?.(); + }); + + searxng.listen("click", ".result-detail-previous", (event: Event) => { + event.preventDefault(); + searxng.selectPrevious?.(false); + }); + + searxng.listen("click", ".result-detail-next", (event: Event) => { + event.preventDefault(); + searxng.selectNext?.(false); + }); + + // listen for the back button to be pressed and dismiss the image details when called + window.addEventListener("hashchange", () => { + if (window.location.hash !== "#image-viewer") { + searxng.closeDetail?.(); + } + }); + + const swipeHorizontal = document.querySelectorAll(".swipe-horizontal"); + for (const element of swipeHorizontal) { + searxng.listen("swiped-left", element, () => { + searxng.selectNext?.(false); + }); + + searxng.listen("swiped-right", element, () => { + searxng.selectPrevious?.(false); + }); + } + + window.addEventListener( + "scroll", + () => { + const backToTopElement = document.getElementById("backToTop"); + const resultsElement = document.getElementById("results"); + + if (backToTopElement && resultsElement) { + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + const isScrolling = scrollTop >= 100; + resultsElement.classList.toggle("scrolling", isScrolling); + } + }, + true + ); + }, + { on: [searxng.endpoint === "results"] } +); diff --git a/client/simple/src/js/main/search.js b/client/simple/src/js/main/search.js deleted file mode 100644 index 5d1e18bde..000000000 --- a/client/simple/src/js/main/search.js +++ /dev/null @@ -1,197 +0,0 @@ -/* SPDX-License-Identifier: AGPL-3.0-or-later */ -/* exported AutoComplete */ - -((_w, d, searxng) => { - const qinput_id = "q"; - let qinput; - - const isMobile = window.matchMedia("only screen and (max-width: 50em)").matches; - const isResultsPage = document.querySelector("main").id === "main_results"; - - function submitIfQuery() { - if (qinput.value.length > 0) { - const search = document.getElementById("search"); - setTimeout(search.submit.bind(search), 0); - } - } - - function createClearButton(qinput) { - const cs = document.getElementById("clear_search"); - const updateClearButton = () => { - if (qinput.value.length === 0) { - cs.classList.add("empty"); - } else { - cs.classList.remove("empty"); - } - }; - - // update status, event listener - updateClearButton(); - cs.addEventListener("click", (ev) => { - qinput.value = ""; - qinput.focus(); - updateClearButton(); - ev.preventDefault(); - }); - qinput.addEventListener("input", updateClearButton, false); - } - - const fetchResults = async (query) => { - let request; - if (searxng.settings.method === "GET") { - const reqParams = new URLSearchParams(); - reqParams.append("q", query); - request = fetch(`./autocompleter?${reqParams.toString()}`); - } else { - const formData = new FormData(); - formData.append("q", query); - request = fetch("./autocompleter", { - method: "POST", - body: formData - }); - } - - request.then(async (response) => { - const results = await response.json(); - - if (!results) return; - - const autocomplete = d.querySelector(".autocomplete"); - const autocompleteList = d.querySelector(".autocomplete ul"); - autocomplete.classList.add("open"); - autocompleteList.innerHTML = ""; - - // show an error message that no result was found - if (!results[1] || results[1].length === 0) { - const noItemFoundMessage = document.createElement("li"); - noItemFoundMessage.classList.add("no-item-found"); - noItemFoundMessage.innerHTML = searxng.settings.translations.no_item_found; - autocompleteList.appendChild(noItemFoundMessage); - return; - } - - for (const result of results[1]) { - const li = document.createElement("li"); - li.innerText = result; - - searxng.on(li, "mousedown", () => { - qinput.value = result; - const form = d.querySelector("#search"); - form.submit(); - autocomplete.classList.remove("open"); - }); - autocompleteList.appendChild(li); - } - }); - }; - - searxng.ready(() => { - // focus search input on large screens - if (!isMobile && !isResultsPage) document.getElementById("q").focus(); - - qinput = d.getElementById(qinput_id); - const autocomplete = d.querySelector(".autocomplete"); - const autocompleteList = d.querySelector(".autocomplete ul"); - - if (qinput !== null) { - // clear button - createClearButton(qinput); - - // autocompleter - if (searxng.settings.autocomplete) { - searxng.on(qinput, "input", () => { - const query = qinput.value; - if (query.length < searxng.settings.autocomplete_min) return; - - setTimeout(() => { - if (query === qinput.value) fetchResults(query); - }, 300); - }); - - searxng.on(qinput, "keyup", (e) => { - let currentIndex = -1; - const listItems = autocompleteList.children; - for (let i = 0; i < listItems.length; i++) { - if (listItems[i].classList.contains("active")) { - currentIndex = i; - break; - } - } - - let newCurrentIndex = -1; - if (e.key === "ArrowUp") { - if (currentIndex >= 0) listItems[currentIndex].classList.remove("active"); - // we need to add listItems.length to the index calculation here because the JavaScript modulos - // operator doesn't work with negative numbers - newCurrentIndex = (currentIndex - 1 + listItems.length) % listItems.length; - } else if (e.key === "ArrowDown") { - if (currentIndex >= 0) listItems[currentIndex].classList.remove("active"); - newCurrentIndex = (currentIndex + 1) % listItems.length; - } else if (e.key === "Tab" || e.key === "Enter") { - autocomplete.classList.remove("open"); - } - - if (newCurrentIndex !== -1) { - const selectedItem = listItems[newCurrentIndex]; - selectedItem.classList.add("active"); - - if (!selectedItem.classList.contains("no-item-found")) qinput.value = selectedItem.innerText; - } - }); - } - } - - // Additionally to searching when selecting a new category, we also - // automatically start a new search request when the user changes a search - // filter (safesearch, time range or language) (this requires JavaScript - // though) - if ( - qinput !== null && - searxng.settings.search_on_category_select && - // If .search_filters is undefined (invisible) we are on the homepage and - // hence don't have to set any listeners - d.querySelector(".search_filters") != null - ) { - searxng.on(d.getElementById("safesearch"), "change", submitIfQuery); - searxng.on(d.getElementById("time_range"), "change", submitIfQuery); - searxng.on(d.getElementById("language"), "change", submitIfQuery); - } - - const categoryButtons = d.querySelectorAll("button.category_button"); - for (const button of categoryButtons) { - searxng.on(button, "click", (event) => { - if (event.shiftKey) { - event.preventDefault(); - button.classList.toggle("selected"); - return; - } - - // manually deselect the old selection when a new category is selected - const selectedCategories = d.querySelectorAll("button.category_button.selected"); - for (const categoryButton of selectedCategories) { - categoryButton.classList.remove("selected"); - } - button.classList.add("selected"); - }); - } - - // override form submit action to update the actually selected categories - const form = d.querySelector("#search"); - if (form != null) { - searxng.on(form, "submit", (event) => { - event.preventDefault(); - const categoryValuesInput = d.querySelector("#selected-categories"); - if (categoryValuesInput) { - const categoryValues = []; - for (const categoryButton of categoryButtons) { - if (categoryButton.classList.contains("selected")) { - categoryValues.push(categoryButton.name.replace("category_", "")); - } - } - categoryValuesInput.value = categoryValues.join(","); - } - form.submit(); - }); - } - }); -})(window, document, window.searxng); diff --git a/client/simple/src/js/main/search.ts b/client/simple/src/js/main/search.ts new file mode 100644 index 000000000..5e68965b1 --- /dev/null +++ b/client/simple/src/js/main/search.ts @@ -0,0 +1,233 @@ +import { assertElement, searxng } from "./00_toolkit.ts"; + +const submitIfQuery = (qInput: HTMLInputElement): void => { + if (qInput.value.length > 0) { + const search = document.getElementById("search") as HTMLFormElement | null; + search?.submit(); + } +}; + +const updateClearButton = (qInput: HTMLInputElement, cs: HTMLElement): void => { + cs.classList.toggle("empty", qInput.value.length === 0); +}; + +const createClearButton = (qInput: HTMLInputElement): void => { + const cs = document.getElementById("clear_search"); + assertElement(cs); + + updateClearButton(qInput, cs); + + searxng.listen("click", cs, (event: MouseEvent) => { + event.preventDefault(); + qInput.value = ""; + qInput.focus(); + updateClearButton(qInput, cs); + }); + + searxng.listen("input", qInput, () => updateClearButton(qInput, cs), { passive: true }); +}; + +const fetchResults = async (qInput: HTMLInputElement, query: string): Promise => { + try { + let res: Response; + + if (searxng.settings.method === "GET") { + res = await searxng.http("GET", `./autocompleter?q=${query}`); + } else { + res = await searxng.http("POST", "./autocompleter", new URLSearchParams({ q: query })); + } + + const results = await res.json(); + + const autocomplete = document.querySelector(".autocomplete"); + assertElement(autocomplete); + + const autocompleteList = document.querySelector(".autocomplete ul"); + assertElement(autocompleteList); + + autocomplete.classList.add("open"); + autocompleteList.replaceChildren(); + + // show an error message that no result was found + if (!results?.[1]?.length) { + const noItemFoundMessage = Object.assign(document.createElement("li"), { + className: "no-item-found", + textContent: searxng.settings.translations?.no_item_found ?? "No results found" + }); + autocompleteList.append(noItemFoundMessage); + return; + } + + const fragment = new DocumentFragment(); + + for (const result of results[1]) { + const li = Object.assign(document.createElement("li"), { textContent: result }); + + searxng.listen("mousedown", li, () => { + qInput.value = result; + + const form = document.querySelector("#search"); + form?.submit(); + + autocomplete.classList.remove("open"); + }); + + fragment.append(li); + } + + autocompleteList.append(fragment); + } catch (error) { + console.error("Error fetching autocomplete results:", error); + } +}; + +searxng.ready( + () => { + const qInput = document.getElementById("q") as HTMLInputElement | null; + assertElement(qInput); + + const isMobile = window.matchMedia("(max-width: 50em)").matches; + const isResultsPage = document.querySelector("main")?.id === "main_results"; + + // focus search input on large screens + if (!isMobile && !isResultsPage) { + qInput.focus(); + } + + createClearButton(qInput); + + // autocompleter + if (searxng.settings.autocomplete) { + let timeoutId: number; + + searxng.listen("input", qInput, () => { + clearTimeout(timeoutId); + + const query = qInput.value; + const minLength = searxng.settings.autocomplete_min ?? 2; + + if (query.length < minLength) return; + + timeoutId = window.setTimeout(async () => { + if (query === qInput.value) { + await fetchResults(qInput, query); + } + }, 300); + }); + + const autocomplete = document.querySelector(".autocomplete"); + const autocompleteList = document.querySelector(".autocomplete ul"); + if (autocompleteList) { + searxng.listen("keyup", qInput, (event: KeyboardEvent) => { + const listItems = [...autocompleteList.children] as HTMLElement[]; + + const currentIndex = listItems.findIndex((item) => item.classList.contains("active")); + let newCurrentIndex = -1; + + switch (event.key) { + case "ArrowUp": { + const currentItem = listItems[currentIndex]; + if (currentItem && currentIndex >= 0) { + currentItem.classList.remove("active"); + } + // we need to add listItems.length to the index calculation here because the JavaScript modulos + // operator doesn't work with negative numbers + newCurrentIndex = (currentIndex - 1 + listItems.length) % listItems.length; + break; + } + case "ArrowDown": { + const currentItem = listItems[currentIndex]; + if (currentItem && currentIndex >= 0) { + currentItem.classList.remove("active"); + } + newCurrentIndex = (currentIndex + 1) % listItems.length; + break; + } + case "Tab": + case "Enter": + if (autocomplete) { + autocomplete.classList.remove("open"); + } + break; + } + + if (newCurrentIndex !== -1) { + const selectedItem = listItems[newCurrentIndex]; + if (selectedItem) { + selectedItem.classList.add("active"); + + if (!selectedItem.classList.contains("no-item-found")) { + const qInput = document.getElementById("q") as HTMLInputElement | null; + if (qInput) { + qInput.value = selectedItem.textContent ?? ""; + } + } + } + } + }); + } + } + + // Additionally to searching when selecting a new category, we also + // automatically start a new search request when the user changes a search + // filter (safesearch, time range or language) (this requires JavaScript + // though) + if ( + searxng.settings.search_on_category_select && + // If .search_filters is undefined (invisible) we are on the homepage and + // hence don't have to set any listeners + document.querySelector(".search_filters") + ) { + const safesearchElement = document.getElementById("safesearch"); + if (safesearchElement) { + searxng.listen("change", safesearchElement, () => submitIfQuery(qInput)); + } + + const timeRangeElement = document.getElementById("time_range"); + if (timeRangeElement) { + searxng.listen("change", timeRangeElement, () => submitIfQuery(qInput)); + } + + const languageElement = document.getElementById("language"); + if (languageElement) { + searxng.listen("change", languageElement, () => submitIfQuery(qInput)); + } + } + + const categoryButtons = [...document.querySelectorAll("button.category_button")]; + for (const button of categoryButtons) { + searxng.listen("click", button, (event: MouseEvent) => { + if (event.shiftKey) { + event.preventDefault(); + button.classList.toggle("selected"); + return; + } + + // deselect all other categories + for (const categoryButton of categoryButtons) { + categoryButton.classList.toggle("selected", categoryButton === button); + } + }); + } + + const form = document.querySelector("#search"); + assertElement(form); + + // override form submit action to update the actually selected categories + searxng.listen("submit", form, (event: Event) => { + event.preventDefault(); + + const categoryValuesInput = document.querySelector("#selected-categories"); + if (categoryValuesInput) { + const categoryValues = categoryButtons + .filter((button) => button.classList.contains("selected")) + .map((button) => button.name.replace("category_", "")); + + categoryValuesInput.value = categoryValues.join(","); + } + + form.submit(); + }); + }, + { on: [searxng.endpoint === "index" || searxng.endpoint === "results"] } +); diff --git a/client/simple/src/js/pkg/ol.ts b/client/simple/src/js/pkg/ol.ts new file mode 100644 index 000000000..f0f932182 --- /dev/null +++ b/client/simple/src/js/pkg/ol.ts @@ -0,0 +1,26 @@ +import { Feature, Map as OlMap, View } from "ol"; +import { createEmpty } from "ol/extent"; +import { GeoJSON } from "ol/format"; +import { Point } from "ol/geom"; +import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer"; +import { fromLonLat } from "ol/proj"; +import { OSM, Vector as VectorSource } from "ol/source"; +import { Circle, Fill, Stroke, Style } from "ol/style"; + +export { + View, + OlMap, + TileLayer, + VectorLayer, + OSM, + createEmpty, + VectorSource, + Style, + Stroke, + Fill, + Circle, + fromLonLat, + GeoJSON, + Feature, + Point +}; diff --git a/client/simple/src/js/searxng.head.js b/client/simple/src/js/searxng.head.js deleted file mode 100644 index 895c95f94..000000000 --- a/client/simple/src/js/searxng.head.js +++ /dev/null @@ -1 +0,0 @@ -import "./head/00_init.js"; diff --git a/client/simple/src/js/searxng.js b/client/simple/src/js/searxng.js deleted file mode 100644 index c9a6eea43..000000000 --- a/client/simple/src/js/searxng.js +++ /dev/null @@ -1,7 +0,0 @@ -import "./main/00_toolkit.js"; -import "./main/infinite_scroll.js"; -import "./main/keyboard.js"; -import "./main/mapresult.js"; -import "./main/preferences.js"; -import "./main/results.js"; -import "./main/search.js"; diff --git a/client/simple/src/less/code.less b/client/simple/src/less/code.less index dd0998b23..d83bb1f6f 100644 --- a/client/simple/src/less/code.less +++ b/client/simple/src/less/code.less @@ -24,6 +24,7 @@ &::-moz-selection { background: transparent; /* Gecko Browsers */ } + margin-right: 8px; text-align: right; } diff --git a/client/simple/theme_icons.js b/client/simple/theme_icons.ts similarity index 82% rename from client/simple/theme_icons.js rename to client/simple/theme_icons.ts index a70867aba..5bb06a020 100644 --- a/client/simple/theme_icons.js +++ b/client/simple/theme_icons.ts @@ -4,40 +4,33 @@ import { dirname, resolve } from "node:path"; import { argv } from "node:process"; -import { jinja_svg_sets } from "./tools/jinja_svg_catalog.js"; +import type { Config as SvgoConfig } from "svgo"; +import { type IconSet, type JinjaMacro, jinja_svg_sets } from "./tools/jinja_svg_catalog.ts"; -const HERE = `${dirname(argv[1])}/`; +const HERE = `${dirname(argv[1] || "")}/`; const dest = resolve(HERE, "../../searx/templates/simple/icons.html"); -/** @type import("./tools/jinja_svg_catalog.js").JinjaMacro[] */ -const searxng_jinja_macros = [ +const searxng_jinja_macros: JinjaMacro[] = [ { name: "icon", class: "sxng-icon-set" }, { name: "icon_small", class: "sxng-icon-set-small" }, { name: "icon_big", class: "sxng-icon-set-big" } ]; -const sxng_icon_opts = { +const sxng_icon_opts: SvgoConfig = { multipass: true, plugins: [ - { name: "removeTitle" }, - { name: "removeXMLNS" }, + "removeTitle", + "removeXMLNS", { name: "addAttributesToSVGElement", params: { - attributes: [ - { - "aria-hidden": "true" - } - ] + attributes: [{ "aria-hidden": "true" }] } } ] }; -/** - * @type import("./tools/jinja_svg_catalog.js").IconSet[] - */ -const simple_icons = [ +const simple_icons: IconSet[] = [ { base: resolve(HERE, "node_modules/ionicons/dist/svg"), set: { diff --git a/client/simple/tools/img.js b/client/simple/tools/img.ts similarity index 58% rename from client/simple/tools/img.js rename to client/simple/tools/img.ts index 0b1b96ed8..db4e08645 100644 --- a/client/simple/tools/img.js +++ b/client/simple/tools/img.ts @@ -1,25 +1,26 @@ import fs from "node:fs"; import path from "node:path"; import sharp from "sharp"; +import type { Config } from "svgo"; import { optimize as svgo } from "svgo"; -/** - * @typedef {object} Src2Dest - Mapping of src to dest - * @property {string} src - Name of the source file. - * @property {string} dest - Name of the destination file. - */ +// Mapping of src to dest +export type Src2Dest = { + // Name of the source file. + src: string; + // Name of the destination file. + dest: string; +}; /** * Convert a list of SVG files to PNG. * - * @param {Src2Dest[]} items - Array of SVG files (src: SVG, dest:PNG) to convert. + * @param items - Array of SVG files (src: SVG, dest:PNG) to convert. */ -async function svg2png(items) { +export const svg2png = async (items: Src2Dest[]) => { for (const item of items) { try { - fs.mkdir(path.dirname(item.dest), { recursive: true }, (err) => { - if (err) throw err; - }); + fs.mkdirSync(path.dirname(item.dest), { recursive: true }); const info = await sharp(item.src) .png({ @@ -35,23 +36,22 @@ async function svg2png(items) { throw err; } } -} +}; /** * Optimize SVG images for WEB. * - * @param {Src2Dest[]} items - Array of SVG files (src:SVG, dest:SVG) to optimize. - * @param {import('svgo').Config} svgo_opts - Options passed to svgo. + * @param items - Array of SVG files (src:SVG, dest:SVG) to optimize. + * @param svgo_opts - Options passed to svgo. */ -async function svg2svg(items, svgo_opts) { +export const svg2svg = (items: Src2Dest[], svgo_opts: Config) => { for (const item of items) { try { - fs.mkdir(path.dirname(item.dest), { recursive: true }, (err) => { - if (err) throw err; - }); + fs.mkdirSync(path.dirname(item.dest), { recursive: true }); const raw = fs.readFileSync(item.src, "utf8"); const opt = svgo(raw, svgo_opts); + fs.writeFileSync(item.dest, opt.data); console.log(`[svg2svg] optimized: ${item.dest} -- src: ${item.src}`); } catch (err) { @@ -59,6 +59,4 @@ async function svg2svg(items, svgo_opts) { throw err; } } -} - -export { svg2png, svg2svg }; +}; diff --git a/client/simple/tools/jinja_svg_catalog.js b/client/simple/tools/jinja_svg_catalog.ts similarity index 51% rename from client/simple/tools/jinja_svg_catalog.js rename to client/simple/tools/jinja_svg_catalog.ts index b7b0347b5..1fa1a6676 100644 --- a/client/simple/tools/jinja_svg_catalog.js +++ b/client/simple/tools/jinja_svg_catalog.ts @@ -2,55 +2,56 @@ import fs from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { Edge } from "edge.js"; -import { optimize as svgo } from "svgo"; +import { type Config as SvgoConfig, optimize as svgo } from "svgo"; const __dirname = dirname(fileURLToPath(import.meta.url)); const __jinja_class_placeholder__ = "__jinja_class_placeholder__"; -// -- types +// A set of icons +export type IconSet = { + // Object of SVG icons, where property name is the name of the icon and value is the src of the SVG (relative to base) + set: Record; + // Folder in which the SVG src files are located + base: string; + // svgo options for this set + svgo_opts: SvgoConfig; +}; -/** - * @typedef {object} IconSet - A set of icons - * @property {object} set - Object of SVG icons, where property name is the - * name of the icon and value is the src of the SVG (relative to base). - * @property {string} base - Folder in which the SVG src files are located. - * @property {import("svgo").Config} svgo_opts - svgo options for this set. - */ +// Mapping of icon name to SVG source file +type IconSVG = { + // Name of the icon isource file + name: string; + // Name of the destination file + src: string; + // Options passed to svgo + svgo_opts: SvgoConfig; +}; -/** - * @typedef {object} IconSVG - Mapping of icon name to SVG source file. - * @property {string} name - Name of the icon isource file. - * @property {string} src - Name of the destination file. - * @property {import("svgo").Config} svgo_opts - Options passed to svgo. - */ - -/** - * @typedef {object} JinjaMacro - Arguments to create a jinja macro - * @property {string} name - Name of the jinja macro. - * @property {string} class - SVG's class name (value of XML class attribute) - */ - -// -- functions +// Arguments to create a jinja macro +export type JinjaMacro = { + // Name of the jinja macro + name: string; + // SVG's class name (value of XML class attribute) + class: string; +}; /** * Generate a jinja template with a catalog of SVG icons that can be - * used in in other HTML jinja templates. + * used in other HTML jinja templates. * - * @param {string} dest - filename of the generate jinja template. - * @param {JinjaMacro} macros - Jinja macros to create. - * @param {IconSVG[]} items - Array of SVG items. + * @param dest - filename of the generate jinja template. + * @param macros - Jinja macros to create. + * @param items - Array of SVG items. */ - -function jinja_svg_catalog(dest, macros, items) { - const svg_catalog = {}; +export const jinja_svg_catalog = (dest: string, macros: JinjaMacro[], items: IconSVG[]) => { + const svg_catalog: Record = {}; const edge_template = resolve(__dirname, "jinja_svg_catalog.html.edge"); - items.forEach((item) => { - /** @type {import("svgo").Config} */ - // JSON.stringify & JSON.parse are used to create a deep copy of the - // item.svgo_opts object - const svgo_opts = JSON.parse(JSON.stringify(item.svgo_opts)); - svgo_opts.plugins.push({ + for (const item of items) { + // JSON.stringify & JSON.parse are used to create a deep copy of the item.svgo_opts object + const svgo_opts: SvgoConfig = JSON.parse(JSON.stringify(item.svgo_opts)); + + svgo_opts.plugins?.push({ name: "addClassesToSVGElement", params: { classNames: [__jinja_class_placeholder__] @@ -60,12 +61,13 @@ function jinja_svg_catalog(dest, macros, items) { try { const raw = fs.readFileSync(item.src, "utf8"); const opt = svgo(raw, svgo_opts); + svg_catalog[item.name] = opt.data; } catch (err) { console.error(`ERROR: jinja_svg_catalog processing ${item.name} src: ${item.src} -- ${err}`); throw err; } - }); + } fs.mkdir(dirname(dest), { recursive: true }, (err) => { if (err) throw err; @@ -85,35 +87,34 @@ function jinja_svg_catalog(dest, macros, items) { fs.writeFileSync(dest, jinjatmpl); console.log(`[jinja_svg_catalog] created: ${dest}`); -} +}; /** * Calls jinja_svg_catalog for a collection of icon sets where each set has its * own parameters. * - * @param {string} dest - filename of the generate jinja template. - * @param {JinjaMacro} macros - Jinja macros to create. - * @param {IconSet[]} sets - Array of SVG sets. + * @param dest - filename of the generate jinja template. + * @param macros - Jinja macros to create. + * @param sets - Array of SVG sets. */ -function jinja_svg_sets(dest, macros, sets) { - /** @type IconSVG[] */ - const items = []; - const all = []; +export const jinja_svg_sets = (dest: string, macros: JinjaMacro[], sets: IconSet[]) => { + const items: IconSVG[] = []; + const all: string[] = []; + for (const obj of sets) { for (const [name, file] of Object.entries(obj.set)) { if (all.includes(name)) { throw new Error(`ERROR: ${name} has already been defined`); } + + all.push(name); items.push({ name: name, src: resolve(obj.base, file), svgo_opts: obj.svgo_opts }); } - jinja_svg_catalog(dest, macros, items); } -} -// -- exports - -export { jinja_svg_sets, jinja_svg_catalog }; + jinja_svg_catalog(dest, macros, items); +}; diff --git a/client/simple/tools/plg.js b/client/simple/tools/plg.js deleted file mode 100644 index 74e488fc4..000000000 --- a/client/simple/tools/plg.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Custom vite plugins to build the web-client components of the simple theme. - * - * HINT: - * - * This is an inital implementation for the migration of the build process - * from grunt to vite. For fully support (vite: build & serve) more work is - * needed. - */ - -import { svg2png, svg2svg } from "./img.js"; - -/** - * Vite plugin to convert a list of SVG files to PNG. - * - * @param {import('./img.js').Src2Dest[]} items - Array of SVG files (src: SVG, dest:PNG) to convert. - */ -function plg_svg2png(items) { - return { - name: "searxng-simple-svg2png", - apply: "build", // or 'serve' - async writeBundle() { - await svg2png(items); - } - }; -} - -/** - * Vite plugin to optimize SVG images for WEB. - * - * @param {import('./img.js').Src2Dest[]} items - Array of SVG files (src:SVG, dest:SVG) to optimize. - * @param {import('svgo').Config} svgo_opts - Options passed to svgo. - */ -function plg_svg2svg(items, svgo_opts) { - return { - name: "searxng-simple-svg2png", - apply: "build", // or 'serve' - async writeBundle() { - await svg2svg(items, svgo_opts); - } - }; -} - -export { plg_svg2png, plg_svg2svg }; diff --git a/client/simple/tools/plg.ts b/client/simple/tools/plg.ts new file mode 100644 index 000000000..2db891d4f --- /dev/null +++ b/client/simple/tools/plg.ts @@ -0,0 +1,43 @@ +/** + * Custom vite plugins to build the web-client components of the simple theme. + * + * HINT: + * This is an inital implementation for the migration of the build process + * from grunt to vite. For fully support (vite: build & serve) more work is + * needed. + */ + +import type { Config } from "svgo"; +import type { Plugin } from "vite"; +import { type Src2Dest, svg2png, svg2svg } from "./img.ts"; + +/** + * Vite plugin to convert a list of SVG files to PNG. + * + * @param items - Array of SVG files (src: SVG, dest:PNG) to convert. + */ +export const plg_svg2png = (items: Src2Dest[]): Plugin => { + return { + name: "searxng-simple-svg2png", + apply: "build", + async writeBundle() { + await svg2png(items); + } + }; +}; + +/** + * Vite plugin to optimize SVG images for WEB. + * + * @param items - Array of SVG files (src:SVG, dest:SVG) to optimize. + * @param svgo_opts - Options passed to svgo. + */ +export const plg_svg2svg = (items: Src2Dest[], svgo_opts: Config): Plugin => { + return { + name: "searxng-simple-svg2svg", + apply: "build", + writeBundle() { + svg2svg(items, svgo_opts); + } + }; +}; diff --git a/client/simple/tsconfig.json b/client/simple/tsconfig.json new file mode 100644 index 000000000..dbc3acdf7 --- /dev/null +++ b/client/simple/tsconfig.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig.json", + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2022", + + "allowImportingTsExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "incremental": true, + "noEmit": true, + "resolveJsonModule": true, + "skipLibCheck": true, + + "strict": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "exactOptionalPropertyTypes": false, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": false, + "noUncheckedIndexedAccess": true, + "noUncheckedSideEffectImports": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "verbatimModuleSyntax": true, + + "baseUrl": ".", + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", + "types": ["vite/client"] + }, + "include": ["./"], + "exclude": ["./node_modules/"] +} diff --git a/client/simple/vite.config.js b/client/simple/vite.config.ts similarity index 52% rename from client/simple/vite.config.js rename to client/simple/vite.config.ts index b2d409db9..78ca07a2c 100644 --- a/client/simple/vite.config.js +++ b/client/simple/vite.config.ts @@ -3,104 +3,89 @@ */ import { resolve } from "node:path"; -import { defineConfig } from "vite"; -import { viteStaticCopy } from "vite-plugin-static-copy"; -import { plg_svg2png, plg_svg2svg } from "./tools/plg.js"; +import browserslistToEsbuild from "browserslist-to-esbuild"; +import { browserslistToTargets } from "lightningcss"; +import type { Config } from "svgo"; +import type { UserConfig } from "vite"; +import { browserslist } from "./package.json"; +import { plg_svg2png, plg_svg2svg } from "./tools/plg"; -const ROOT = "../.."; // root of the git reposetory +const ROOT = "../../"; // root of the git repository const PATH = { - dist: resolve(ROOT, "searx/static/themes/simple"), - // dist: resolve(ROOT, "client/simple/dist"), - - src: "src", - modules: "node_modules", - brand: "src/brand", - static: resolve(ROOT, "client/simple/static"), - leaflet: resolve(ROOT, "client/simple/node_modules/leaflet/dist"), - templates: resolve(ROOT, "searx/templates/simple") + brand: "src/brand/", + dist: resolve(ROOT, "searx/static/themes/simple/"), + modules: "node_modules/", + src: "src/", + templates: resolve(ROOT, "searx/templates/simple/") }; -/** - * @type {import('svgo').Config} - */ -const svg2svg_opts = { +const svg2svg_opts: Config = { plugins: [{ name: "preset-default" }, "sortAttrs", "convertStyleToAttrs"] }; -/** - * @type {import('svgo').Config} - */ -const svg2svg_favicon_opts = { +const svg2svg_favicon_opts: Config = { plugins: [{ name: "preset-default" }, "sortAttrs"] }; -export default defineConfig({ - root: PATH.src, +export default { + base: "/static/themes/simple/", + publicDir: "static/", mode: "production", // mode: "development", - // FIXME: missing CCS sourcemaps!! - // see: https://github.com/vitejs/vite/discussions/13845#discussioncomment-11992084 - // - // what I have tried so far (see config below): - // - // - build.sourcemap - // - esbuild.sourcemap - // - css.preprocessorOptions.less.sourceMap - css: { - devSourcemap: true - }, // end: css - build: { - target: ["chrome87", "edge88", "firefox78", "safari14"], + target: browserslistToEsbuild(browserslist), + cssTarget: browserslistToEsbuild(browserslist), manifest: "manifest.json", emptyOutDir: true, assetsDir: "", outDir: PATH.dist, sourcemap: true, - minify: "esbuild", - cssMinify: "esbuild", rollupOptions: { input: { // build CSS files - "css/searxng.min.css": `${PATH.src}/less/style-ltr.less`, - "css/searxng-rtl.min.css": `${PATH.src}/less/style-rtl.less`, - "css/rss.min.css": `${PATH.src}/less/rss.less`, + "searxng-ltr.min.css": `${PATH.src}/less/style-ltr.less`, + "searxng-rtl.min.css": `${PATH.src}/less/style-rtl.less`, + "rss.min.css": `${PATH.src}/less/rss.less`, - // build JS files - "js/searxng.head.min": `${PATH.src}/js/searxng.head.js`, - "js/searxng.min": `${PATH.src}/js/searxng.js` + // build script files + "searxng.min": `${PATH.src}/js/main/index.ts`, + + // ol + "ol.min": `${PATH.src}/js/pkg/ol.ts`, + "ol.min.css": `${PATH.modules}/ol/ol.css` }, // file naming conventions / pathnames are relative to outDir (PATH.dist) output: { - entryFileNames: "[name].js", - chunkFileNames: "[name].js", - assetFileNames: "[name].[ext]" - // Vite does not support "rollupOptions.output.sourcemap". - // Please use "build.sourcemap" instead. - // sourcemap: true, + entryFileNames: "js/[name].js", + chunkFileNames: "js/[name].js", + assetFileNames: ({ names }) => { + const [name] = names; + + const extension = name?.split(".").pop(); + switch (extension) { + case "css": + return `css/[name][extname]`; + case "js": + return `js/[name][extname]`; + case "png": + case "svg": + return `img/[name][extname]`; + default: + console.warn("Unknown asset:", name); + return `[name][extname]`; + } + } } } }, // end: build plugins: [ - // Leaflet - - viteStaticCopy({ - targets: [ - { src: `${PATH.leaflet}/leaflet.{js,js.map}`, dest: `${PATH.dist}/js` }, - { src: `${PATH.leaflet}/images/*.png`, dest: `${PATH.dist}/css/images/` }, - { src: `${PATH.leaflet}/*.{css,css.map}`, dest: `${PATH.dist}/css` }, - { src: `${PATH.static}/**/*`, dest: PATH.dist } - ] - }), - // -- svg images - plg_svg2svg( [ { src: `${PATH.src}/svg/empty_favicon.svg`, dest: `${PATH.dist}/img/empty_favicon.svg` }, @@ -111,7 +96,6 @@ export default defineConfig({ ), // SearXNG brand (static) - plg_svg2png([ { src: `${PATH.brand}/searxng-wordmark.svg`, dest: `${PATH.dist}/img/favicon.png` }, { src: `${PATH.brand}/searxng.svg`, dest: `${PATH.dist}/img/searxng.png` } @@ -137,5 +121,25 @@ export default defineConfig({ [{ src: `${PATH.brand}/searxng-wordmark.svg`, dest: `${PATH.templates}/searxng-wordmark.min.svg` }], svg2svg_opts ) - ] // end: plugins -}); + ], // end: plugins + + // FIXME: missing CCS sourcemaps!! + // see: https://github.com/vitejs/vite/discussions/13845#discussioncomment-11992084 + // + // what I have tried so far (see config below): + // + // - build.sourcemap + // - esbuild.sourcemap + // - css.preprocessorOptions.less.sourceMap + css: { + transformer: "lightningcss", + lightningcss: { + targets: browserslistToTargets(browserslist) + }, + devSourcemap: true + }, // end: css + + experimental: { + enableNativePlugin: true + } // end: experimental +} satisfies UserConfig; diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst index ea7ddecd8..fa81f3d23 100644 --- a/docs/dev/makefile.rst +++ b/docs/dev/makefile.rst @@ -103,8 +103,8 @@ Node.js environment (``make node.env``) Node.js_ version {{version.node}} or higher is required to build the themes. If the requirement is not met, the build chain uses nvm_ (Node Version - Manager) to install latest LTS of Node.js_ locally: there is no need to - install nvm_ or npm_ on your system. + Manager) to install Node.js_ locally: there is no need to install + nvm_ or npm_ on your system. To install NVM_ and Node.js_ in once you can use :ref:`make nvm.nodejs`. @@ -150,7 +150,7 @@ setup. ``make nvm.nodejs`` ------------------- -Install latest Node.js_ LTS locally (uses nvm_):: +Install latest Node.js_ locally (uses nvm_):: $ make nvm.nodejs INFO: install (update) NVM at /share/searxng/.nvm diff --git a/package.json b/package.json index c26fa2742..790ab8648 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "searxng.org/devtools", + "type": "module", "dependencies": { "pyright": "^1.1.403" }, diff --git a/searx/templates/simple/base.html b/searx/templates/simple/base.html index deac295b7..2170ccea5 100644 --- a/searx/templates/simple/base.html +++ b/searx/templates/simple/base.html @@ -8,21 +8,21 @@ - - {% block title %}{% endblock %}{{ instance_name }} {% block meta %}{% endblock %} {% if rtl %} {% else %} - + {% endif %} {% if get_setting('server.limiter') or get_setting('server.public_instance') %} {% endif %} - - - + {% block head %} {% endblock %} @@ -82,8 +82,6 @@ {% endfor %}

- - - + diff --git a/utils/lib_nvm.sh b/utils/lib_nvm.sh index 04a9ddbbf..8351e6d22 100755 --- a/utils/lib_nvm.sh +++ b/utils/lib_nvm.sh @@ -94,7 +94,7 @@ nvm.: use nvm (without dot) to execute nvm commands directly install : install NVM locally at $(git rev-parse --show-toplevel)/${NVM_LOCAL_FOLDER} clean : remove NVM installation status : prompt some status information about nvm & node - nodejs : install Node.js latest LTS + nodejs : install latest Node.js cmd ... : run command ... in NVM environment bash : start bash interpreter with NVM environment sourced EOF @@ -163,7 +163,7 @@ nvm.status() { info_msg "NVM is installed at ${NVM_DIR}" else warn_msg "NVM is not installed" - info_msg "to install NVM and Node.js (LTS) use: ${main_cmd} nvm.nodejs" + info_msg "to install NVM and Node.js use: ${main_cmd} nvm.nodejs" fi } diff --git a/utils/lib_sxng_node.sh b/utils/lib_sxng_node.sh index c072dd40d..79a1f0c1f 100755 --- a/utils/lib_sxng_node.sh +++ b/utils/lib_sxng_node.sh @@ -4,7 +4,7 @@ declare _Blue declare _creset -export NODE_MINIMUM_VERSION="18.17.0" +export NODE_MINIMUM_VERSION="23.6.0" node.help() { cat <