37 Commits

Author SHA1 Message Date
dependabot[bot]
d0bd7bf2db [upd] pypi: Bump the minor group with 2 updates
Bumps the minor group with 2 updates: [selenium](https://github.com/SeleniumHQ/Selenium) and [basedpyright](https://github.com/detachhead/basedpyright).


Updates `selenium` from 4.38.0 to 4.39.0
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/compare/selenium-4.38.0...selenium-4.39.0)

Updates `basedpyright` from 1.35.0 to 1.36.1
- [Release notes](https://github.com/detachhead/basedpyright/releases)
- [Commits](https://github.com/detachhead/basedpyright/compare/v1.35.0...v1.36.1)

---
updated-dependencies:
- dependency-name: selenium
  dependency-version: 4.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: basedpyright
  dependency-version: 1.36.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-19 07:03:16 +00:00
Guanzhong Chen
896863802e [fix] engine: brave - BrotliDecoderDecompressStream encoding issue (#5572)
For some reason, I keep getting this error from the brave engine:

    httpx.DecodingError: BrotliDecoderDecompressStream failed while processing the stream

Forcing the server to use either gzip or deflate fixes this issue.

This makes the brave engine work when the server seems to be encoding brotli incorrectly, or at least in a way incompatible with certain installs.

Related:

- https://github.com/searxng/searxng/pull/1787
- https://github.com/searxng/searxng/pull/5536
2025-12-17 09:39:03 +01:00
searxng-bot
920b40253c [l10n] update translations from Weblate
e23dc20f7 - 2025-12-11 - SomeTr <sometr@noreply.codeberg.org>
eb67f948f - 2025-12-11 - artnay <artnay@noreply.codeberg.org>
d4fdfc449 - 2025-12-10 - SomeTr <sometr@noreply.codeberg.org>
0f02ac7cd - 2025-12-10 - IcewindX <icewindx@noreply.codeberg.org>
533ae3947 - 2025-12-11 - return42 <return42@noreply.codeberg.org>
19fe65dc7 - 2025-12-11 - return42 <return42@noreply.codeberg.org>
bca557cea - 2025-12-09 - Hangry-Studios <hangry-studios@noreply.codeberg.org>
e43e9a299 - 2025-12-10 - Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
c98083cef - 2025-12-09 - eudemo <eudemo@noreply.codeberg.org>
316225017 - 2025-12-08 - aindriu80 <aindriu80@noreply.codeberg.org>
1b827e5a4 - 2025-12-08 - Aadniz <aadniz@noreply.codeberg.org>
68e760f9b - 2025-12-09 - kratos <makesocialfoss32@keemail.me>
99945ac31 - 2025-12-07 - lucasmz.dev <lucasmz.dev@noreply.codeberg.org>
56602eb75 - 2025-12-07 - Fjuro <fjuro@alius.cz>
df092e811 - 2025-12-06 - c2qd <c2qd@noreply.codeberg.org>
12c25cd85 - 2025-12-06 - Outbreak2096 <outbreak2096@noreply.codeberg.org>
081243428 - 2025-12-05 - SomeTr <sometr@noreply.codeberg.org>
66362c02d - 2025-12-06 - ghose <ghose@noreply.codeberg.org>
2025-12-12 20:23:43 +00:00
dependabot[bot]
07440e3332 [upd] web-client (simple): Bump the minor group
Bumps the minor group in /client/simple with 2 updates: [sort-package-json](https://github.com/keithamus/sort-package-json) and [vite-bundle-analyzer](https://github.com/nonzzz/vite-bundle-analyzer).

Updates `sort-package-json` from 3.5.0 to 3.5.1
- [Release notes](https://github.com/keithamus/sort-package-json/releases)
- [Commits](https://github.com/keithamus/sort-package-json/compare/v3.5.0...v3.5.1)

Updates `vite-bundle-analyzer` from 1.2.3 to 1.3.1
- [Release notes](https://github.com/nonzzz/vite-bundle-analyzer/releases)
- [Changelog](https://github.com/nonzzz/vite-bundle-analyzer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nonzzz/vite-bundle-analyzer/compare/v1.2.3...v1.3.1)

---
updated-dependencies:
- dependency-name: sort-package-json
  dependency-version: 3.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: vite-bundle-analyzer
  dependency-version: 1.3.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 20:22:48 +00:00
dependabot[bot]
1827dfc071 [upd] web-client (simple): Bump @types/node in /client/simple
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.1 to 25.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 20:13:11 +00:00
dependabot[bot]
c46aecd4e3 [upd] web-client (simple): Bump vite in /client/simple
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.0-beta.0 to 8.0.0-beta.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.0-beta.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.0-beta.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 19:59:31 +00:00
dependabot[bot]
21bf8a6973 [upd] github-actions: Bump peter-evans/create-pull-request
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.9 to 8.0.0.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](84ae59a2cd...98357b18bf)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 19:56:18 +00:00
dependabot[bot]
f5475ba782 [upd] github-actions: Bump JamesIves/github-pages-deploy-action
Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.7.4 to 4.7.6.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](4a3abc783e...9d877eea73)

---
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-version: 4.7.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 19:52:52 +00:00
dependabot[bot]
265f15498c [upd] github-actions: Bump github/codeql-action from 4.31.6 to 4.31.7
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.6 to 4.31.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](fe4161a26a...cf1bb45a27)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 19:51:40 +00:00
dependabot[bot]
666409ec7e [upd] github-actions: Bump actions/cache from 4.3.0 to 5.0.0
Bumps [actions/cache](https://github.com/actions/cache) from 4.3.0 to 5.0.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0057852bfa...a783357455)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 19:51:17 +00:00
Viktor
b719d559b6 [feat] marginalia: switch to the new, improved API version 2025-12-09 18:18:37 +01:00
Austin-Olacsi
9d3ec9a2a2 [feat] pixiv engine: add filter for AI generated images 2025-12-07 23:34:16 +01:00
Ivan Gabaldon
74ec225ad1 [fix] themes: clear search input (#5540)
* [fix] themes: clear search input

Closes https://github.com/searxng/searxng/issues/5539

* [fix] themes: rebuild static
2025-12-07 13:26:01 +00:00
Markus Heiser
b5a1a092f1 [debug] partial revert of 5e0e1c6b3 (#5535)
Issue #5490 was caused by a regression of GH action in v6.0.0, updating to
v6.0.1 [2] fixed our workflow and debug messages are no longer needed.

[1] https://github.com/searxng/searxng/issues/5490#issuecomment-3616230451
[2] https://github.com/searxng/searxng/pull/5530

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2025-12-06 09:15:56 +01:00
dependabot[bot]
ddc6d68114 [upd] pypi: Bump the minor group with 2 updates (#5527)
Bumps the minor group with 2 updates: [pylint](https://github.com/pylint-dev/pylint) and [basedpyright](https://github.com/detachhead/basedpyright).


Updates `pylint` from 4.0.3 to 4.0.4
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v4.0.3...v4.0.4)

Updates `basedpyright` from 1.34.0 to 1.35.0
- [Release notes](https://github.com/detachhead/basedpyright/releases)
- [Commits](https://github.com/detachhead/basedpyright/compare/v1.34.0...v1.35.0)
2025-12-05 15:39:35 +01:00
github-actions[bot]
32eb84d6d3 [l10n] update translations from Weblate (#5532) 2025-12-05 15:38:35 +01:00
Ivan Gabaldon
da6c635ea2 [upd] themes: update vite 2025-12-05 11:14:26 +00:00
dependabot[bot]
e34c356e64 [upd] web-client (simple): Bump the minor group
Bumps the minor group in /client/simple with 2 updates: [browserslist](https://github.com/browserslist/browserslist) and [mathjs](https://github.com/josdejong/mathjs).

Updates `browserslist` from 4.28.0 to 4.28.1
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.28.0...4.28.1)

Updates `mathjs` from 15.0.0 to 15.1.0
- [Changelog](https://github.com/josdejong/mathjs/blob/develop/HISTORY.md)
- [Commits](https://github.com/josdejong/mathjs/compare/v15.0.0...v15.1.0)

---
updated-dependencies:
- dependency-name: browserslist
  dependency-version: 4.28.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: mathjs
  dependency-version: 15.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-05 10:42:18 +00:00
dependabot[bot]
7017393647 [upd] github-actions: Bump actions/setup-node from 6.0.0 to 6.1.0
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](2028fbc5c2...395ad32622)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-05 10:26:25 +00:00
dependabot[bot]
aa49f5b933 [upd] github-actions: Bump github/codeql-action from 4.31.5 to 4.31.6
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.5 to 4.31.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](fdbfb4d275...fe4161a26a)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-05 10:25:37 +00:00
dependabot[bot]
3f91ac47e6 [upd] github-actions: Bump actions/checkout from 6.0.0 to 6.0.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](1af3b93b68...8e8c483db8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-05 10:14:41 +00:00
Markus Heiser
8c631b92ce [mod] setup.py package_data - use recursive globs for package_data
To test this patch build a python wheel::

    $ make clean py.build

and llok out if you are missing any files in the wheel::

    $ unzip -l dist/searxng-*-py3-none-any.whl

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2025-12-04 15:04:36 +01:00
leaty
0ebac144f5 [fix] py: sxng wheel build still broken
Directories chunk and img were not included

Signed-off-by: leaty <dev@leaty.net>
2025-12-04 15:04:36 +01:00
Markus Heiser
5e0e1c6b31 [debug] CI - add debug to trace issue #5490 (#5519)
The error only occurs in the CI action, which is why we need to commit the coded
debug.  As soon as the bug is identified and fixed, this commit can be reverted
/ the ``set -x`` can be removed from the code.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2025-12-04 14:07:22 +01:00
Markus Heiser
3c7545c6ce [fix] plugin unit-converter - remove leftovers (#5517)
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2025-12-04 07:51:48 +01:00
Austin-Olacsi
aba839195b [fix] findthatmeme: hardening the response against KeyErrors (#5516) 2025-12-04 07:13:05 +01:00
Bnyro
1f6ea41272 [fix] mojeek: first search page is rate-limited
Mojeek blocks all requests where the page offset parameter `s`
is set to 0, hence we may not set it for fetching the first results page.
2025-12-03 17:03:27 +01:00
Ivan Gabaldon
5450d22796 [fix] py: sxng wheel build broken (#5510)
Include everything inside `static/themes/` on SearXNG wheel creation.

```
2025-12-03 10:40:43,509 INFO🛞 adding
'searx/static/themes/simple/manifest.json' 2025-12-03 10:40:43,509
INFO🛞 adding 'searx/static/themes/simple/sxng-core.min.js'
2025-12-03 10:40:43,509 INFO🛞 adding
'searx/static/themes/simple/sxng-core.min.js.map' 2025-12-03
10:40:43,510 INFO🛞 adding
'searx/static/themes/simple/sxng-ltr.min.css' 2025-12-03 10:40:43,510
INFO🛞 adding 'searx/static/themes/simple/sxng-rss.min.css'
2025-12-03 10:40:43,511 INFO🛞 adding
'searx/static/themes/simple/sxng-rtl.min.css' 2025-12-03 10:40:43,511
INFO🛞 adding 'searx/static/themes/simple/chunk/13gvpunf.min.js'
2025-12-03 10:40:43,511 INFO🛞 adding
'searx/static/themes/simple/chunk/13gvpunf.min.js.map' 2025-12-03
10:40:43,516 INFO🛞 adding
'searx/static/themes/simple/chunk/BAcZkB_P.min.js' 2025-12-03
10:40:43,548 INFO🛞 adding
'searx/static/themes/simple/chunk/BAcZkB_P.min.js.map' 2025-12-03
10:40:43,551 INFO🛞 adding
'searx/static/themes/simple/chunk/CHkLfdMs.min.js' 2025-12-03
10:40:43,561 INFO🛞 adding
'searx/static/themes/simple/chunk/CHkLfdMs.min.js.map' 2025-12-03
10:40:43,562 INFO🛞 adding
'searx/static/themes/simple/chunk/CyyZ9XJS.min.js' 2025-12-03
10:40:43,562 INFO🛞 adding
'searx/static/themes/simple/chunk/CyyZ9XJS.min.js.map' 2025-12-03
10:40:43,562 INFO🛞 adding
'searx/static/themes/simple/chunk/DBO1tjH7.min.js' 2025-12-03
10:40:43,562 INFO🛞 adding
'searx/static/themes/simple/chunk/DBO1tjH7.min.js.map' 2025-12-03
10:40:43,562 INFO🛞 adding
'searx/static/themes/simple/chunk/Db5v-hxx.min.js' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/Db5v-hxx.min.js.map' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/DxJxX49r.min.js' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/DxJxX49r.min.js.map' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/KPZlR0ib.min.js' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/KPZlR0ib.min.js.map' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/Q2SRo2ED.min.js' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/Q2SRo2ED.min.js.map' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/gZqIRpW1.min.js' 2025-12-03
10:40:43,563 INFO🛞 adding
'searx/static/themes/simple/chunk/gZqIRpW1.min.js.map' 2025-12-03
10:40:43,564 INFO🛞 adding
'searx/static/themes/simple/img/empty_favicon.svg' 2025-12-03
10:40:43,564 INFO🛞 adding
'searx/static/themes/simple/img/favicon.png' 2025-12-03 10:40:43,564
INFO🛞 adding 'searx/static/themes/simple/img/favicon.svg'
2025-12-03 10:40:43,564 INFO🛞 adding
'searx/static/themes/simple/img/img_load_error.svg' 2025-12-03
10:40:43,564 INFO🛞 adding
'searx/static/themes/simple/img/searxng.png' 2025-12-03 10:40:43,564
INFO🛞 adding 'searx/static/themes/simple/img/searxng.svg'
2025-12-03 10:40:43,564 INFO🛞 adding
'searx/static/themes/simple/img/select-dark.svg' 2025-12-03 10:40:43,564
INFO🛞 adding 'searx/static/themes/simple/img/select-light.svg'
```
2025-12-03 11:21:18 +00:00
Bnyro
1174fde1f3 [feat] engines: add lucide icons (#5503) 2025-12-03 09:57:42 +01:00
Ivan Gabaldon
fb089ae297 [mod] client/simple: client plugins (#5406)
* [mod] client/simple: client plugins

Defines a new interface for client side *"plugins"* that coexist with server
side plugin system. Each plugin (e.g., `InfiniteScroll`) extends the base
`ts Plugin`. Client side plugins are independent and lazy‑loaded via `router.ts`
when their `load()` conditions are met. On each navigation request, all
applicable plugins are instanced.

Since these are client side plugins, we can only invoke them once DOM is fully
loaded. E.g. `Calculator` will not render a new `answer` block until fully
loaded and executed.

For some plugins, we might want to handle its availability in `settings.yml`
and toggle in UI, like we do for server side plugins. In that case, we extend
`py Plugin` instancing only the information and then checking client side if
[`settings.plugins`](1ad832b1dc/client/simple/src/js/toolkit.ts (L134))
array has the plugin id.

* [mod] client/simple: rebuild static
2025-12-02 10:18:00 +00:00
Bnyro
ab8224c939 [fix] brave: content description also contains website URL (#5502)
there are other classes like 'site-name-content' we don't want to match,
however only using contains(@class, 'content') would e.g. also match `site-name-content`
thus, we explicitly also require the spaces as class separator
2025-12-01 15:19:06 +01:00
github-actions[bot]
c954e71f87 [data] update searx.data - update_engine_descriptions.py (#5496) 2025-11-29 16:04:34 +01:00
Ivan Gabaldon
cbc04a839a [fix] py: missing module sniffio 2025-11-29 14:56:30 +00:00
searxng-bot
cb4a5abc8c [data] update searx.data - update_currencies.py 2025-11-29 14:54:09 +00:00
searxng-bot
07ff6e3ccc [data] update searx.data - update_wikidata_units.py 2025-11-29 09:00:05 +00:00
searxng-bot
cdaab944b4 [data] update searx.data - update_firefox_version.py 2025-11-29 08:58:56 +00:00
searxng-bot
6ecf32fd4a [data] update searx.data - update_ahmia_blacklist.py 2025-11-29 08:58:26 +00:00
232 changed files with 5110 additions and 3710 deletions

View File

@@ -29,12 +29,12 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"

View File

@@ -83,13 +83,13 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
fetch-depth: "0"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
@@ -100,7 +100,7 @@ jobs:
run: echo "date=$(date +'%Y%m%d')" >>$GITHUB_OUTPUT
- name: Setup cache container
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: |
@@ -145,7 +145,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
@@ -179,7 +179,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"

View File

@@ -45,12 +45,12 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
@@ -64,7 +64,7 @@ jobs:
- name: Create PR
id: cpr
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
author: "searxng-bot <searxng-bot@users.noreply.github.com>"
committer: "searxng-bot <searxng-bot@users.noreply.github.com>"

View File

@@ -37,13 +37,13 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
fetch-depth: "0"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
@@ -57,7 +57,7 @@ jobs:
- if: github.ref_name == 'master'
name: Release
uses: JamesIves/github-pages-deploy-action@4a3abc783e1a24aeb44c16e869ad83caf6b4cc23 # v4.7.4
uses: JamesIves/github-pages-deploy-action@9d877eea73427180ae43cf98e8914934fe157a1a # v4.7.6
with:
folder: "dist/docs"
branch: "gh-pages"

View File

@@ -40,12 +40,12 @@ jobs:
python-version: "${{ matrix.python-version }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ matrix.python-version }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ matrix.python-version }}-${{ runner.arch }}-"
@@ -67,23 +67,23 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
- name: Setup Node.js
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version-file: "./.nvmrc"
- name: Setup cache Node.js
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "nodejs-${{ runner.arch }}-${{ hashFiles('./.nvmrc', './package.json') }}"
path: "./client/simple/node_modules/"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"

View File

@@ -40,13 +40,13 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
fetch-depth: "0"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
@@ -87,13 +87,13 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
fetch-depth: "0"
- name: Setup cache Python
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
@@ -117,7 +117,7 @@ jobs:
- name: Create PR
id: cpr
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
author: "searxng-bot <searxng-bot@users.noreply.github.com>"
committer: "searxng-bot <searxng-bot@users.noreply.github.com>"

View File

@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: "false"
@@ -41,6 +41,6 @@ jobs:
write-comment: "false"
- name: Upload SARIFs
uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
with:
sarif_file: "./scout.sarif"

2
.nvmrc
View File

@@ -1 +1 @@
24
25

View File

@@ -16,21 +16,21 @@
},
"devDependencies": {
"@biomejs/biome": "2.3.8",
"@types/node": "~24.10.1",
"browserslist": "~4.28.0",
"@types/node": "~25.0.1",
"browserslist": "~4.28.1",
"browserslist-to-esbuild": "~2.1.1",
"edge.js": "~6.3.0",
"less": "~4.4.2",
"lightningcss": "~1.30.2",
"mathjs": "~15.1.0",
"sharp": "~0.34.5",
"sort-package-json": "~3.5.0",
"sort-package-json": "~3.5.1",
"stylelint": "~16.26.0",
"stylelint-config-standard-less": "~3.0.1",
"stylelint-prettier": "~5.0.3",
"svgo": "~4.0.0",
"typescript": "~5.9.3",
"vite": "npm:rolldown-vite@7.2.7",
"vite-bundle-analyzer": "~1.2.3"
"vite": "8.0.0-beta.2",
"vite-bundle-analyzer": "~1.3.1"
}
},
"node_modules/@babel/code-frame": {
@@ -58,6 +58,16 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@biomejs/biome": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.8.tgz",
@@ -269,6 +279,26 @@
"@csstools/css-tokenizer": "^3.0.4"
}
},
"node_modules/@csstools/css-syntax-patches-for-csstree": {
"version": "1.0.21",
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.21.tgz",
"integrity": "sha512-plP8N8zKfEZ26figX4Nvajx8DuzfuRpLTqglQ5d0chfnt35Qt3X+m6ASZ+rG0D0kxe/upDVNwSIVJP5n4FuNfw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"engines": {
"node": ">=18"
}
},
"node_modules/@csstools/css-tokenizer": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
@@ -896,26 +926,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@lukeed/ms": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
"integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz",
"integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz",
"integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.5.0",
"@emnapi/runtime": "^1.5.0",
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1",
"@tybys/wasm-util": "^0.10.1"
}
},
@@ -958,9 +978,9 @@
}
},
"node_modules/@oxc-project/runtime": {
"version": "0.98.0",
"resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.98.0.tgz",
"integrity": "sha512-F0ldlBv2orG2YqNL0w77deq9yCaO4zEHbanGnW/jaJxGBR8ImekvZb8x42zAHvdzr8J76psibijvHtXfSjbEIQ==",
"version": "0.102.0",
"resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.102.0.tgz",
"integrity": "sha512-vEDGxVIeeO+u5XCHD5+iSzWwC3DgRpEaf3lPZETC+6GnoRKHaxbxV6XqpbOhiY423RVkAbBEtfetfrjJjPWByA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -968,9 +988,9 @@
}
},
"node_modules/@oxc-project/types": {
"version": "0.98.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.98.0.tgz",
"integrity": "sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==",
"version": "0.102.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.102.0.tgz",
"integrity": "sha512-8Skrw405g+/UJPKWJ1twIk3BIH2nXdiVlVNtYT23AXVwpsd79es4K+KYt06Fbnkc5BaTvk/COT2JuCLYdwnCdA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -984,9 +1004,9 @@
"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==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
"integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
"dev": true,
"license": "MIT"
},
@@ -1018,20 +1038,16 @@
}
},
"node_modules/@poppinss/string": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@poppinss/string/-/string-1.7.0.tgz",
"integrity": "sha512-IuCtWaUwmJeAdby0n1a5cTYsBLe7fPymdc4oNTTl1b6l+Ok+14XpSX0ILOEU6UtZ9D2XI3f4TVUh4Titkk1xgw==",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@poppinss/string/-/string-1.7.1.tgz",
"integrity": "sha512-OrLzv/nGDU6l6dLXIQHe8nbNSWWfuSbpB/TW5nRpZFf49CLuQlIHlSPN9IdSUv2vG+59yGM6LoibsaHn8B8mDw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@lukeed/ms": "^2.0.2",
"@types/bytes": "^3.1.5",
"@types/pluralize": "^0.0.33",
"bytes": "^3.1.2",
"case-anything": "^3.1.2",
"pluralize": "^8.0.0",
"slugify": "^1.6.6",
"truncatise": "^0.0.8"
"slugify": "^1.6.6"
}
},
"node_modules/@poppinss/utils": {
@@ -1053,9 +1069,9 @@
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.51.tgz",
"integrity": "sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.54.tgz",
"integrity": "sha512-zZRx/ur3Fai3fxiEmVp48+6GCBR48PRWJR1X3TTMn9yiq2bBHlYPgBaQtDOYWXv5H3J5dXujeTyGnuoY+kdGCg==",
"cpu": [
"arm64"
],
@@ -1070,9 +1086,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.51.tgz",
"integrity": "sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.54.tgz",
"integrity": "sha512-zMyFEJmbIs91x22HAA/eUvmZHgjX8tGsD3TJ+WC9aY4bCdl3w84H9vMZmChSHAF1dYvGNH4KQDI2IubeZaCYtg==",
"cpu": [
"arm64"
],
@@ -1087,9 +1103,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.51.tgz",
"integrity": "sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.54.tgz",
"integrity": "sha512-Ex7QttdaVnEpmE/zroUT5Qm10e2+Vjd9q0LX9eXm59SitxDODMpC8GI1Rct5RrLf4GLU4DzdXBj6DGzuR+6g6w==",
"cpu": [
"x64"
],
@@ -1104,9 +1120,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.51.tgz",
"integrity": "sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.54.tgz",
"integrity": "sha512-E1XO10ryM/Vxw3Q1wvs9s2mSpVBfbHtzkbJcdu26qh17ZmVwNWLiIoqEcbkXm028YwkReG4Gd2gCZ3NxgTQ28Q==",
"cpu": [
"x64"
],
@@ -1121,9 +1137,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.51.tgz",
"integrity": "sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.54.tgz",
"integrity": "sha512-oS73Uks8jczQR9pg0Bj718vap/x71exyJ5yuxu4X5V4MhwRQnky7ANSPm6ARUfraxOqt49IBfcMeGnw2rTSqdA==",
"cpu": [
"arm"
],
@@ -1138,9 +1154,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.51.tgz",
"integrity": "sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.54.tgz",
"integrity": "sha512-pY8N2X5C+/ZQcy0eRdfOzOP//OFngP1TaIqDjFwfBPws2UNavKS8SpxhPEgUaYIaT0keVBd/TB+eVy9z+CIOtw==",
"cpu": [
"arm64"
],
@@ -1155,9 +1171,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.51.tgz",
"integrity": "sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.54.tgz",
"integrity": "sha512-cgTooAFm2MUmFriB7IYaWBNyqrGlRPKG+yaK2rGFl2rcdOcO24urY4p3eyB0ogqsRLvJbIxwjjYiWiIP7Eo1Cw==",
"cpu": [
"arm64"
],
@@ -1172,9 +1188,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.51.tgz",
"integrity": "sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.54.tgz",
"integrity": "sha512-nGyLT1Qau0W+kEL44V2jhHmvfS3wyJW08E4WEu2E6NuIy+uChKN1X0aoxzFIDi2owDsYaZYez/98/f268EupIQ==",
"cpu": [
"x64"
],
@@ -1189,9 +1205,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.51.tgz",
"integrity": "sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.54.tgz",
"integrity": "sha512-KH374P0TUjDXssROT/orvzaWrzGOptD13PTrltgKwbDprJTMknoLiYsOD6Ttz92O2VuAcCtFuJ1xbyFM2Uo/Xg==",
"cpu": [
"x64"
],
@@ -1206,9 +1222,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.51.tgz",
"integrity": "sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.54.tgz",
"integrity": "sha512-oMAVO4wbfAbhpBxPsSp8R7ntL2DchpNfO+tGhN8/sI9jsbYwOv78uIW1fTwOBslhjTVFltGJ+l23mubNQcYNaQ==",
"cpu": [
"arm64"
],
@@ -1223,9 +1239,9 @@
}
},
"node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.51.tgz",
"integrity": "sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.54.tgz",
"integrity": "sha512-MYY/FmY+HehHiQkNx04W5oLy/Fqd1hXYqZmmorSDXvAHnxMbSgmdFicKsSYOg/sVGHBMEP1tTn6kV5sWrS45rA==",
"cpu": [
"wasm32"
],
@@ -1233,16 +1249,16 @@
"license": "MIT",
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^1.0.7"
"@napi-rs/wasm-runtime": "^1.1.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.51.tgz",
"integrity": "sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.54.tgz",
"integrity": "sha512-66o3uKxUmcYskT9exskxs3OVduXf5x0ndlMkYOjSpBgqzhLtkub136yDvZkNT1OkNDET0odSwcU7aWdpnwzAyg==",
"cpu": [
"arm64"
],
@@ -1256,27 +1272,10 @@
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-ia32-msvc": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.51.tgz",
"integrity": "sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.51.tgz",
"integrity": "sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.54.tgz",
"integrity": "sha512-FbbbrboChLBXfeEsOfaypBGqzbdJ/CcSA2BPLCggojnIHy58Jo+AXV7HATY8opZk7194rRbokIT8AfPJtZAWtg==",
"cpu": [
"x64"
],
@@ -1291,9 +1290,9 @@
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.51.tgz",
"integrity": "sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.54.tgz",
"integrity": "sha512-AHgcZ+w7RIRZ65ihSQL8YuoKcpD9Scew4sEeP1BBUT9QdTo6KjwHrZZXjID6nL10fhKessCH6OPany2QKwAwTQ==",
"dev": true,
"license": "MIT"
},
@@ -1402,9 +1401,9 @@
]
},
"node_modules/@stencil/core": {
"version": "4.38.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.38.3.tgz",
"integrity": "sha512-rSDzGUfi58X8K79ySjlM6KlY+mq7D+ittzgNAdYHcsXHc70sBpdatFhnbOg25uVDiMf7xRAH9slP38pPdXnZOQ==",
"version": "4.39.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.39.0.tgz",
"integrity": "sha512-wLASFh5wecnbxY+9pEPd6bl7AZJksLmuiBd0ShvkJ0v/N1nL4HNSw/jq2+TzgFE1+XqCUhKPDeVXFpZf1uuRDw==",
"license": "MIT",
"bin": {
"stencil": "bin/stencil"
@@ -1435,17 +1434,10 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/bytes": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.5.tgz",
"integrity": "sha512-VgZkrJckypj85YxEsEavcMmmSOIzkUHqWmM4CCyia5dc54YwsXzJ5uT4fYxBQNEXx+oF1krlhgCbvfubXqZYsQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"version": "25.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.1.tgz",
"integrity": "sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1567,9 +1559,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.8.30",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz",
"integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==",
"version": "2.9.7",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz",
"integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -1597,9 +1589,9 @@
}
},
"node_modules/browserslist": {
"version": "4.28.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
"integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
@@ -1618,11 +1610,11 @@
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.25",
"caniuse-lite": "^1.0.30001754",
"electron-to-chromium": "^1.5.249",
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.1.4"
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
@@ -1650,16 +1642,6 @@
"browserslist": "*"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/cacheable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.0.tgz",
@@ -1685,9 +1667,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001756",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz",
"integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==",
"version": "1.0.30001760",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
"integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
"dev": true,
"funding": [
{
@@ -1762,6 +1744,20 @@
"node": ">=16"
}
},
"node_modules/complex.js": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz",
"integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
@@ -1923,6 +1919,13 @@
}
}
},
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"dev": true,
"license": "MIT"
},
"node_modules/detect-indent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz",
@@ -2102,9 +2105,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.259",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz",
"integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==",
"version": "1.5.267",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
"dev": true,
"license": "ISC"
},
@@ -2185,6 +2188,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/escape-latex": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2305,6 +2315,20 @@
"node": ">=8"
}
},
"node_modules/fraction.js": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/fs-readdir-recursive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
@@ -2454,9 +2478,9 @@
}
},
"node_modules/hashery": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hashery/-/hashery-1.2.0.tgz",
"integrity": "sha512-43XJKpwle72Ik5Zpam7MuzRWyNdwwdf6XHlh8wCj2PggvWf+v/Dm5B0dxGZOmddidgeO6Ofu9As/o231Ti/9PA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/hashery/-/hashery-1.3.0.tgz",
"integrity": "sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2477,9 +2501,9 @@
}
},
"node_modules/hookified": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/hookified/-/hookified-1.13.0.tgz",
"integrity": "sha512-6sPYUY8olshgM/1LDNW4QZQN0IqgKhtl/1C8koNZBJrKLBk3AZl6chQtNwpNztvfiApHMEwMHek5rv993PRbWw==",
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/hookified/-/hookified-1.14.0.tgz",
"integrity": "sha512-pi1ynXIMFx/uIIwpWJ/5CEtOHLGtnUB0WhGeeYT+fKcQ+WCQbm3/rrkAXnpfph++PgepNqPdTC2WTj8A6k6zoQ==",
"dev": true,
"license": "MIT"
},
@@ -2674,6 +2698,13 @@
"dev": true,
"license": "ISC"
},
"node_modules/javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
"dev": true,
"license": "MIT"
},
"node_modules/js-stringify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
@@ -2716,9 +2747,9 @@
"license": "MIT"
},
"node_modules/keyv": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz",
"integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==",
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.5.tgz",
"integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -3067,6 +3098,30 @@
"node": ">=6"
}
},
"node_modules/mathjs": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-15.1.0.tgz",
"integrity": "sha512-HfnAcScQm9drGryodlDqeS3WAl4gUTYGDcOtcqL/8s23MZ28Ib1i8XnYK3ZdjNuaW/L4BAp9lIp8vxAMrcuu1w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.26.10",
"complex.js": "^2.2.5",
"decimal.js": "^10.4.3",
"escape-latex": "^1.2.0",
"fraction.js": "^5.2.1",
"javascript-natural-sort": "^0.7.1",
"seedrandom": "^3.0.5",
"tiny-emitter": "^2.1.0",
"typed-function": "^4.2.1"
},
"bin": {
"mathjs": "bin/cli.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/mathml-tag-names": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
@@ -3428,9 +3483,9 @@
}
},
"node_modules/postcss-selector-parser": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
"integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -3450,9 +3505,9 @@
"license": "MIT"
},
"node_modules/prettier": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -3505,9 +3560,9 @@
"optional": true
},
"node_modules/qified": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/qified/-/qified-0.5.2.tgz",
"integrity": "sha512-7gJ6mxcQb9vUBOtbKm5mDevbe2uRcOEVp1g4gb/Q+oLntB3HY8eBhOYRxFI2mlDFlY1e4DOSCptzxarXRvzxCA==",
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/qified/-/qified-0.5.3.tgz",
"integrity": "sha512-kXuQdQTB6oN3KhI6V4acnBSZx8D2I4xzZvn9+wFLLFCoBNQY/sFnCW6c43OL7pOQ2HvGV4lnWIXNmgfp7cTWhQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3606,14 +3661,14 @@
}
},
"node_modules/rolldown": {
"version": "1.0.0-beta.51",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.51.tgz",
"integrity": "sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==",
"version": "1.0.0-beta.54",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.54.tgz",
"integrity": "sha512-3lIvjCWgjPL3gmiATUdV1NeVBGJZy6FdtwgLPol25tAkn46Q/MsVGfCSNswXwFOxGrxglPaN20IeALSIFuFyEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.98.0",
"@rolldown/pluginutils": "1.0.0-beta.51"
"@oxc-project/types": "=0.102.0",
"@rolldown/pluginutils": "1.0.0-beta.54"
},
"bin": {
"rolldown": "bin/cli.mjs"
@@ -3622,20 +3677,19 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.0-beta.51",
"@rolldown/binding-darwin-arm64": "1.0.0-beta.51",
"@rolldown/binding-darwin-x64": "1.0.0-beta.51",
"@rolldown/binding-freebsd-x64": "1.0.0-beta.51",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.51",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.51",
"@rolldown/binding-linux-arm64-musl": "1.0.0-beta.51",
"@rolldown/binding-linux-x64-gnu": "1.0.0-beta.51",
"@rolldown/binding-linux-x64-musl": "1.0.0-beta.51",
"@rolldown/binding-openharmony-arm64": "1.0.0-beta.51",
"@rolldown/binding-wasm32-wasi": "1.0.0-beta.51",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.51",
"@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.51",
"@rolldown/binding-win32-x64-msvc": "1.0.0-beta.51"
"@rolldown/binding-android-arm64": "1.0.0-beta.54",
"@rolldown/binding-darwin-arm64": "1.0.0-beta.54",
"@rolldown/binding-darwin-x64": "1.0.0-beta.54",
"@rolldown/binding-freebsd-x64": "1.0.0-beta.54",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.54",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.54",
"@rolldown/binding-linux-arm64-musl": "1.0.0-beta.54",
"@rolldown/binding-linux-x64-gnu": "1.0.0-beta.54",
"@rolldown/binding-linux-x64-musl": "1.0.0-beta.54",
"@rolldown/binding-openharmony-arm64": "1.0.0-beta.54",
"@rolldown/binding-wasm32-wasi": "1.0.0-beta.54",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.54",
"@rolldown/binding-win32-x64-msvc": "1.0.0-beta.54"
}
},
"node_modules/run-parallel": {
@@ -3704,6 +3758,13 @@
],
"license": "BSD-3-Clause"
},
"node_modules/seedrandom": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
"dev": true,
"license": "MIT"
},
"node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
@@ -3832,19 +3893,19 @@
"license": "MIT"
},
"node_modules/sort-package-json": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.5.0.tgz",
"integrity": "sha512-moY4UtptUuP5sPuu9H9dp8xHNel7eP5/Kz/7+90jTvC0IOiPH2LigtRM/aSFSxreaWoToHUVUpEV4a2tAs2oKQ==",
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-3.5.1.tgz",
"integrity": "sha512-LfwyHQuTnJ3336Sexi6yDz1QxvSjXKbc78pOw2HQy8BJHFXzONu+zLvLald0NpLWM0exsYyI7M8PeJAngNgpbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"detect-indent": "^7.0.1",
"detect-indent": "^7.0.2",
"detect-newline": "^4.0.1",
"git-hooks-list": "^4.0.0",
"git-hooks-list": "^4.1.1",
"is-plain-obj": "^4.1.0",
"semver": "^7.7.1",
"sort-object-keys": "^2.0.0",
"tinyglobby": "^0.2.12"
"semver": "^7.7.3",
"sort-object-keys": "^2.0.1",
"tinyglobby": "^0.2.15"
},
"bin": {
"sort-package-json": "cli.js"
@@ -3932,9 +3993,9 @@
}
},
"node_modules/stylelint": {
"version": "16.26.0",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.26.0.tgz",
"integrity": "sha512-Y/3AVBefrkqqapVYH3LBF5TSDZ1kw+0XpdKN2KchfuhMK6lQ85S4XOG4lIZLcrcS4PWBmvcY6eS2kCQFz0jukQ==",
"version": "16.26.1",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.26.1.tgz",
"integrity": "sha512-v20V59/crfc8sVTAtge0mdafI3AdnzQ2KsWe6v523L4OA1bJO02S7MO2oyXDCS6iWb9ckIPnqAFVItqSBQr7jw==",
"dev": true,
"funding": [
{
@@ -3950,6 +4011,7 @@
"peer": true,
"dependencies": {
"@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-syntax-patches-for-csstree": "^1.0.19",
"@csstools/css-tokenizer": "^3.0.4",
"@csstools/media-query-list-parser": "^4.0.3",
"@csstools/selector-specificity": "^5.0.0",
@@ -3962,7 +4024,7 @@
"debug": "^4.4.3",
"fast-glob": "^3.3.3",
"fastest-levenshtein": "^1.0.16",
"file-entry-cache": "^11.1.0",
"file-entry-cache": "^11.1.1",
"global-modules": "^2.0.0",
"globby": "^11.1.0",
"globjoin": "^0.1.4",
@@ -4192,6 +4254,13 @@
"node": ">=10.0.0"
}
},
"node_modules/tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -4254,13 +4323,6 @@
"node": ">=8.0"
}
},
"node_modules/truncatise": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/truncatise/-/truncatise-0.0.8.tgz",
"integrity": "sha512-cXzueh9pzBCsLzhToB4X4gZCb3KYkrsAcBAX97JnazE74HOl3cpBJYEV7nabHeG/6/WXCU5Yujlde/WPBUwnsg==",
"dev": true,
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -4268,6 +4330,16 @@
"dev": true,
"license": "0BSD"
},
"node_modules/typed-function": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz",
"integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 18"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -4291,9 +4363,9 @@
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
"integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
"dev": true,
"funding": [
{
@@ -4329,19 +4401,18 @@
"license": "MIT"
},
"node_modules/vite": {
"name": "rolldown-vite",
"version": "7.2.7",
"resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.2.7.tgz",
"integrity": "sha512-N6a9KgNZ0xgCJ6/Ej2FQ7W8D3fOzDwFw7CLWZ2ubZknVrs9NdNkx25AFEuNbSwQO76VEHp4N7YatsZwp/ST1Gg==",
"version": "8.0.0-beta.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0-beta.2.tgz",
"integrity": "sha512-PIkpGhNy7r5r6Sepwo07BDWf8vr6O4CXVBm+vg7aIpswvL0VNGTjok1qiNRypcqT9dhFQJggtPoubZwXM7yeAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/runtime": "0.98.0",
"@oxc-project/runtime": "0.102.0",
"fdir": "^6.5.0",
"lightningcss": "^1.30.2",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rolldown": "1.0.0-beta.51",
"rolldown": "1.0.0-beta.54",
"tinyglobby": "^0.2.15"
},
"bin": {
@@ -4406,9 +4477,9 @@
}
},
"node_modules/vite-bundle-analyzer": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/vite-bundle-analyzer/-/vite-bundle-analyzer-1.2.3.tgz",
"integrity": "sha512-8nhwDGHWMKKgg6oegAOpDgTT7/yzTVzeYzLF4y8WBJoYu9gO7h29UpHiQnXD2rAvfQzDy5Wqe/Za5cgqhnxi5g==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/vite-bundle-analyzer/-/vite-bundle-analyzer-1.3.1.tgz",
"integrity": "sha512-39J8a+d1KTi9fPt0a7MqqEJibMekdIDCW2nohiyQPSiww+xEBRHKSX4fdpwSvia9KDiFg7THjQRTs0xefzB+lg==",
"dev": true,
"license": "MIT",
"bin": {

View File

@@ -19,9 +19,7 @@
"lint:tsc": "tsc --noEmit"
},
"browserslist": [
"Chrome >= 93",
"Firefox >= 92",
"Safari >= 15.4",
"baseline 2022",
"not dead"
],
"dependencies": {
@@ -32,20 +30,20 @@
},
"devDependencies": {
"@biomejs/biome": "2.3.8",
"@types/node": "~24.10.1",
"browserslist": "~4.28.0",
"@types/node": "~25.0.1",
"browserslist": "~4.28.1",
"browserslist-to-esbuild": "~2.1.1",
"edge.js": "~6.3.0",
"less": "~4.4.2",
"lightningcss": "~1.30.2",
"mathjs": "~15.1.0",
"sharp": "~0.34.5",
"sort-package-json": "~3.5.0",
"sort-package-json": "~3.5.1",
"stylelint": "~16.26.0",
"stylelint-config-standard-less": "~3.0.1",
"stylelint-prettier": "~5.0.3",
"svgo": "~4.0.0",
"typescript": "~5.9.3",
"vite": "npm:rolldown-vite@7.2.7",
"vite-bundle-analyzer": "~1.2.3"
"vite": "8.0.0-beta.2",
"vite-bundle-analyzer": "~1.3.1"
}
}

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
/**
* Base class for client-side plugins.
*
* @remarks
* Handle conditional loading of the plugin in:
*
* - client/simple/src/js/router.ts
*
* @abstract
*/
export abstract class Plugin {
/**
* Plugin name.
*/
protected readonly id: string;
/**
* @remarks
* Don't hold references of this instance outside the class.
*/
protected constructor(id: string) {
this.id = id;
void this.invoke();
}
private async invoke(): Promise<void> {
try {
console.debug(`[PLUGIN] ${this.id}: Running...`);
const result = await this.run();
if (!result) return;
console.debug(`[PLUGIN] ${this.id}: Running post-exec...`);
// @ts-expect-error
void (await this.post(result as NonNullable<Awaited<ReturnType<this["run"]>>>));
} catch (error) {
console.error(`[PLUGIN] ${this.id}:`, error);
} finally {
console.debug(`[PLUGIN] ${this.id}: Done.`);
}
}
/**
* Plugin goes here.
*
* @remarks
* The plugin is already loaded at this point. If you wish to execute
* conditions to exit early, consider moving the logic to:
*
* - client/simple/src/js/router.ts
*
* ...to avoid unnecessarily loading this plugin on the client.
*/
protected abstract run(): Promise<unknown>;
/**
* Post-execution hook.
*
* @remarks
* The hook is only executed if `#run()` returns a truthy value.
*/
// @ts-expect-error
protected abstract post(result: NonNullable<Awaited<ReturnType<this["run"]>>>): Promise<void>;
}

View File

@@ -1,6 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import "./nojs.ts";
import "./router.ts";
import "./toolkit.ts";
import "./listener.ts";

View File

@@ -1,7 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { listen } from "./toolkit.ts";
listen("click", ".close", function (this: HTMLElement) {
(this.parentNode as HTMLElement)?.classList.add("invisible");
});

View File

@@ -1,8 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { ready } from "./toolkit.ts";
ready(() => {
document.documentElement.classList.remove("no-js");
document.documentElement.classList.add("js");
});

View File

@@ -1,40 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { Endpoints, endpoint, ready, settings } from "./toolkit.ts";
ready(
() => {
void import("../main/keyboard.ts");
void import("../main/search.ts");
if (settings.autocomplete) {
void import("../main/autocomplete.ts");
}
},
{ on: [endpoint === Endpoints.index] }
);
ready(
() => {
void import("../main/keyboard.ts");
void import("../main/mapresult.ts");
void import("../main/results.ts");
void import("../main/search.ts");
if (settings.infinite_scroll) {
void import("../main/infinite_scroll.ts");
}
if (settings.autocomplete) {
void import("../main/autocomplete.ts");
}
},
{ on: [endpoint === Endpoints.results] }
);
ready(
() => {
void import("../main/preferences.ts");
},
{ on: [endpoint === Endpoints.preferences] }
);

View File

@@ -0,0 +1,4 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// core
void import.meta.glob(["./*.ts", "./util/**/.ts"], { eager: true });

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type { Plugin } from "./Plugin.ts";
import { type EndpointsKeys, endpoint } from "./toolkit.ts";
type Options =
| {
on: "global";
}
| {
on: "endpoint";
where: EndpointsKeys[];
};
export const load = <T extends Plugin>(instance: () => Promise<T>, options: Options): void => {
if (!check(options)) return;
void instance();
};
const check = (options: Options): boolean => {
// biome-ignore lint/style/useDefaultSwitchClause: options is typed
switch (options.on) {
case "global": {
return true;
}
case "endpoint": {
if (!options.where.includes(endpoint)) {
// not on the expected endpoint
return false;
}
return true;
}
}
};

View File

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { assertElement, http, listen, settings } from "../core/toolkit.ts";
import { http, listen, settings } from "../toolkit.ts";
import { assertElement } from "../util/assertElement.ts";
const fetchResults = async (qInput: HTMLInputElement, query: string): Promise<void> => {
try {

View File

@@ -1,100 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { assertElement, http, settings } from "../core/toolkit.ts";
const newLoadSpinner = (): HTMLDivElement => {
return Object.assign(document.createElement("div"), {
className: "loader"
});
};
const loadNextPage = async (onlyImages: boolean, callback: () => void): Promise<void> => {
const searchForm = document.querySelector<HTMLFormElement>("#search");
assertElement(searchForm);
const form = document.querySelector<HTMLFormElement>("#pagination form.next_page");
assertElement(form);
const action = searchForm.getAttribute("action");
if (!action) {
throw new Error("Form action not defined");
}
const paginationElement = document.querySelector<HTMLElement>("#pagination");
assertElement(paginationElement);
paginationElement.replaceChildren(newLoadSpinner());
try {
const res = await http("POST", action, { body: new FormData(form) });
const nextPage = await res.text();
if (!nextPage) return;
const nextPageDoc = new DOMParser().parseFromString(nextPage, "text/html");
const articleList = nextPageDoc.querySelectorAll<HTMLElement>("#urls article");
const nextPaginationElement = nextPageDoc.querySelector<HTMLElement>("#pagination");
document.querySelector("#pagination")?.remove();
const urlsElement = document.querySelector<HTMLElement>("#urls");
if (!urlsElement) {
throw new Error("URLs element not found");
}
if (articleList.length > 0 && !onlyImages) {
// do not add <hr> element when there are only images
urlsElement.appendChild(document.createElement("hr"));
}
urlsElement.append(...Array.from(articleList));
if (nextPaginationElement) {
const results = document.querySelector<HTMLElement>("#results");
results?.appendChild(nextPaginationElement);
callback();
}
} catch (error) {
console.error("Error loading next page:", error);
const errorElement = Object.assign(document.createElement("div"), {
textContent: settings.translations?.error_loading_next_page ?? "Error loading next page",
className: "dialog-error"
});
errorElement.setAttribute("role", "alert");
document.querySelector("#pagination")?.replaceChildren(errorElement);
}
};
const resultsElement: HTMLElement | null = document.getElementById("results");
if (!resultsElement) {
throw new Error("Results element not found");
}
const onlyImages: boolean = resultsElement.classList.contains("only_template_images");
const observedSelector = "article.result:last-child";
const intersectionObserveOptions: IntersectionObserverInit = {
rootMargin: "320px"
};
const observer: IntersectionObserver = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
const [paginationEntry] = entries;
if (paginationEntry?.isIntersecting) {
observer.unobserve(paginationEntry.target);
void loadNextPage(onlyImages, () => {
const nextObservedElement = document.querySelector<HTMLElement>(observedSelector);
if (nextObservedElement) {
observer.observe(nextObservedElement);
}
}).then(() => {
// wait until promise is resolved
});
}
}, intersectionObserveOptions);
const initialObservedElement: HTMLElement | null = document.querySelector<HTMLElement>(observedSelector);
if (initialObservedElement) {
observer.observe(initialObservedElement);
}

View File

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { assertElement, listen, mutable, settings } from "../core/toolkit.ts";
import { listen, mutable, settings } from "../toolkit.ts";
import { assertElement } from "../util/assertElement.ts";
export type KeyBindingLayout = "default" | "vim";

View File

@@ -1,86 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { listen } from "../core/toolkit.ts";
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");
void import("ol/ol.css");
const { leafletTarget: target, mapLon, mapLat, mapGeojson } = this.dataset;
const lon = Number.parseFloat(mapLon || "0");
const lat = Number.parseFloat(mapLat || "0");
const view = new View({ maxZoom: 16, enableRotation: false });
const map = new OlMap({
target: target,
layers: [new TileLayer({ source: new OSM({ maxZoom: 16 }) })],
view: 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);
}
}
});

View File

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { assertElement, http, listen, settings } from "../core/toolkit.ts";
import { http, listen, settings } from "../toolkit.ts";
import { assertElement } from "../util/assertElement.ts";
let engineDescriptions: Record<string, [string, string]> | undefined;

View File

@@ -1,7 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import "../../../node_modules/swiped-events/src/swiped-events.js";
import { assertElement, listen, mutable, settings } from "../core/toolkit.ts";
import { listen, mutable, settings } from "../toolkit.ts";
import { assertElement } from "../util/assertElement.ts";
let imgTimeoutID: number;

View File

@@ -1,88 +1,51 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { assertElement, listen, settings } from "../core/toolkit.ts";
import { listen } from "../toolkit.ts";
import { getElement } from "../util/getElement.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);
listen("click", cs, (event: MouseEvent) => {
event.preventDefault();
qInput.value = "";
qInput.focus();
updateClearButton(qInput, cs);
});
listen("input", qInput, () => updateClearButton(qInput, cs), { passive: true });
};
const qInput = document.getElementById("q") as HTMLInputElement | null;
assertElement(qInput);
const searchForm: HTMLFormElement = getElement<HTMLFormElement>("search");
const searchInput: HTMLInputElement = getElement<HTMLInputElement>("q");
const searchReset: HTMLButtonElement = getElement<HTMLButtonElement>("clear_search");
const isMobile: boolean = window.matchMedia("(max-width: 50em)").matches;
const isResultsPage: boolean = document.querySelector("main")?.id === "main_results";
const categoryButtons: HTMLButtonElement[] = Array.from(
document.querySelectorAll<HTMLButtonElement>("#categories_container button.category")
);
if (searchInput.value.length === 0) {
searchReset.classList.add("empty");
}
// focus search input on large screens
if (!(isMobile || isResultsPage)) {
qInput.focus();
searchInput.focus();
}
// On mobile, move cursor to the end of the input on focus
if (isMobile) {
listen("focus", qInput, () => {
listen("focus", searchInput, () => {
// Defer cursor move until the next frame to prevent a visual jump
requestAnimationFrame(() => {
const end = qInput.value.length;
qInput.setSelectionRange(end, end);
qInput.scrollLeft = qInput.scrollWidth;
const end = searchInput.value.length;
searchInput.setSelectionRange(end, end);
searchInput.scrollLeft = searchInput.scrollWidth;
});
});
}
createClearButton(qInput);
listen("input", searchInput, () => {
searchReset.classList.toggle("empty", searchInput.value.length === 0);
});
// 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 (
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) {
listen("change", safesearchElement, () => submitIfQuery(qInput));
}
listen("click", searchReset, (event: MouseEvent) => {
event.preventDefault();
searchInput.value = "";
searchInput.focus();
searchReset.classList.add("empty");
});
const timeRangeElement = document.getElementById("time_range");
if (timeRangeElement) {
listen("change", timeRangeElement, () => submitIfQuery(qInput));
}
const languageElement = document.getElementById("language");
if (languageElement) {
listen("change", languageElement, () => submitIfQuery(qInput));
}
}
const categoryButtons: HTMLButtonElement[] = [
...document.querySelectorAll<HTMLButtonElement>("button.category_button")
];
for (const button of categoryButtons) {
listen("click", button, (event: MouseEvent) => {
if (event.shiftKey) {
@@ -98,21 +61,34 @@ for (const button of categoryButtons) {
});
}
const form: HTMLFormElement | null = document.querySelector<HTMLFormElement>("#search");
assertElement(form);
// override form submit action to update the actually selected categories
listen("submit", form, (event: Event) => {
event.preventDefault();
const categoryValuesInput = document.querySelector<HTMLInputElement>("#selected-categories");
if (categoryValuesInput) {
const categoryValues = categoryButtons
.filter((button) => button.classList.contains("selected"))
.map((button) => button.name.replace("category_", ""));
categoryValuesInput.value = categoryValues.join(",");
if (document.querySelector("div.search_filters")) {
const safesearchElement = document.getElementById("safesearch");
if (safesearchElement) {
listen("change", safesearchElement, () => searchForm.submit());
}
form.submit();
const timeRangeElement = document.getElementById("time_range");
if (timeRangeElement) {
listen("change", timeRangeElement, () => searchForm.submit());
}
const languageElement = document.getElementById("language");
if (languageElement) {
listen("change", languageElement, () => searchForm.submit());
}
}
// override searchForm submit event
listen("submit", searchForm, (event: Event) => {
event.preventDefault();
if (categoryButtons.length > 0) {
const searchCategories = getElement<HTMLInputElement>("selected-categories");
searchCategories.value = categoryButtons
.filter((button) => button.classList.contains("selected"))
.map((button) => button.name.replace("category_", ""))
.join(",");
}
searchForm.submit();
});

View File

@@ -1,28 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
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
};

View File

@@ -0,0 +1,93 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
absDependencies,
addDependencies,
create,
divideDependencies,
eDependencies,
evaluateDependencies,
expDependencies,
factorialDependencies,
gcdDependencies,
lcmDependencies,
log1pDependencies,
log2Dependencies,
log10Dependencies,
logDependencies,
modDependencies,
multiplyDependencies,
nthRootDependencies,
piDependencies,
powDependencies,
roundDependencies,
signDependencies,
sqrtDependencies,
subtractDependencies
} from "mathjs/number";
import { Plugin } from "../Plugin.ts";
import { appendAnswerElement } from "../util/appendAnswerElement.ts";
import { getElement } from "../util/getElement.ts";
/**
* Parses and solves mathematical expressions. Can do basic arithmetic and
* evaluate some functions.
*
* @example
* "(3 + 5) / 2" = "4"
* "e ^ 2 + pi" = "10.530648752520442"
* "gcd(48, 18) + lcm(4, 5)" = "26"
*
* @remarks
* Depends on `mathjs` library.
*/
export default class Calculator extends Plugin {
public constructor() {
super("calculator");
}
/**
* @remarks
* Compare bundle size after adding or removing features.
*/
private static readonly math = create({
...absDependencies,
...addDependencies,
...divideDependencies,
...eDependencies,
...evaluateDependencies,
...expDependencies,
...factorialDependencies,
...gcdDependencies,
...lcmDependencies,
...log10Dependencies,
...log1pDependencies,
...log2Dependencies,
...logDependencies,
...modDependencies,
...multiplyDependencies,
...nthRootDependencies,
...piDependencies,
...powDependencies,
...roundDependencies,
...signDependencies,
...sqrtDependencies,
...subtractDependencies
});
protected async run(): Promise<string | undefined> {
const searchInput = getElement<HTMLInputElement>("q");
const node = Calculator.math.parse(searchInput.value);
try {
return `${node.toString()} = ${node.evaluate()}`;
} catch {
// not a compatible math expression
return;
}
}
protected async post(result: string): Promise<void> {
appendAnswerElement(result);
}
}

View File

@@ -0,0 +1,110 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { Plugin } from "../Plugin.ts";
import { http, settings } from "../toolkit.ts";
import { assertElement } from "../util/assertElement.ts";
import { getElement } from "../util/getElement.ts";
/**
* Automatically loads the next page when scrolling to bottom of the current page.
*/
export default class InfiniteScroll extends Plugin {
public constructor() {
super("infiniteScroll");
}
protected async run(): Promise<void> {
const resultsElement = getElement<HTMLElement>("results");
const onlyImages: boolean = resultsElement.classList.contains("only_template_images");
const observedSelector = "article.result:last-child";
const spinnerElement = document.createElement("div");
spinnerElement.className = "loader";
const loadNextPage = async (callback: () => void): Promise<void> => {
const searchForm = document.querySelector<HTMLFormElement>("#search");
assertElement(searchForm);
const form = document.querySelector<HTMLFormElement>("#pagination form.next_page");
assertElement(form);
const action = searchForm.getAttribute("action");
if (!action) {
throw new Error("Form action not defined");
}
const paginationElement = document.querySelector<HTMLElement>("#pagination");
assertElement(paginationElement);
paginationElement.replaceChildren(spinnerElement);
try {
const res = await http("POST", action, { body: new FormData(form) });
const nextPage = await res.text();
if (!nextPage) return;
const nextPageDoc = new DOMParser().parseFromString(nextPage, "text/html");
const articleList = nextPageDoc.querySelectorAll<HTMLElement>("#urls article");
const nextPaginationElement = nextPageDoc.querySelector<HTMLElement>("#pagination");
document.querySelector("#pagination")?.remove();
const urlsElement = document.querySelector<HTMLElement>("#urls");
if (!urlsElement) {
throw new Error("URLs element not found");
}
if (articleList.length > 0 && !onlyImages) {
// do not add <hr> element when there are only images
urlsElement.appendChild(document.createElement("hr"));
}
urlsElement.append(...articleList);
if (nextPaginationElement) {
const results = document.querySelector<HTMLElement>("#results");
results?.appendChild(nextPaginationElement);
callback();
}
} catch (error) {
console.error("Error loading next page:", error);
const errorElement = Object.assign(document.createElement("div"), {
textContent: settings.translations?.error_loading_next_page ?? "Error loading next page",
className: "dialog-error"
});
errorElement.setAttribute("role", "alert");
document.querySelector("#pagination")?.replaceChildren(errorElement);
}
};
const intersectionObserveOptions: IntersectionObserverInit = {
rootMargin: "320px"
};
const observer: IntersectionObserver = new IntersectionObserver(async (entries: IntersectionObserverEntry[]) => {
const [paginationEntry] = entries;
if (paginationEntry?.isIntersecting) {
observer.unobserve(paginationEntry.target);
await loadNextPage(() => {
const nextObservedElement = document.querySelector<HTMLElement>(observedSelector);
if (nextObservedElement) {
observer.observe(nextObservedElement);
}
});
}
}, intersectionObserveOptions);
const initialObservedElement: HTMLElement | null = document.querySelector<HTMLElement>(observedSelector);
if (initialObservedElement) {
observer.observe(initialObservedElement);
}
}
protected async post(): Promise<void> {
// noop
}
}

View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import "ol/ol.css?inline";
import { Feature, Map as OlMap, View } from "ol";
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";
import { Plugin } from "../Plugin.ts";
/**
* MapView
*/
export default class MapView extends Plugin {
private readonly map: HTMLElement;
public constructor(map: HTMLElement) {
super("mapView");
this.map = map;
}
protected async run(): Promise<void> {
const { leafletTarget: target, mapLon, mapLat, mapGeojson } = this.map.dataset;
const lon = Number.parseFloat(mapLon || "0");
const lat = Number.parseFloat(mapLat || "0");
const view = new View({ maxZoom: 16, enableRotation: false });
const map = new OlMap({
target: target,
layers: [new TileLayer({ source: new OSM({ maxZoom: 16 }) })],
view: 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);
}
}
}
protected async post(): Promise<void> {
// noop
}
}

View File

@@ -0,0 +1,69 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { load } from "./loader.ts";
import { Endpoints, endpoint, listen, ready, settings } from "./toolkit.ts";
ready(() => {
document.documentElement.classList.remove("no-js");
document.documentElement.classList.add("js");
listen("click", ".close", function (this: HTMLElement) {
(this.parentNode as HTMLElement)?.classList.add("invisible");
});
listen("click", ".searxng_init_map", async function (this: HTMLElement, event: Event) {
event.preventDefault();
this.classList.remove("searxng_init_map");
load(() => import("./plugin/MapView.ts").then(({ default: Plugin }) => new Plugin(this)), {
on: "endpoint",
where: [Endpoints.results]
});
});
if (settings.plugins?.includes("infiniteScroll")) {
load(() => import("./plugin/InfiniteScroll.ts").then(({ default: Plugin }) => new Plugin()), {
on: "endpoint",
where: [Endpoints.results]
});
}
if (settings.plugins?.includes("calculator")) {
load(() => import("./plugin/Calculator.ts").then(({ default: Plugin }) => new Plugin()), {
on: "endpoint",
where: [Endpoints.results]
});
}
});
ready(
() => {
void import("./main/keyboard.ts");
void import("./main/search.ts");
if (settings.autocomplete) {
void import("./main/autocomplete.ts");
}
},
{ on: [endpoint === Endpoints.index] }
);
ready(
() => {
void import("./main/keyboard.ts");
void import("./main/results.ts");
void import("./main/search.ts");
if (settings.autocomplete) {
void import("./main/autocomplete.ts");
}
},
{ on: [endpoint === Endpoints.results] }
);
ready(
() => {
void import("./main/preferences.ts");
},
{ on: [endpoint === Endpoints.preferences] }
);

View File

@@ -1,16 +1,16 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type { KeyBindingLayout } from "../main/keyboard.ts";
import type { KeyBindingLayout } from "./main/keyboard.ts";
// synced with searx/webapp.py get_client_settings
type Settings = {
plugins?: string[];
advanced_search?: boolean;
autocomplete?: string;
autocomplete_min?: number;
doi_resolver?: string;
favicon_resolver?: string;
hotkeys?: KeyBindingLayout;
infinite_scroll?: boolean;
method?: "GET" | "POST";
query_in_title?: boolean;
results_on_new_tab?: boolean;
@@ -32,8 +32,6 @@ type ReadyOptions = {
on?: (boolean | undefined)[];
};
type AssertElement = (element?: HTMLElement | null) => asserts element is HTMLElement;
export type EndpointsKeys = keyof typeof Endpoints;
export const Endpoints = {
@@ -73,12 +71,6 @@ const getSettings = (): Settings => {
}
};
export const assertElement: AssertElement = (element?: HTMLElement | null): asserts element is HTMLElement => {
if (!element) {
throw new Error("Bad assertion: DOM element not found");
}
};
export const http = async (method: string, url: string | URL, options?: HTTPOptions): Promise<Response> => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), options?.timeout ?? 30_000);

View File

@@ -0,0 +1,34 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { getElement } from "./getElement.ts";
export const appendAnswerElement = (element: HTMLElement | string | number): void => {
const results = getElement<HTMLDivElement>("results");
// ./searx/templates/elements/answers.html
let answers = getElement<HTMLDivElement>("answers", { assert: false });
if (!answers) {
// what is this?
const answersTitle = document.createElement("h4");
answersTitle.setAttribute("class", "title");
answersTitle.setAttribute("id", "answers-title");
answersTitle.textContent = "Answers : ";
answers = document.createElement("div");
answers.setAttribute("id", "answers");
answers.setAttribute("role", "complementary");
answers.setAttribute("aria-labelledby", "answers-title");
answers.appendChild(answersTitle);
}
if (!(element instanceof HTMLElement)) {
const span = document.createElement("span");
span.innerHTML = element.toString();
// biome-ignore lint/style/noParameterAssign: TODO
element = span;
}
answers.appendChild(element);
results.insertAdjacentElement("afterbegin", answers);
};

View File

@@ -0,0 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
type AssertElement = <T>(element?: T | null) => asserts element is T;
export const assertElement: AssertElement = <T>(element?: T | null): asserts element is T => {
if (!element) {
throw new Error("DOM element not found");
}
};

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import { assertElement } from "./assertElement.ts";
type Options = {
assert?: boolean;
};
export function getElement<T>(id: string, options?: { assert: true }): T;
export function getElement<T>(id: string, options?: { assert: false }): T | null;
export function getElement<T>(id: string, options: Options = {}): T | null {
options.assert ??= true;
const element = document.getElementById(id) as T | null;
if (options.assert) {
assertElement(element);
}
return element;
}

View File

@@ -1,19 +1,16 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
iframe[src^="https://w.soundcloud.com"]
{
iframe[src^="https://w.soundcloud.com"] {
height: 120px;
}
iframe[src^="https://www.deezer.com"]
{
iframe[src^="https://www.deezer.com"] {
// The real size is 92px, but 94px are needed to avoid an inner scrollbar of
// the embedded HTML.
height: 94px;
}
iframe[src^="https://www.mixcloud.com"]
{
iframe[src^="https://www.mixcloud.com"] {
// the embedded player from mixcloud has some quirks: initial there is an
// issue with an image URL that is blocked since it is an a Cross-Origin
// request. The alternative text (<img alt='Mixcloud Logo'> then cause an
@@ -23,19 +20,16 @@ iframe[src^="https://www.mixcloud.com"]
height: 250px;
}
iframe[src^="https://bandcamp.com/EmbeddedPlayer"]
{
iframe[src^="https://bandcamp.com/EmbeddedPlayer"] {
// show playlist
height: 350px;
}
iframe[src^="https://bandcamp.com/EmbeddedPlayer/track"]
{
iframe[src^="https://bandcamp.com/EmbeddedPlayer/track"] {
// hide playlist
height: 120px;
}
iframe[src^="https://genius.com/songs"]
{
iframe[src^="https://genius.com/songs"] {
height: 65px;
}

View File

@@ -8,7 +8,7 @@
text-align: center;
.title {
background: url("../img/searxng.png") no-repeat;
background: url("./img/searxng.png") no-repeat;
min-height: 4rem;
margin: 4rem auto;
background-position: center;

View File

@@ -46,39 +46,34 @@ export default {
sourcemap: true,
rolldownOptions: {
input: {
// build CSS files
"searxng-ltr.css": `${PATH.src}/less/style-ltr.less`,
"searxng-rtl.css": `${PATH.src}/less/style-rtl.less`,
"rss.css": `${PATH.src}/less/rss.less`,
// entrypoint
core: `${PATH.src}/js/index.ts`,
// build script files
"searxng.core": `${PATH.src}/js/core/index.ts`,
// ol pkg
ol: `${PATH.src}/js/pkg/ol.ts`,
"ol.css": `${PATH.modules}/ol/ol.css`
// stylesheets
ltr: `${PATH.src}/less/style-ltr.less`,
rtl: `${PATH.src}/less/style-rtl.less`,
rss: `${PATH.src}/less/rss.less`
},
// file naming conventions / pathnames are relative to outDir (PATH.dist)
output: {
entryFileNames: "js/[name].min.js",
chunkFileNames: "js/[name].min.js",
entryFileNames: "sxng-[name].min.js",
chunkFileNames: "chunk/[hash].min.js",
assetFileNames: ({ names }: PreRenderedAsset): string => {
const [name] = names;
const extension = name?.split(".").pop();
switch (extension) {
switch (name?.split(".").pop()) {
case "css":
return "css/[name].min[extname]";
case "js":
return "js/[name].min[extname]";
case "png":
case "svg":
return "img/[name][extname]";
return "sxng-[name].min[extname]";
default:
console.warn("Unknown asset:", name);
return "[name][extname]";
return "sxng-[name][extname]";
}
},
sanitizeFileName: (name: string): string => {
return name
.normalize("NFD")
.replace(/[^a-zA-Z0-9.-]/g, "_")
.toLowerCase();
}
}
}

View File

@@ -69,6 +69,9 @@ The built-in plugins are all located in the namespace `searx.plugins`.
searx.plugins.calculator.SXNGPlugin:
active: true
searx.plugins.infinite_scroll.SXNGPlugin:
active: false
searx.plugins.hash_plugin.SXNGPlugin:
active: true

View File

@@ -12,7 +12,6 @@
ui:
default_locale: ""
query_in_title: false
infinite_scroll: false
center_alignment: false
cache_url: https://web.archive.org/web/
default_theme: simple
@@ -32,9 +31,6 @@
When true, the result page's titles contains the query it decreases the
privacy, since the browser can records the page titles.
``infinite_scroll``:
When true, automatically loads the next page when scrolling to bottom of the current page.
``center_alignment`` : default ``false``
When enabled, the results are centered instead of being in the left (or RTL)
side of the screen. This setting only affects the *desktop layout*

View File

@@ -10,6 +10,7 @@ Built-in Plugins
calculator
hash_plugin
hostnames
infinite_scroll
self_info
tor_check
unit_converter

View File

@@ -0,0 +1,8 @@
.. _plugins.infinite_scroll:
===============
Infinite scroll
===============
.. automodule:: searx.plugins.infinite_scroll
:members:

View File

@@ -1,7 +1,7 @@
[tools]
# minimal version we support
python = "3.10"
node = "24.3.0"
node = "25"
go = "1.24.5"
shellcheck = "0.11.0"
# python 3.10 uses 3.40.1 (on mac and win)

View File

@@ -2,9 +2,9 @@ mock==5.2.0
nose2[coverage_plugin]==0.15.1
cov-core==1.15.0
black==25.9.0
pylint==4.0.3
pylint==4.0.4
splinter==0.21.0
selenium==4.38.0
selenium==4.39.0
Pallets-Sphinx-Themes==2.3.0
Sphinx==8.2.3 ; python_version >= '3.11'
Sphinx==8.1.3 ; python_version < '3.11'
@@ -24,5 +24,5 @@ coloredlogs==15.0.1
docutils>=0.21.2
parameterized==0.9.0
granian[reload]==2.6.0
basedpyright==1.34.0
basedpyright==1.36.1
types-lxml==2025.11.25

View File

@@ -9,6 +9,7 @@ python-dateutil==2.9.0.post0
pyyaml==6.0.3
httpx[http2]==0.28.1
httpx-socks[asyncio]==0.10.0
sniffio==1.3.1
valkey==6.1.1
markdown-it-py==3.0.0
fasttext-predict==0.9.2.4

File diff suppressed because it is too large Load Diff

View File

@@ -321,6 +321,7 @@
"ja": "アルゼンチン・ペソ",
"ko": "아르헨티나 페소",
"lt": "Argentinos pesas",
"lv": "Argentīnas peso",
"ms": "Peso Argentina",
"nl": "Argentijnse peso",
"oc": "Peso",
@@ -803,6 +804,7 @@
"ja": "ボリビアーノ",
"ko": "볼리비아 볼리비아노",
"lt": "Bolivianas",
"lv": "Bolīvijas boliviano",
"ms": "Boliviano",
"nl": "Boliviaanse boliviano",
"oc": "Boliviano",
@@ -1467,6 +1469,7 @@
"ja": "カーボベルデ・エスクード",
"ko": "카보베르데 이스쿠두",
"lt": "Žaliojo Kyšulio eskudas",
"lv": "Kaboverdes eskudo",
"nl": "Kaapverdische escudo",
"oc": "Escut de Cap Verd",
"pl": "escudo Zielonego Przylądka",
@@ -1567,7 +1570,7 @@
"ar": "كرونة دنماركية",
"bg": "Датска крона",
"ca": "corona danesa",
"cs": "Dánská koruna",
"cs": "dánská koruna",
"cy": "Krone Danaidd",
"da": "dansk krone",
"de": "dänische Krone",
@@ -2037,6 +2040,7 @@
"ja": "セディ",
"ko": "가나 세디",
"lt": "Sedis",
"lv": "Ganas sedi",
"ms": "Cedi Ghana",
"nl": "Ghanese cedi",
"oc": "Cedi",
@@ -2151,6 +2155,7 @@
"ja": "ギニア・フラン",
"ko": "기니 프랑",
"lt": "Gvinėjos frankas",
"lv": "Gvinejas franks",
"ms": "Franc Guinea",
"nl": "Guineese frank",
"oc": "Franc guinean",
@@ -2967,6 +2972,7 @@
"ms": "Won Korea Utara",
"nl": "Noord-Koreaanse won",
"pa": "ਉੱਤਰੀ ਕੋਰੀਆਈ ਵੌਨ",
"pap": "won nortkoreano",
"pl": "Won północnokoreański",
"pt": "won norte-coreano",
"ro": "Won nord-coreean",
@@ -3813,6 +3819,7 @@
"ja": "メキシコ・ペソ",
"ko": "멕시코 페소",
"lt": "Meksikos pesas",
"lv": "Meksikas peso",
"ms": "Peso Mexico",
"nl": "Mexicaanse peso",
"pa": "ਮੈਕਸੀਕੀ ਪੇਸੋ",
@@ -3828,7 +3835,7 @@
"tr": "Meksika pesosu",
"tt": "Миксикә писысы",
"uk": "мексиканський песо",
"vi": "Peso Mexico"
"vi": "Peso México"
},
"MXV": {
"de": "UNIDAD DE INVERSION",
@@ -3882,7 +3889,7 @@
"MZN": {
"ar": "مثقال موزنبيقي",
"ca": "metical",
"cs": "Mosambický metical",
"cs": "mosambický metical",
"cy": "Metical Mosambic",
"da": "Metical",
"de": "Metical",
@@ -3975,6 +3982,7 @@
"ja": "ナイラ",
"ko": "나이지리아 나이라",
"lt": "Naira",
"lv": "Nigērijas naira",
"ms": "Naira Nigeria",
"nl": "Nigeriaanse naira",
"oc": "Naira",
@@ -4031,7 +4039,7 @@
"ar": "كرونة نروجية",
"bg": "норвежка крона",
"ca": "corona noruega",
"cs": "Norská koruna",
"cs": "norská koruna",
"cy": "krone Norwy",
"da": "norsk krone",
"de": "norwegische Krone",
@@ -4258,6 +4266,7 @@
"ja": "ヌエボ・ソル",
"ko": "페루 솔",
"lt": "Naujasis solis",
"lv": "Peru sols",
"ms": "Nuevo Sol Peru",
"nl": "Peruviaanse sol",
"oc": "Nuevo Sol",
@@ -4782,7 +4791,7 @@
"en": "Solomon Islands dollar",
"eo": "salomona dolaro",
"es": "dólar de las Islas Salomón",
"fi": "Salomonsaarten dollari",
"fi": "Salomoninsaarten dollari",
"fr": "dollar des îles Salomon",
"ga": "dollar Oileáin Sholaimh",
"gl": "Dólar das Illas Salomón",
@@ -5018,6 +5027,7 @@
"ja": "レオン",
"ko": "시에라리온 레온",
"lt": "leonė",
"lv": "Sjerraleones leone",
"ms": "leone",
"nl": "Sierra Leoonse leone",
"oc": "leone",
@@ -5055,6 +5065,7 @@
"ja": "ソマリア・シリング",
"ko": "소말리아 실링",
"lt": "Somalio šilingas",
"lv": "Somālijas šiliņš",
"ms": "Shilling Somalia",
"nl": "Somalische shilling",
"pl": "Szyling somalijski",
@@ -5500,7 +5511,7 @@
"TTD": {
"ar": "دولار ترينيداد وتوباغو",
"bg": "Тринидадски и тобагски долар",
"ca": "dòlar de Trinitat i Tobago",
"ca": "dòlar de Trinidad i Tobago",
"cs": "Dolar Trinidadu a Tobaga",
"cy": "doler Trinidad a Thobago",
"de": "Trinidad-und-Tobago-Dollar",
@@ -5718,7 +5729,7 @@
"lv": "ASV dolārs",
"ml": "യുണൈറ്റഡ് സ്റ്റേറ്റ്സ് ഡോളർ",
"ms": "Dolar Amerika Syarikat",
"nl": "US dollar",
"nl": "Amerikaanse dollar",
"oc": "dolar american",
"pa": "ਸੰਯੁਕਤ ਰਾਜ ਡਾਲਰ",
"pap": "Dollar merikano",
@@ -5813,6 +5824,7 @@
"nl": "Oezbeekse sum",
"oc": "som ozbèc",
"pa": "ਉਜ਼ਬੇਕਿਸਤਾਨੀ ਸੋਮ",
"pap": "som usbekistani",
"pl": "Sum",
"pt": "som usbeque",
"ro": "Som uzbec",
@@ -5838,6 +5850,7 @@
"en": "sovereign bolivar",
"es": "bolívar soberano",
"fr": "bolivar souverain",
"gl": "bolívar soberano",
"hu": "venezuelai bolívar",
"ja": "ボリバル・ソベラノ",
"pt": "Bolívar soberano",
@@ -6578,10 +6591,13 @@
"R": "ZAR",
"R$": "BRL",
"RD$": "DOP",
"RF": "RWF",
"RM": "MYR",
"RWF": "RWF",
"Rf": "MVR",
"Rp": "IDR",
"Rs": "LKR",
"R₣": "RWF",
"S$": "SGD",
"S/.": "PEN",
"SI$": "SBD",
@@ -6601,6 +6617,7 @@
"Ush": "UGX",
"VT": "VUV",
"WS$": "WST",
"XAF": "XAF",
"XCG": "XCG",
"XDR": "XDR",
"Z$": "ZWL",
@@ -6726,6 +6743,7 @@
"argentinské peso": "ARS",
"argentinski peso": "ARS",
"argentinski pezo": "ARS",
"argentīnas peso": "ARS",
"ariari": "MGA",
"ariari de madagascar": "MGA",
"ariari de madagáscar": "MGA",
@@ -7045,6 +7063,7 @@
"bolívar soberano": "VES",
"bolívar sobirà": "VES",
"bolíviai boliviano": "BOB",
"bolīvijas boliviano": "BOB",
"bosenská konvertibilní marka": "BAM",
"bosna hersek değiştirilebilir markı": "BAM",
"bosnia and herzegovina convertible mark": "BAM",
@@ -8242,9 +8261,7 @@
"dòlar de singapur": "SGD",
"dòlar de surinam": "SRD",
"dòlar de taiwan": "TWD",
"dòlar de trinitat": "TTD",
"dòlar de trinitat i tobago": "TTD",
"dòlar de trinitat tobago": "TTD",
"dòlar de trinidad i tobago": "TTD",
"dòlar de zimbàbue": "ZWL",
"dòlar del canadà": "CAD",
"dòlar del carib oriental": "XCD",
@@ -8905,6 +8922,7 @@
"gambijski dalasi": "GMD",
"gambijský dalasi": "GMD",
"ganaa cedio": "GHS",
"ganas sedi": "GHS",
"ganski cedi": "GHS",
"gbp": "GBP",
"gbp£": "GBP",
@@ -9054,6 +9072,7 @@
"gvatemalski kvecal": "GTQ",
"gvatemalski quetzal": "GTQ",
"gvinea franko": "GNF",
"gvinejas franks": "GNF",
"gvinejski franak": "GNF",
"gvinejski frank": "GNF",
"gvinėjos frankas": "GNF",
@@ -9381,6 +9400,7 @@
"kaaimaneilandse dollar": "KYD",
"kaapverdische escudo": "CVE",
"kaboverda eskudo": "CVE",
"kaboverdes eskudo": "CVE",
"kaiman dollar": "KYD",
"kaimanu dolārs": "KYD",
"kaimanu salu dolārs": "KYD",
@@ -9790,6 +9810,7 @@
"lari na seoirsia": "GEL",
"lario": "GEL",
"laris": "GEL",
"lári": "GEL",
"länsi afrikan cfa frangi": "XOF",
"lbp": "LBP",
"ld": "LYD",
@@ -10316,6 +10337,7 @@
"meksika peso": "MXN",
"meksika pesosu": "MXN",
"meksikaanse peso": "MXN",
"meksikas peso": "MXN",
"meksikon peso": "MXN",
"meksikos pesas": "MXN",
"meticais": "MZN",
@@ -10552,6 +10574,7 @@
"nigerijská naira": "NGN",
"nigériai naira": "NGN",
"nigérijská naira": "NGN",
"nigērijas naira": "NGN",
"niĝera najro": "NGN",
"niĝeria najro": "NGN",
"nijerya nairası": "NGN",
@@ -10680,7 +10703,6 @@
"nuevo dólar taiwanes": "TWD",
"nuevo dólar taiwanés": "TWD",
"nuevo peso": [
"UYU",
"MXN",
"ARS"
],
@@ -10878,6 +10900,7 @@
"penny": "GBP",
"perak sebagai pelaburan": "XAG",
"peru nueva solü": "PEN",
"peru sols": "PEN",
"perua nova suno": "PEN",
"peruanischer nuevo sol": "PEN",
"peruanischer sol": "PEN",
@@ -10952,7 +10975,6 @@
"peso de méxico": "MXN",
"peso de republica dominicana": "DOP",
"peso de república dominicana": "DOP",
"peso de uruguay": "UYU",
"peso de xile": "CLP",
"peso do chile": "CLP",
"peso do uruguai": "UYU",
@@ -11599,7 +11621,6 @@
"rúpia indiana": "INR",
"rúpies": "INR",
"rūpija": "IDR",
"rwanda franc": "RWF",
"rwanda frank": "RWF",
"rwandan franc": "RWF",
"rwandan frank": "RWF",
@@ -11840,6 +11861,7 @@
"sistema unificato di compensazione regionale": "XSU",
"sistema único de compensación regional": "XSU",
"sjekel": "ILS",
"sjerraleones leone": "SLE",
"sjevernokorejski von": "KPW",
"sle": "SLE",
"sll": "SLE",
@@ -11884,6 +11906,7 @@
"som ozbèc": "UZS",
"som quirguiz": "KGS",
"som usbeco": "UZS",
"som usbekistani": "UZS",
"som usbeque": "UZS",
"som uzbec": "UZS",
"som uzbeco": "UZS",
@@ -11906,6 +11929,7 @@
"somas": "KGS",
"somálsky šiling": "SOS",
"somálský šilink": "SOS",
"somālijas šiliņš": "SOS",
"some": "KGS",
"somoni": "TJS",
"somoni na táidsíceastáine": "TJS",
@@ -12675,6 +12699,7 @@
"won nord coréen": "KPW",
"won nordcoreano": "KPW",
"won norte coreano": "KPW",
"won nortkoreano": "KPW",
"won południowokoreański": "KRW",
"won północnokoreański": "KPW",
"won sud corean": "KRW",
@@ -15372,7 +15397,6 @@
"యునైటెడ్ స్టేట్స్ డాలర్": "USD",
"యూరో": "EUR",
"రూపాయి": "INR",
"సంయుక్త రాష్ట్రాల డాలర్": "USD",
"స్విస్ ఫ్రాంక్": "CHF",
"അൾജീരിയൻ ദിനാർ": "DZD",
"ഇന്തോനേഷ്യൻ റുപിയ": "IDR",

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
],
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
"versions": [
"144.0",
"143.0"
"145.0",
"144.0"
]
}

View File

@@ -5459,6 +5459,11 @@
"symbol": "T",
"to_si_factor": 907.18474
},
"Q4741": {
"si_name": null,
"symbol": "RF",
"to_si_factor": null
},
"Q474533": {
"si_name": null,
"symbol": "At",

View File

@@ -214,6 +214,7 @@ def request(query: str, params: dict[str, t.Any]) -> None:
if brave_category == "goggles":
args["goggles_id"] = Goggles
params["headers"]["Accept-Encoding"] = "gzip, deflate"
params["url"] = f"{base_url}{brave_category}?{urlencode(args)}"
logger.debug("url %s", params["url"])
@@ -301,7 +302,10 @@ def _parse_search(resp: SXNG_Response) -> EngineResults:
content: str = ""
pub_date = None
_content = eval_xpath_getindex(result, ".//div[contains(@class, 'content')]", 0, default="")
# there are other classes like 'site-name-content' we don't want to match,
# however only using contains(@class, 'content') would e.g. also match `site-name-content`
# thus, we explicitly also require the spaces as class separator
_content = eval_xpath_getindex(result, ".//div[contains(concat(' ', @class, ' '), ' content ')]", 0, default="")
if len(_content):
content = extract_text(_content) # type: ignore
_pub_date = extract_text(

View File

@@ -42,8 +42,8 @@ def response(resp):
results.append(
{
'url': item['source_page_url'],
'title': item['source_site'],
'url': item.get('source_page_url'),
'title': item.get('source_site'),
'img_src': img if item['type'] == 'IMAGE' else thumb,
'filesize': humanize_bytes(item['meme_file_size']),
'publishedDate': formatted_date,

69
searx/engines/lucide.py Normal file
View File

@@ -0,0 +1,69 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Browse one of the largest collections of copyleft icons
that can be used for own projects (e.g. apps, websites).
.. _Website: https://lucide.dev
"""
import typing as t
from searx.result_types import EngineResults
if t.TYPE_CHECKING:
from extended_types import SXNG_Response
from search.processors.online import OnlineParams
about = {
"website": "https://lucide.dev/",
"wikidata_id": None,
"official_api_documentation": None,
"use_official_api": True,
"results": "JSON",
}
cdn_base_url = "https://cdn.jsdelivr.net/npm/lucide-static"
categories = ["images", "icons"]
def request(query: str, params: "OnlineParams"):
params["url"] = f"{cdn_base_url}/tags.json"
params['query'] = query
return params
def response(resp: "SXNG_Response") -> EngineResults:
res = EngineResults()
query_parts = resp.search_params["query"].lower().split(" ")
def is_result_match(result: tuple[str, list[str]]) -> bool:
icon_name, tags = result
for part in query_parts:
if part in icon_name:
return True
for tag in tags:
if part in tag:
return True
return False
filtered_results = filter(is_result_match, resp.json().items())
for icon_name, tags in filtered_results:
img_src = f"{cdn_base_url}/icons/{icon_name}.svg"
res.add(
res.types.LegacyResult(
{
"template": "images.html",
"url": img_src,
"title": icon_name,
"content": ", ".join(tags),
"img_src": img_src,
"img_format": "SVG",
}
)
)
return res

View File

@@ -28,7 +28,7 @@ Implementations
"""
import typing as t
from urllib.parse import urlencode, quote_plus
from urllib.parse import urlencode
from searx.utils import searxng_useragent
from searx.result_types import EngineResults
from searx.extended_types import SXNG_Response
@@ -42,7 +42,7 @@ about = {
"results": "JSON",
}
base_url = "https://api.marginalia.nu"
base_url = "https://api2.marginalia-search.com"
safesearch = True
categories = ["general"]
paging = False
@@ -85,13 +85,11 @@ class ApiSearchResults(t.TypedDict):
def request(query: str, params: dict[str, t.Any]):
query_params = {
"count": results_per_page,
"nsfw": min(params["safesearch"], 1),
}
query_params = {"count": results_per_page, "nsfw": min(params["safesearch"], 1), "query": query}
params["url"] = f"{base_url}/{api_key}/search/{quote_plus(query)}?{urlencode(query_params)}"
params["url"] = f"{base_url}/search?{urlencode(query_params)}"
params["headers"]["User-Agent"] = searxng_useragent()
params["headers"]["API-Key"] = api_key
def response(resp: SXNG_Response):

View File

@@ -65,7 +65,8 @@ def request(query, params):
if search_type:
args['fmt'] = search_type
if search_type == '':
# setting the page number on the first page (i.e. s=0) triggers a rate-limit
if search_type == '' and params['pageno'] > 1:
args['s'] = 10 * (params['pageno'] - 1)
if params['time_range'] and search_type != 'images':

View File

@@ -40,8 +40,8 @@ Known Quirks
The implementation to support :py:obj:`paging <searx.enginelib.Engine.paging>`
is based on the *nextpage* method of Piped's REST API / the :py:obj:`frontend
API <frontend_url>`. This feature is *next page driven* and plays well with the
:ref:`infinite_scroll <settings ui>` setting in SearXNG but it does not really
fit into SearXNG's UI to select a page by number.
:ref:`infinite_scroll <settings plugins>` plugin in SearXNG but it does not
really fit into SearXNG's UI to select a page by number.
Implementations
===============

View File

@@ -17,6 +17,7 @@ about = {
# Engine configuration
paging = True
categories = ['images']
remove_ai_images = False
# Search URL
base_url = "https://www.pixiv.net/ajax/search/illustrations"
@@ -34,6 +35,9 @@ def request(query, params):
"lang": "en",
}
if remove_ai_images is True:
query_params.update({"ai_type": 1})
params["url"] = f"{base_url}/{query}?{urlencode(query_params)}"
return params

View File

@@ -32,8 +32,8 @@ Known Quirks
The implementation to support :py:obj:`paging <searx.enginelib.Engine.paging>`
is based on the *nextpage* method of Seekr's REST API. This feature is *next
page driven* and plays well with the :ref:`infinite_scroll <settings ui>`
setting in SearXNG but it does not really fit into SearXNG's UI to select a page
page driven* and plays well with the :ref:`infinite_scroll <settings plugins>`
plugin in SearXNG but it does not really fit into SearXNG's UI to select a page
by number.
Implementations

View File

@@ -1,31 +1,19 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Calculate mathematical expressions using :py:obj:`ast.parse` (mode="eval")."""
# pylint: disable=missing-module-docstring
import typing
import typing as t
import ast
import math
import re
import operator
import multiprocessing
from flask_babel import gettext # pyright: ignore[reportUnknownVariableType]
import babel
import babel.numbers
from flask_babel import gettext
from searx.result_types import EngineResults
from searx.plugins import Plugin, PluginInfo
if typing.TYPE_CHECKING:
from searx.search import SearchWithPlugins
from searx.extended_types import SXNG_Request
if t.TYPE_CHECKING:
from searx.plugins import PluginCfg
@t.final
class SXNGPlugin(Plugin):
"""Plugin converts strings to different hash digests. The results are
displayed in area for the "answers".
"""
"""Parses and solves mathematical expressions."""
id = "calculator"
@@ -34,200 +22,7 @@ class SXNGPlugin(Plugin):
self.info = PluginInfo(
id=self.id,
name=gettext("Basic Calculator"),
description=gettext("Calculate mathematical expressions via the search bar"),
preference_section="general",
name=gettext("Calculator"),
description=gettext("Parses and solves mathematical expressions."),
preference_section="query",
)
def timeout_func(self, timeout, func, *args, **kwargs):
que = mp_fork.Queue()
p = mp_fork.Process(target=handler, args=(que, func, args), kwargs=kwargs)
p.start()
p.join(timeout=timeout)
ret_val = None
# pylint: disable=used-before-assignment,undefined-variable
if not p.is_alive():
ret_val = que.get()
else:
self.log.debug("terminate function (%s: %s // %s) after timeout is exceeded", func.__name__, args, kwargs)
p.terminate()
p.join()
p.close()
return ret_val
def post_search(self, request: "SXNG_Request", search: "SearchWithPlugins") -> EngineResults:
results = EngineResults()
# only show the result of the expression on the first page
if search.search_query.pageno > 1:
return results
query = search.search_query.query
# in order to avoid DoS attacks with long expressions, ignore long expressions
if len(query) > 100:
return results
# replace commonly used math operators with their proper Python operator
query = query.replace("x", "*").replace(":", "/")
# Is this a term that can be calculated?
word, constants = "", set()
for x in query:
# Alphabetic characters are defined as "Letters" in the Unicode
# character database and are the constants in an equation.
if x.isalpha():
word += x.strip()
elif word:
constants.add(word)
word = ""
# In the term of an arithmetic operation there should be no other
# alphabetic characters besides the constants
if constants - set(math_constants):
return results
# use UI language
ui_locale = babel.Locale.parse(request.preferences.get_value("locale"), sep="-")
# parse the number system in a localized way
def _decimal(match: re.Match) -> str:
val = match.string[match.start() : match.end()]
val = babel.numbers.parse_decimal(val, ui_locale, numbering_system="latn")
return str(val)
decimal = ui_locale.number_symbols["latn"]["decimal"]
group = ui_locale.number_symbols["latn"]["group"]
query = re.sub(f"[0-9]+[{decimal}|{group}][0-9]+[{decimal}|{group}]?[0-9]?", _decimal, query)
# in python, powers are calculated via **
query_py_formatted = query.replace("^", "**")
# Prevent the runtime from being longer than 50 ms
res = self.timeout_func(0.05, _eval_expr, query_py_formatted)
if res is None or res[0] == "":
return results
res, is_boolean = res
if is_boolean:
res = "True" if res != 0 else "False"
else:
res = babel.numbers.format_decimal(res, locale=ui_locale)
results.add(results.types.Answer(answer=f"{search.search_query.query} = {res}"))
return results
def _compare(ops: list[ast.cmpop], values: list[int | float]) -> int:
"""
2 < 3 becomes ops=[ast.Lt] and values=[2,3]
2 < 3 <= 4 becomes ops=[ast.Lt, ast.LtE] and values=[2,3, 4]
"""
for op, a, b in zip(ops, values, values[1:]): # pylint: disable=invalid-name
if isinstance(op, ast.Eq) and a == b:
continue
if isinstance(op, ast.NotEq) and a != b:
continue
if isinstance(op, ast.Lt) and a < b:
continue
if isinstance(op, ast.LtE) and a <= b:
continue
if isinstance(op, ast.Gt) and a > b:
continue
if isinstance(op, ast.GtE) and a >= b:
continue
# Ignore impossible ops:
# * ast.Is
# * ast.IsNot
# * ast.In
# * ast.NotIn
# the result is False for a and b and operation op
return 0
# the results for all the ops are True
return 1
operators: dict[type, typing.Callable] = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.BitXor: operator.xor,
ast.BitOr: operator.or_,
ast.BitAnd: operator.and_,
ast.USub: operator.neg,
ast.RShift: operator.rshift,
ast.LShift: operator.lshift,
ast.Mod: operator.mod,
ast.Compare: _compare,
}
math_constants = {
'e': math.e,
'pi': math.pi,
}
# with multiprocessing.get_context("fork") we are ready for Py3.14 (by emulating
# the old behavior "fork") but it will not solve the core problem of fork, nor
# will it remove the deprecation warnings in py3.12 & py3.13. Issue is
# ddiscussed here: https://github.com/searxng/searxng/issues/4159
mp_fork = multiprocessing.get_context("fork")
def _eval_expr(expr):
"""
Evaluates the given textual expression.
Returns a tuple of (numericResult, isBooleanResult).
>>> _eval_expr('2^6')
64, False
>>> _eval_expr('2**6')
64, False
>>> _eval_expr('1 + 2*3**(4^5) / (6 + -7)')
-5.0, False
>>> _eval_expr('1 < 3')
1, True
>>> _eval_expr('5 < 3')
0, True
>>> _eval_expr('17 == 11+1+5 == 7+5+5')
1, True
"""
try:
root_expr = ast.parse(expr, mode='eval').body
return _eval(root_expr), isinstance(root_expr, ast.Compare)
except (SyntaxError, TypeError, ZeroDivisionError):
# Expression that can't be evaluated (i.e. not a math expression)
return "", False
def _eval(node):
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp):
return operators[type(node.op)](_eval(node.left), _eval(node.right))
if isinstance(node, ast.UnaryOp):
return operators[type(node.op)](_eval(node.operand))
if isinstance(node, ast.Compare):
return _compare(node.ops, [_eval(node.left)] + [_eval(c) for c in node.comparators])
if isinstance(node, ast.Name) and node.id in math_constants:
return math_constants[node.id]
raise TypeError(node)
def handler(q: multiprocessing.Queue, func, args, **kwargs): # pylint:disable=invalid-name
try:
q.put(func(*args, **kwargs))
except:
q.put(None)
raise

View File

@@ -0,0 +1,28 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring
import typing as t
from flask_babel import gettext # pyright: ignore[reportUnknownVariableType]
from searx.plugins import Plugin, PluginInfo
if t.TYPE_CHECKING:
from searx.plugins import PluginCfg
@t.final
class SXNGPlugin(Plugin):
"""Automatically loads the next page when scrolling to bottom of the current page."""
id = "infiniteScroll"
def __init__(self, plg_cfg: "PluginCfg") -> None:
super().__init__(plg_cfg)
self.info = PluginInfo(
id=self.id,
name=gettext("Infinite scroll"),
description=gettext("Automatically loads the next page when scrolling to bottom of the current page"),
preference_section="ui",
)

View File

@@ -24,12 +24,6 @@ if typing.TYPE_CHECKING:
from searx.plugins import PluginCfg
name = ""
description = gettext("")
plugin_id = ""
preference_section = ""
CONVERT_KEYWORDS = ["in", "to", "as"]

View File

@@ -476,10 +476,6 @@ class Preferences:
settings['ui']['query_in_title'],
locked=is_locked('query_in_title')
),
'infinite_scroll': BooleanSetting(
settings['ui']['infinite_scroll'],
locked=is_locked('infinite_scroll')
),
'search_on_category_select': BooleanSetting(
settings['ui']['search_on_category_select'],
locked=is_locked('search_on_category_select')

View File

@@ -124,8 +124,6 @@ ui:
# query_in_title: When true, the result page's titles contains the query
# it decreases the privacy, since the browser can records the page titles.
query_in_title: false
# infinite_scroll: When true, automatically loads the next page when scrolling to bottom of the current page.
infinite_scroll: false
# ui theme
default_theme: simple
# center the results ?
@@ -162,7 +160,6 @@ ui:
# - locale
# - theme
# - results_on_new_tab
# - infinite_scroll
# - search_on_category_select
# - method
# - image_proxy
@@ -214,6 +211,7 @@ outgoing:
# - 1.1.1.2
# - fe80::/126
# Plugin configuration, for more details see
# https://docs.searxng.org/admin/settings/settings_plugins.html
#
@@ -222,6 +220,9 @@ plugins:
searx.plugins.calculator.SXNGPlugin:
active: true
searx.plugins.infinite_scroll.SXNGPlugin:
active: false
searx.plugins.hash_plugin.SXNGPlugin:
active: true
@@ -1292,6 +1293,11 @@ engines:
require_api_key: false
results: HTML
- name: lucide
engine: lucide
shortcut: luc
timeout: 3.0
- name: marginalia
engine: marginalia
shortcut: mar
@@ -1616,6 +1622,7 @@ engines:
engine: pixiv
disabled: true
inactive: true
remove_ai_images: false
pixiv_image_proxies:
- https://pximg.example.org
# A proxy is required to load the images. Hosting an image proxy server

View File

@@ -238,7 +238,6 @@ SCHEMA: dict[str, t.Any] = {
'results_on_new_tab': SettingsValue(bool, False),
'advanced_search': SettingsValue(bool, False),
'query_in_title': SettingsValue(bool, False),
'infinite_scroll': SettingsValue(bool, False),
'cache_url': SettingsValue(str, 'https://web.archive.org/web/'),
'search_on_category_select': SettingsValue(bool, True),
'hotkeys': SettingsValue(('default', 'vim'), 'default'),

View File

@@ -0,0 +1,2 @@
const e=e=>{if(!e)throw Error(`DOM element not found`)};export{e as t};
//# sourceMappingURL=13gvpunf.min.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"13gvpunf.min.js","names":["assertElement: AssertElement"],"sources":["../../../../../client/simple/src/js/util/assertElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\ntype AssertElement = <T>(element?: T | null) => asserts element is T;\nexport const assertElement: AssertElement = <T>(element?: T | null): asserts element is T => {\n if (!element) {\n throw new Error(\"DOM element not found\");\n }\n};\n"],"mappings":"AAGA,MAAaA,EAAmC,GAA6C,CAC3F,GAAI,CAAC,EACH,MAAU,MAAM,wBAAwB"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import{i as e,n as t,t as n}from"../sxng-core.min.js";import{t as r}from"./13gvpunf.min.js";var i,a=async()=>{if(!i){try{i=await(await n(`GET`,`engine_descriptions.json`)).json()}catch(e){console.error(`Error fetching engineDescriptions:`,e)}if(i)for(let[t,[n,r]]of Object.entries(i)){let i=document.querySelectorAll(`[data-engine-name="${t}"] .engine-description`),a=` (<i>${e.translations?.Source}:&nbsp;${r}</i>)`;for(let e of i)e.innerHTML=n+a}}},o=(e,t)=>{for(let n of t)n.offsetParent&&(n.checked=!e)},s=document.querySelectorAll(`[data-engine-name]`);for(let e of s)t(`mouseenter`,e,a);var c=document.querySelectorAll(`tbody input[type=checkbox][class~=checkbox-onoff]`),l=document.querySelectorAll(`.enable-all-engines`);for(let e of l)t(`click`,e,()=>o(!0,c));var u=document.querySelectorAll(`.disable-all-engines`);for(let e of u)t(`click`,e,()=>o(!1,c));t(`click`,`#copy-hash`,async function(){let e=this.parentElement?.querySelector(`pre`);if(r(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}let t=this.dataset.copiedText;t&&(this.innerText=t)});
//# sourceMappingURL=CyyZ9XJS.min.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CyyZ9XJS.min.js","names":["engineDescriptions: Record<string, [string, string]> | undefined","engineElements: NodeListOf<HTMLElement>","engineToggles: NodeListOf<HTMLInputElement>","enableAllEngines: NodeListOf<HTMLElement>","disableAllEngines: NodeListOf<HTMLElement>"],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { http, listen, settings } from \"../toolkit.ts\";\nimport { assertElement } from \"../util/assertElement.ts\";\n\nlet engineDescriptions: Record<string, [string, string]> | undefined;\n\nconst loadEngineDescriptions = async (): Promise<void> => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll<HTMLElement>(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (<i>${settings.translations?.Source}:&nbsp;${source}</i>)`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf<HTMLInputElement>): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf<HTMLInputElement> = document.querySelectorAll<HTMLInputElement>(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector<HTMLPreElement>(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n const copiedText = this.dataset.copiedText;\n if (copiedText) {\n this.innerText = copiedText;\n }\n});\n"],"mappings":"4FAKA,IAAIA,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MADT,MAAM,EAAK,MAAO,2BAA2B,EAC1B,MAAM,OAC9B,EAAO,CACd,QAAQ,MAAM,qCAAsC,EAAM,CAEvD,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,EAAmB,CAAE,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,wBAAwB,CAC5G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,KAKlC,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,IAKxBC,EAA0C,SAAS,iBAA8B,qBAAqB,CAC5G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,EAAuB,CAG7D,IAAMC,EAA8C,SAAS,iBAC3D,oDACD,CAEKC,EAA4C,SAAS,iBAA8B,sBAAsB,CAC/G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,EAAc,CAAC,CAGnE,IAAMC,EAA6C,SAAS,iBAA8B,uBAAuB,CACjH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,EAAc,CAAC,CAGpE,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAM,EAAS,KAAK,eAAe,cAA8B,MAAM,CAGvE,GAFA,EAAc,EAAO,CAEjB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,UAAU,KAChD,CACL,IAAM,EAAY,OAAO,cAAc,CACvC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,aAAa,CACpC,EAAM,mBAAmB,EAAO,CAChC,EAAU,iBAAiB,CAC3B,EAAU,SAAS,EAAM,CACzB,SAAS,YAAY,OAAO,EAIhC,IAAM,EAAa,KAAK,QAAQ,WAC5B,IACF,KAAK,UAAY,IAEnB"}

View File

@@ -0,0 +1,2 @@
import{a as e,i as t,t as n}from"../sxng-core.min.js";import{t as r}from"./13gvpunf.min.js";import{t as i}from"./gZqIRpW1.min.js";var a=class extends e{constructor(){super(`infiniteScroll`)}async run(){let e=i(`results`).classList.contains(`only_template_images`),a=`article.result:last-child`,o=document.createElement(`div`);o.className=`loader`;let s=async i=>{let a=document.querySelector(`#search`);r(a);let s=document.querySelector(`#pagination form.next_page`);r(s);let c=a.getAttribute(`action`);if(!c)throw Error(`Form action not defined`);let l=document.querySelector(`#pagination`);r(l),l.replaceChildren(o);try{let t=await(await n(`POST`,c,{body:new FormData(s)})).text();if(!t)return;let r=new DOMParser().parseFromString(t,`text/html`),a=r.querySelectorAll(`#urls article`),o=r.querySelector(`#pagination`);document.querySelector(`#pagination`)?.remove();let l=document.querySelector(`#urls`);if(!l)throw Error(`URLs element not found`);a.length>0&&!e&&l.appendChild(document.createElement(`hr`)),l.append(...a),o&&(document.querySelector(`#results`)?.appendChild(o),i())}catch(e){console.error(`Error loading next page:`,e);let n=Object.assign(document.createElement(`div`),{textContent:t.translations?.error_loading_next_page??`Error loading next page`,className:`dialog-error`});n.setAttribute(`role`,`alert`),document.querySelector(`#pagination`)?.replaceChildren(n)}},c=new IntersectionObserver(async e=>{let[t]=e;t?.isIntersecting&&(c.unobserve(t.target),await s(()=>{let e=document.querySelector(a);e&&c.observe(e)}))},{rootMargin:`320px`}),l=document.querySelector(a);l&&c.observe(l)}async post(){}};export{a as default};
//# sourceMappingURL=DBO1tjH7.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import{i as e,n as t,t as n}from"../sxng-core.min.js";import{t as r}from"./13gvpunf.min.js";var i=async(i,a)=>{try{let o;o=e.method===`GET`?await n(`GET`,`./autocompleter?q=${a}`):await n(`POST`,`./autocompleter`,{body:new URLSearchParams({q:a})});let s=await o.json(),c=document.querySelector(`.autocomplete`);r(c);let l=document.querySelector(`.autocomplete ul`);if(r(l),c.classList.add(`open`),l.replaceChildren(),s?.[1]?.length===0){let t=Object.assign(document.createElement(`li`),{className:`no-item-found`,textContent:e.translations?.no_item_found??`No results found`});l.append(t);return}let u=new DocumentFragment;for(let e of s[1]){let n=Object.assign(document.createElement(`li`),{textContent:e});t(`mousedown`,n,()=>{i.value=e,document.querySelector(`#search`)?.submit(),c.classList.remove(`open`)}),u.append(n)}l.append(u)}catch(e){console.error(`Error fetching autocomplete results:`,e)}},a=document.getElementById(`q`);r(a);var o;t(`input`,a,()=>{clearTimeout(o);let t=a.value,n=e.autocomplete_min??2;t.length<n||(o=window.setTimeout(async()=>{t===a.value&&await i(a,t)},300))});var s=document.querySelector(`.autocomplete`),c=document.querySelector(`.autocomplete ul`);c&&t(`keyup`,a,e=>{let t=[...c.children],n=t.findIndex(e=>e.classList.contains(`active`)),r=-1;switch(e.key){case`ArrowUp`:{let e=t[n];e&&n>=0&&e.classList.remove(`active`),r=(n-1+t.length)%t.length;break}case`ArrowDown`:{let e=t[n];e&&n>=0&&e.classList.remove(`active`),r=(n+1)%t.length;break}case`Tab`:case`Enter`:s&&s.classList.remove(`open`);break;default:break}if(r!==-1){let e=t[r];if(e&&(e.classList.add(`active`),!e.classList.contains(`no-item-found`))){let t=document.getElementById(`q`);t&&(t.value=e.textContent??``)}}});
//# sourceMappingURL=KPZlR0ib.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import{i as e,n as t,r as n}from"../sxng-core.min.js";import{t as r}from"./13gvpunf.min.js";(function(e,t){typeof e.CustomEvent!=`function`&&(e.CustomEvent=function(e,n){n||={bubbles:!1,cancelable:!1,detail:void 0};var r=t.createEvent(`CustomEvent`);return r.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),r},e.CustomEvent.prototype=e.Event.prototype),t.addEventListener(`touchstart`,u,!1),t.addEventListener(`touchmove`,d,!1),t.addEventListener(`touchend`,l,!1);var n=null,r=null,i=null,a=null,o=null,s=null,c=0;function l(e){if(s===e.target){var l=parseInt(f(s,`data-swipe-threshold`,`20`),10),u=f(s,`data-swipe-unit`,`px`),d=parseInt(f(s,`data-swipe-timeout`,`500`),10),p=Date.now()-o,m=``,h=e.changedTouches||e.touches||[];if(u===`vh`&&(l=Math.round(l/100*t.documentElement.clientHeight)),u===`vw`&&(l=Math.round(l/100*t.documentElement.clientWidth)),Math.abs(i)>Math.abs(a)?Math.abs(i)>l&&p<d&&(m=i>0?`swiped-left`:`swiped-right`):Math.abs(a)>l&&p<d&&(m=a>0?`swiped-up`:`swiped-down`),m!==``){var g={dir:m.replace(/swiped-/,``),touchType:(h[0]||{}).touchType||`direct`,fingers:c,xStart:parseInt(n,10),xEnd:parseInt((h[0]||{}).clientX||-1,10),yStart:parseInt(r,10),yEnd:parseInt((h[0]||{}).clientY||-1,10)};s.dispatchEvent(new CustomEvent(`swiped`,{bubbles:!0,cancelable:!0,detail:g})),s.dispatchEvent(new CustomEvent(m,{bubbles:!0,cancelable:!0,detail:g}))}n=null,r=null,o=null}}function u(e){e.target.getAttribute(`data-swipe-ignore`)!==`true`&&(s=e.target,o=Date.now(),n=e.touches[0].clientX,r=e.touches[0].clientY,i=0,a=0,c=e.touches.length)}function d(e){if(!(!n||!r)){var t=e.touches[0].clientX,o=e.touches[0].clientY;i=n-t,a=r-o}}function f(e,n,r){for(;e&&e!==t.documentElement;){var i=e.getAttribute(n);if(i)return i;e=e.parentNode}return r}})(window,document);var i,a=t=>{i&&clearTimeout(i);let n=t.querySelector(`.result-images-source img`);if(!n)return;let r=t.querySelector(`.image_thumbnail`);if(r){if(r.src===`${e.theme_static_path}/img/img_load_error.svg`)return;n.onerror=()=>{n.src=r.src},n.src=r.src}let a=n.getAttribute(`data-src`);a&&(i=setTimeout(()=>{n.src=a,n.removeAttribute(`data-src`)},1e3))},o=document.querySelectorAll(`#urls img.image_thumbnail`);for(let t of o)t.complete&&t.naturalWidth===0&&(t.src=`${e.theme_static_path}/img/img_load_error.svg`),t.onerror=()=>{t.src=`${e.theme_static_path}/img/img_load_error.svg`};document.querySelector(`#search_url button#copy_url`)?.style.setProperty(`display`,`block`),n.selectImage=e=>{document.getElementById(`results`)?.classList.add(`image-detail-open`),window.location.hash=`#image-viewer`,n.scrollPageToSelected?.(),e&&a(e)},n.closeDetail=()=>{document.getElementById(`results`)?.classList.remove(`image-detail-open`),window.location.hash===`#image-viewer`&&window.history.back(),n.scrollPageToSelected?.()},t(`click`,`.btn-collapse`,function(){let e=this.getAttribute(`data-btn-text-collapsed`),t=this.getAttribute(`data-btn-text-not-collapsed`),n=this.getAttribute(`data-target`);if(!(n&&e&&t))return;let i=document.querySelector(n);r(i);let a=this.classList.contains(`collapsed`),o=a?t:e,s=a?e:t;this.innerHTML=this.innerHTML.replace(s,o),this.classList.toggle(`collapsed`),i.classList.toggle(`invisible`)}),t(`click`,`.media-loader`,function(){let e=this.getAttribute(`data-target`);if(!e)return;let t=document.querySelector(`${e} > iframe`);if(r(t),!t.getAttribute(`src`)){let e=t.getAttribute(`data-src`);e&&t.setAttribute(`src`,e)}}),t(`click`,`#copy_url`,async function(){let e=this.parentElement?.querySelector(`pre`);if(r(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}let t=this.dataset.copiedText;t&&(this.innerText=t)}),t(`click`,`.result-detail-close`,e=>{e.preventDefault(),n.closeDetail?.()}),t(`click`,`.result-detail-previous`,e=>{e.preventDefault(),n.selectPrevious?.(!1)}),t(`click`,`.result-detail-next`,e=>{e.preventDefault(),n.selectNext?.(!1)}),window.addEventListener(`hashchange`,()=>{window.location.hash!==`#image-viewer`&&n.closeDetail?.()});var s=document.querySelectorAll(`.swipe-horizontal`);for(let e of s)t(`swiped-left`,e,()=>{n.selectNext?.(!1)}),t(`swiped-right`,e,()=>{n.selectPrevious?.(!1)});window.addEventListener(`scroll`,()=>{let e=document.getElementById(`backToTop`),t=document.getElementById(`results`);if(e&&t){let e=(document.documentElement.scrollTop||document.body.scrollTop)>=100;t.classList.toggle(`scrolling`,e)}},!0);
//# sourceMappingURL=Q2SRo2ED.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
import{n as e}from"../sxng-core.min.js";import{t}from"./gZqIRpW1.min.js";var n=t(`search`),r=t(`q`),i=t(`clear_search`),a=window.matchMedia(`(max-width: 50em)`).matches,o=document.querySelector(`main`)?.id===`main_results`,s=Array.from(document.querySelectorAll(`#categories_container button.category`));r.value.length===0&&i.classList.add(`empty`),a||o||r.focus(),a&&e(`focus`,r,()=>{requestAnimationFrame(()=>{let e=r.value.length;r.setSelectionRange(e,e),r.scrollLeft=r.scrollWidth})}),e(`input`,r,()=>{i.classList.toggle(`empty`,r.value.length===0)}),e(`click`,i,e=>{e.preventDefault(),r.value=``,r.focus(),i.classList.add(`empty`)});for(let t of s)e(`click`,t,e=>{if(e.shiftKey){e.preventDefault(),t.classList.toggle(`selected`);return}for(let e of s)e.classList.toggle(`selected`,e===t)});if(document.querySelector(`div.search_filters`)){let t=document.getElementById(`safesearch`);t&&e(`change`,t,()=>n.submit());let r=document.getElementById(`time_range`);r&&e(`change`,r,()=>n.submit());let i=document.getElementById(`language`);i&&e(`change`,i,()=>n.submit())}e(`submit`,n,e=>{if(e.preventDefault(),s.length>0){let e=t(`selected-categories`);e.value=s.filter(e=>e.classList.contains(`selected`)).map(e=>e.name.replace(`category_`,``)).join(`,`)}n.submit()});
//# sourceMappingURL=Q2W2fJLA.min.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Q2W2fJLA.min.js","names":["searchForm: HTMLFormElement","searchInput: HTMLInputElement","searchReset: HTMLButtonElement","isMobile: boolean","isResultsPage: boolean","categoryButtons: HTMLButtonElement[]"],"sources":["../../../../../client/simple/src/js/main/search.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { listen } from \"../toolkit.ts\";\nimport { getElement } from \"../util/getElement.ts\";\n\nconst searchForm: HTMLFormElement = getElement<HTMLFormElement>(\"search\");\nconst searchInput: HTMLInputElement = getElement<HTMLInputElement>(\"q\");\nconst searchReset: HTMLButtonElement = getElement<HTMLButtonElement>(\"clear_search\");\n\nconst isMobile: boolean = window.matchMedia(\"(max-width: 50em)\").matches;\nconst isResultsPage: boolean = document.querySelector(\"main\")?.id === \"main_results\";\n\nconst categoryButtons: HTMLButtonElement[] = Array.from(\n document.querySelectorAll<HTMLButtonElement>(\"#categories_container button.category\")\n);\n\nif (searchInput.value.length === 0) {\n searchReset.classList.add(\"empty\");\n}\n\n// focus search input on large screens\nif (!(isMobile || isResultsPage)) {\n searchInput.focus();\n}\n\n// On mobile, move cursor to the end of the input on focus\nif (isMobile) {\n listen(\"focus\", searchInput, () => {\n // Defer cursor move until the next frame to prevent a visual jump\n requestAnimationFrame(() => {\n const end = searchInput.value.length;\n searchInput.setSelectionRange(end, end);\n searchInput.scrollLeft = searchInput.scrollWidth;\n });\n });\n}\n\nlisten(\"input\", searchInput, () => {\n searchReset.classList.toggle(\"empty\", searchInput.value.length === 0);\n});\n\nlisten(\"click\", searchReset, (event: MouseEvent) => {\n event.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n searchReset.classList.add(\"empty\");\n});\n\nfor (const button of categoryButtons) {\n listen(\"click\", button, (event: MouseEvent) => {\n if (event.shiftKey) {\n event.preventDefault();\n button.classList.toggle(\"selected\");\n return;\n }\n\n // deselect all other categories\n for (const categoryButton of categoryButtons) {\n categoryButton.classList.toggle(\"selected\", categoryButton === button);\n }\n });\n}\n\nif (document.querySelector(\"div.search_filters\")) {\n const safesearchElement = document.getElementById(\"safesearch\");\n if (safesearchElement) {\n listen(\"change\", safesearchElement, () => searchForm.submit());\n }\n\n const timeRangeElement = document.getElementById(\"time_range\");\n if (timeRangeElement) {\n listen(\"change\", timeRangeElement, () => searchForm.submit());\n }\n\n const languageElement = document.getElementById(\"language\");\n if (languageElement) {\n listen(\"change\", languageElement, () => searchForm.submit());\n }\n}\n\n// override searchForm submit event\nlisten(\"submit\", searchForm, (event: Event) => {\n event.preventDefault();\n\n if (categoryButtons.length > 0) {\n const searchCategories = getElement<HTMLInputElement>(\"selected-categories\");\n searchCategories.value = categoryButtons\n .filter((button) => button.classList.contains(\"selected\"))\n .map((button) => button.name.replace(\"category_\", \"\"))\n .join(\",\");\n }\n\n searchForm.submit();\n});\n"],"mappings":"yEAKA,IAAMA,EAA8B,EAA4B,SAAS,CACnEC,EAAgC,EAA6B,IAAI,CACjEC,EAAiC,EAA8B,eAAe,CAE9EC,EAAoB,OAAO,WAAW,oBAAoB,CAAC,QAC3DC,EAAyB,SAAS,cAAc,OAAO,EAAE,KAAO,eAEhEC,EAAuC,MAAM,KACjD,SAAS,iBAAoC,wCAAwC,CACtF,CAEG,EAAY,MAAM,SAAW,GAC/B,EAAY,UAAU,IAAI,QAAQ,CAI9B,GAAY,GAChB,EAAY,OAAO,CAIjB,GACF,EAAO,QAAS,MAAmB,CAEjC,0BAA4B,CAC1B,IAAM,EAAM,EAAY,MAAM,OAC9B,EAAY,kBAAkB,EAAK,EAAI,CACvC,EAAY,WAAa,EAAY,aACrC,EACF,CAGJ,EAAO,QAAS,MAAmB,CACjC,EAAY,UAAU,OAAO,QAAS,EAAY,MAAM,SAAW,EAAE,EACrE,CAEF,EAAO,QAAS,EAAc,GAAsB,CAClD,EAAM,gBAAgB,CACtB,EAAY,MAAQ,GACpB,EAAY,OAAO,CACnB,EAAY,UAAU,IAAI,QAAQ,EAClC,CAEF,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,EAAS,GAAsB,CAC7C,GAAI,EAAM,SAAU,CAClB,EAAM,gBAAgB,CACtB,EAAO,UAAU,OAAO,WAAW,CACnC,OAIF,IAAK,IAAM,KAAkB,EAC3B,EAAe,UAAU,OAAO,WAAY,IAAmB,EAAO,EAExE,CAGJ,GAAI,SAAS,cAAc,qBAAqB,CAAE,CAChD,IAAM,EAAoB,SAAS,eAAe,aAAa,CAC3D,GACF,EAAO,SAAU,MAAyB,EAAW,QAAQ,CAAC,CAGhE,IAAM,EAAmB,SAAS,eAAe,aAAa,CAC1D,GACF,EAAO,SAAU,MAAwB,EAAW,QAAQ,CAAC,CAG/D,IAAM,EAAkB,SAAS,eAAe,WAAW,CACvD,GACF,EAAO,SAAU,MAAuB,EAAW,QAAQ,CAAC,CAKhE,EAAO,SAAU,EAAa,GAAiB,CAG7C,GAFA,EAAM,gBAAgB,CAElB,EAAgB,OAAS,EAAG,CAC9B,IAAM,EAAmB,EAA6B,sBAAsB,CAC5E,EAAiB,MAAQ,EACtB,OAAQ,GAAW,EAAO,UAAU,SAAS,WAAW,CAAC,CACzD,IAAK,GAAW,EAAO,KAAK,QAAQ,YAAa,GAAG,CAAC,CACrD,KAAK,IAAI,CAGd,EAAW,QAAQ,EACnB"}

View File

@@ -0,0 +1,2 @@
import{t as e}from"./13gvpunf.min.js";function t(t,n={}){n.assert??=!0;let r=document.getElementById(t);return n.assert&&e(r),r}export{t};
//# sourceMappingURL=gZqIRpW1.min.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"gZqIRpW1.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/getElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement } from \"./assertElement.ts\";\n\ntype Options = {\n assert?: boolean;\n};\n\nexport function getElement<T>(id: string, options?: { assert: true }): T;\nexport function getElement<T>(id: string, options?: { assert: false }): T | null;\nexport function getElement<T>(id: string, options: Options = {}): T | null {\n options.assert ??= true;\n\n const element = document.getElementById(id) as T | null;\n\n if (options.assert) {\n assertElement(element);\n }\n\n return element;\n}\n"],"mappings":"sCAUA,SAAgB,EAAc,EAAY,EAAmB,EAAE,CAAY,CACzE,EAAQ,SAAW,GAEnB,IAAM,EAAU,SAAS,eAAe,EAAG,CAM3C,OAJI,EAAQ,QACV,EAAc,EAAQ,CAGjB"}

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
import{i as e,n as t,o as n,r}from"./searxng.core.min.js";var i=async(i,a)=>{try{let o;o=n.method===`GET`?await r(`GET`,`./autocompleter?q=${a}`):await r(`POST`,`./autocompleter`,{body:new URLSearchParams({q:a})});let s=await o.json(),c=document.querySelector(`.autocomplete`);t(c);let l=document.querySelector(`.autocomplete ul`);if(t(l),c.classList.add(`open`),l.replaceChildren(),s?.[1]?.length===0){let e=Object.assign(document.createElement(`li`),{className:`no-item-found`,textContent:n.translations?.no_item_found??`No results found`});l.append(e);return}let u=new DocumentFragment;for(let t of s[1]){let n=Object.assign(document.createElement(`li`),{textContent:t});e(`mousedown`,n,()=>{i.value=t,document.querySelector(`#search`)?.submit(),c.classList.remove(`open`)}),u.append(n)}l.append(u)}catch(e){console.error(`Error fetching autocomplete results:`,e)}},a=document.getElementById(`q`);t(a);var o;e(`input`,a,()=>{clearTimeout(o);let e=a.value,t=n.autocomplete_min??2;e.length<t||(o=window.setTimeout(async()=>{e===a.value&&await i(a,e)},300))});var s=document.querySelector(`.autocomplete`),c=document.querySelector(`.autocomplete ul`);c&&e(`keyup`,a,e=>{let t=[...c.children],n=t.findIndex(e=>e.classList.contains(`active`)),r=-1;switch(e.key){case`ArrowUp`:{let e=t[n];e&&n>=0&&e.classList.remove(`active`),r=(n-1+t.length)%t.length;break}case`ArrowDown`:{let e=t[n];e&&n>=0&&e.classList.remove(`active`),r=(n+1)%t.length;break}case`Tab`:case`Enter`:s&&s.classList.remove(`open`);break;default:break}if(r!==-1){let e=t[r];if(e&&(e.classList.add(`active`),!e.classList.contains(`no-item-found`))){let t=document.getElementById(`q`);t&&(t.value=e.textContent??``)}}});
//# sourceMappingURL=autocomplete.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;

View File

@@ -1,2 +0,0 @@
import{n as e,o as t,r as n}from"./searxng.core.min.js";var r=()=>Object.assign(document.createElement(`div`),{className:`loader`}),i=async(i,a)=>{let o=document.querySelector(`#search`);e(o);let s=document.querySelector(`#pagination form.next_page`);e(s);let c=o.getAttribute(`action`);if(!c)throw Error(`Form action not defined`);let l=document.querySelector(`#pagination`);e(l),l.replaceChildren(r());try{let e=await(await n(`POST`,c,{body:new FormData(s)})).text();if(!e)return;let t=new DOMParser().parseFromString(e,`text/html`),r=t.querySelectorAll(`#urls article`),o=t.querySelector(`#pagination`);document.querySelector(`#pagination`)?.remove();let l=document.querySelector(`#urls`);if(!l)throw Error(`URLs element not found`);r.length>0&&!i&&l.appendChild(document.createElement(`hr`)),l.append(...Array.from(r)),o&&(document.querySelector(`#results`)?.appendChild(o),a())}catch(e){console.error(`Error loading next page:`,e);let n=Object.assign(document.createElement(`div`),{textContent:t.translations?.error_loading_next_page??`Error loading next page`,className:`dialog-error`});n.setAttribute(`role`,`alert`),document.querySelector(`#pagination`)?.replaceChildren(n)}},a=document.getElementById(`results`);if(!a)throw Error(`Results element not found`);var o=a.classList.contains(`only_template_images`),s=`article.result:last-child`,c=new IntersectionObserver(e=>{let[t]=e;t?.isIntersecting&&(c.unobserve(t.target),i(o,()=>{let e=document.querySelector(s);e&&c.observe(e)}).then(()=>{}))},{rootMargin:`320px`}),l=document.querySelector(s);l&&c.observe(l);
//# sourceMappingURL=infinite_scroll.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["css/ol.min.css"])))=>i.map(i=>d[i]);
import{i as e,t}from"./searxng.core.min.js";e(`click`,`.searxng_init_map`,async function(e){e.preventDefault(),this.classList.remove(`searxng_init_map`);let{View:n,OlMap:r,TileLayer:i,VectorLayer:a,OSM:o,VectorSource:s,Style:c,Stroke:l,Fill:u,Circle:d,fromLonLat:f,GeoJSON:p,Feature:m,Point:h}=await t(async()=>{let{View:e,OlMap:t,TileLayer:n,VectorLayer:r,OSM:i,VectorSource:a,Style:o,Stroke:s,Fill:c,Circle:l,fromLonLat:u,GeoJSON:d,Feature:f,Point:p}=await import(`./ol.min.js`);return{View:e,OlMap:t,TileLayer:n,VectorLayer:r,OSM:i,VectorSource:a,Style:o,Stroke:s,Fill:c,Circle:l,fromLonLat:u,GeoJSON:d,Feature:f,Point:p}},[]);t(()=>Promise.resolve({}),__vite__mapDeps([0]));let{leafletTarget:g,mapLon:_,mapLat:v,mapGeojson:y}=this.dataset,b=Number.parseFloat(_||`0`),x=Number.parseFloat(v||`0`),S=new n({maxZoom:16,enableRotation:!1}),C=new r({target:g,layers:[new i({source:new o({maxZoom:16})})],view:S});try{let e=new a({source:new s({features:[new m({geometry:new h(f([b,x]))})]}),style:new c({image:new d({radius:6,fill:new u({color:`#3050ff`})})})});C.addLayer(e)}catch(e){console.error(`Failed to create marker layer:`,e)}if(y)try{let e=new s({features:new p().readFeatures(JSON.parse(y),{dataProjection:`EPSG:4326`,featureProjection:`EPSG:3857`})}),t=new a({source:e,style:new c({stroke:new l({color:`#3050ff`,width:2}),fill:new u({color:`#3050ff33`})})});C.addLayer(t),S.fit(e.getExtent(),{padding:[20,20,20,20]})}catch(e){console.error(`Failed to create GeoJSON layer:`,e)}});
//# sourceMappingURL=mapresult.min.js.map

View File

@@ -1 +0,0 @@
{"version":3,"mappings":";4CAIA,EAAO,QAAS,oBAAqB,eAAmC,EAAc,CACpF,EAAM,gBAAgB,CACtB,KAAK,UAAU,OAAO,mBAAmB,CAEzC,GAAM,CACJ,OACA,QACA,YACA,cACA,MACA,eACA,QACA,SACA,OACA,SACA,aACA,UACA,UACA,8BAdI,CACJ,OACA,QACA,YACA,cACA,MACA,eACA,QACA,SACA,OACA,SACA,aACA,UACA,UACA,SACE,MAAM,OAAO,sBAdf,OACA,QACA,YACA,cACA,MACA,eACA,QACA,SACA,OACA,SACA,aACA,UACA,UACA,cAEF,MAAK,mBAAO,uBAEZ,GAAM,CAAE,cAAe,EAAQ,SAAQ,SAAQ,cAAe,KAAK,QAE7D,EAAM,OAAO,WAAW,GAAU,IAAI,CACtC,EAAM,OAAO,WAAW,GAAU,IAAI,CACtC,EAAO,IAAI,EAAK,CAAE,QAAS,GAAI,eAAgB,GAAO,CAAC,CACvD,EAAM,IAAI,EAAM,CACZ,SACR,OAAQ,CAAC,IAAI,EAAU,CAAE,OAAQ,IAAI,EAAI,CAAE,QAAS,GAAI,CAAC,CAAE,CAAC,CAAC,CACvD,OACP,CAAC,CAEF,GAAI,CASF,IAAM,EAAc,IAAI,EAAY,CAClC,OATmB,IAAI,EAAa,CACpC,SAAU,CACR,IAAI,EAAQ,CACV,SAAU,IAAI,EAAM,EAAW,CAAC,EAAK,EAAI,CAAC,CAAC,CAC5C,CAAC,CACH,CACF,CAAC,CAIA,MAAO,IAAI,EAAM,CACf,MAAO,IAAI,EAAO,CAChB,OAAQ,EACR,KAAM,IAAI,EAAK,CAAE,MAAO,UAAW,CAAC,CACrC,CAAC,CACH,CAAC,CACH,CAAC,CAEF,EAAI,SAAS,EAAY,OAClB,EAAO,CACd,QAAQ,MAAM,iCAAkC,EAAM,CAGxD,GAAI,EACF,GAAI,CACF,IAAM,EAAY,IAAI,EAAa,CACjC,SAAU,IAAI,GAAS,CAAC,aAAa,KAAK,MAAM,EAAW,CAAE,CAC3D,eAAgB,YAChB,kBAAmB,YACpB,CAAC,CACH,CAAC,CAEI,EAAW,IAAI,EAAY,CAC/B,OAAQ,EACR,MAAO,IAAI,EAAM,CACf,OAAQ,IAAI,EAAO,CAAE,MAAO,UAAW,MAAO,EAAG,CAAC,CAClD,KAAM,IAAI,EAAK,CAAE,MAAO,YAAa,CAAC,CACvC,CAAC,CACH,CAAC,CAEF,EAAI,SAAS,EAAS,CAEtB,EAAK,IAAI,EAAU,WAAW,CAAE,CAAE,QAAS,CAAC,GAAI,GAAI,GAAI,GAAG,CAAE,CAAC,OACvD,EAAO,CACd,QAAQ,MAAM,kCAAmC,EAAM,GAG3D","names":[],"ignoreList":[],"sources":["../../../../../client/simple/src/js/main/mapresult.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { listen } from \"../core/toolkit.ts\";\n\nlisten(\"click\", \".searxng_init_map\", async function (this: HTMLElement, event: Event) {\n event.preventDefault();\n this.classList.remove(\"searxng_init_map\");\n\n const {\n View,\n OlMap,\n TileLayer,\n VectorLayer,\n OSM,\n VectorSource,\n Style,\n Stroke,\n Fill,\n Circle,\n fromLonLat,\n GeoJSON,\n Feature,\n Point\n } = await import(\"../pkg/ol.ts\");\n void import(\"ol/ol.css\");\n\n const { leafletTarget: target, mapLon, mapLat, mapGeojson } = this.dataset;\n\n const lon = Number.parseFloat(mapLon || \"0\");\n const lat = Number.parseFloat(mapLat || \"0\");\n const view = new View({ maxZoom: 16, enableRotation: false });\n const map = new OlMap({\n target: target,\n layers: [new TileLayer({ source: new OSM({ maxZoom: 16 }) })],\n view: view\n });\n\n try {\n const markerSource = new VectorSource({\n features: [\n new Feature({\n geometry: new Point(fromLonLat([lon, lat]))\n })\n ]\n });\n\n const markerLayer = new VectorLayer({\n source: markerSource,\n style: new Style({\n image: new Circle({\n radius: 6,\n fill: new Fill({ color: \"#3050ff\" })\n })\n })\n });\n\n map.addLayer(markerLayer);\n } catch (error) {\n console.error(\"Failed to create marker layer:\", error);\n }\n\n if (mapGeojson) {\n try {\n const geoSource = new VectorSource({\n features: new GeoJSON().readFeatures(JSON.parse(mapGeojson), {\n dataProjection: \"EPSG:4326\",\n featureProjection: \"EPSG:3857\"\n })\n });\n\n const geoLayer = new VectorLayer({\n source: geoSource,\n style: new Style({\n stroke: new Stroke({ color: \"#3050ff\", width: 2 }),\n fill: new Fill({ color: \"#3050ff33\" })\n })\n });\n\n map.addLayer(geoLayer);\n\n view.fit(geoSource.getExtent(), { padding: [20, 20, 20, 20] });\n } catch (error) {\n console.error(\"Failed to create GeoJSON layer:\", error);\n }\n }\n});\n"],"file":"mapresult.min.js"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
import{i as e,n as t,o as n,r}from"./searxng.core.min.js";var i,a=async()=>{if(!i){try{i=await(await r(`GET`,`engine_descriptions.json`)).json()}catch(e){console.error(`Error fetching engineDescriptions:`,e)}if(i)for(let[e,[t,r]]of Object.entries(i)){let i=document.querySelectorAll(`[data-engine-name="${e}"] .engine-description`),a=` (<i>${n.translations?.Source}:&nbsp;${r}</i>)`;for(let e of i)e.innerHTML=t+a}}},o=(e,t)=>{for(let n of t)n.offsetParent&&(n.checked=!e)},s=document.querySelectorAll(`[data-engine-name]`);for(let t of s)e(`mouseenter`,t,a);var c=document.querySelectorAll(`tbody input[type=checkbox][class~=checkbox-onoff]`),l=document.querySelectorAll(`.enable-all-engines`);for(let t of l)e(`click`,t,()=>o(!0,c));var u=document.querySelectorAll(`.disable-all-engines`);for(let t of u)e(`click`,t,()=>o(!1,c));e(`click`,`#copy-hash`,async function(){let e=this.parentElement?.querySelector(`pre`);if(t(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}let n=this.dataset.copiedText;n&&(this.innerText=n)});
//# sourceMappingURL=preferences.min.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"preferences.min.js","names":["engineDescriptions: Record<string, [string, string]> | undefined","engineElements: NodeListOf<HTMLElement>","engineToggles: NodeListOf<HTMLInputElement>","enableAllEngines: NodeListOf<HTMLElement>","disableAllEngines: NodeListOf<HTMLElement>"],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement, http, listen, settings } from \"../core/toolkit.ts\";\n\nlet engineDescriptions: Record<string, [string, string]> | undefined;\n\nconst loadEngineDescriptions = async (): Promise<void> => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll<HTMLElement>(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (<i>${settings.translations?.Source}:&nbsp;${source}</i>)`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf<HTMLInputElement>): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf<HTMLInputElement> = document.querySelectorAll<HTMLInputElement>(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector<HTMLPreElement>(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n const copiedText = this.dataset.copiedText;\n if (copiedText) {\n this.innerText = copiedText;\n }\n});\n"],"mappings":"0DAIA,IAAIA,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MADT,MAAM,EAAK,MAAO,2BAA2B,EAC1B,MAAM,OAC9B,EAAO,CACd,QAAQ,MAAM,qCAAsC,EAAM,CAEvD,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,EAAmB,CAAE,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,wBAAwB,CAC5G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,KAKlC,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,IAKxBC,EAA0C,SAAS,iBAA8B,qBAAqB,CAC5G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,EAAuB,CAG7D,IAAMC,EAA8C,SAAS,iBAC3D,oDACD,CAEKC,EAA4C,SAAS,iBAA8B,sBAAsB,CAC/G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,EAAc,CAAC,CAGnE,IAAMC,EAA6C,SAAS,iBAA8B,uBAAuB,CACjH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,EAAc,CAAC,CAGpE,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAM,EAAS,KAAK,eAAe,cAA8B,MAAM,CAGvE,GAFA,EAAc,EAAO,CAEjB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,UAAU,KAChD,CACL,IAAM,EAAY,OAAO,cAAc,CACvC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,aAAa,CACpC,EAAM,mBAAmB,EAAO,CAChC,EAAU,iBAAiB,CAC3B,EAAU,SAAS,EAAM,CACzB,SAAS,YAAY,OAAO,EAIhC,IAAM,EAAa,KAAK,QAAQ,WAC5B,IACF,KAAK,UAAY,IAEnB"}

View File

@@ -1,2 +0,0 @@
import{a as e,i as t,n,o as r}from"./searxng.core.min.js";(function(e,t){typeof e.CustomEvent!=`function`&&(e.CustomEvent=function(e,n){n||={bubbles:!1,cancelable:!1,detail:void 0};var r=t.createEvent(`CustomEvent`);return r.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),r},e.CustomEvent.prototype=e.Event.prototype),t.addEventListener(`touchstart`,u,!1),t.addEventListener(`touchmove`,d,!1),t.addEventListener(`touchend`,l,!1);var n=null,r=null,i=null,a=null,o=null,s=null,c=0;function l(e){if(s===e.target){var l=parseInt(f(s,`data-swipe-threshold`,`20`),10),u=f(s,`data-swipe-unit`,`px`),d=parseInt(f(s,`data-swipe-timeout`,`500`),10),p=Date.now()-o,m=``,h=e.changedTouches||e.touches||[];if(u===`vh`&&(l=Math.round(l/100*t.documentElement.clientHeight)),u===`vw`&&(l=Math.round(l/100*t.documentElement.clientWidth)),Math.abs(i)>Math.abs(a)?Math.abs(i)>l&&p<d&&(m=i>0?`swiped-left`:`swiped-right`):Math.abs(a)>l&&p<d&&(m=a>0?`swiped-up`:`swiped-down`),m!==``){var g={dir:m.replace(/swiped-/,``),touchType:(h[0]||{}).touchType||`direct`,fingers:c,xStart:parseInt(n,10),xEnd:parseInt((h[0]||{}).clientX||-1,10),yStart:parseInt(r,10),yEnd:parseInt((h[0]||{}).clientY||-1,10)};s.dispatchEvent(new CustomEvent(`swiped`,{bubbles:!0,cancelable:!0,detail:g})),s.dispatchEvent(new CustomEvent(m,{bubbles:!0,cancelable:!0,detail:g}))}n=null,r=null,o=null}}function u(e){e.target.getAttribute(`data-swipe-ignore`)!==`true`&&(s=e.target,o=Date.now(),n=e.touches[0].clientX,r=e.touches[0].clientY,i=0,a=0,c=e.touches.length)}function d(e){if(!(!n||!r)){var t=e.touches[0].clientX,o=e.touches[0].clientY;i=n-t,a=r-o}}function f(e,n,r){for(;e&&e!==t.documentElement;){var i=e.getAttribute(n);if(i)return i;e=e.parentNode}return r}})(window,document);var i,a=e=>{i&&clearTimeout(i);let t=e.querySelector(`.result-images-source img`);if(!t)return;let n=e.querySelector(`.image_thumbnail`);if(n){if(n.src===`${r.theme_static_path}/img/img_load_error.svg`)return;t.onerror=()=>{t.src=n.src},t.src=n.src}let a=t.getAttribute(`data-src`);a&&(i=setTimeout(()=>{t.src=a,t.removeAttribute(`data-src`)},1e3))},o=document.querySelectorAll(`#urls img.image_thumbnail`);for(let e of o)e.complete&&e.naturalWidth===0&&(e.src=`${r.theme_static_path}/img/img_load_error.svg`),e.onerror=()=>{e.src=`${r.theme_static_path}/img/img_load_error.svg`};document.querySelector(`#search_url button#copy_url`)?.style.setProperty(`display`,`block`),e.selectImage=t=>{document.getElementById(`results`)?.classList.add(`image-detail-open`),window.location.hash=`#image-viewer`,e.scrollPageToSelected?.(),t&&a(t)},e.closeDetail=()=>{document.getElementById(`results`)?.classList.remove(`image-detail-open`),window.location.hash===`#image-viewer`&&window.history.back(),e.scrollPageToSelected?.()},t(`click`,`.btn-collapse`,function(){let e=this.getAttribute(`data-btn-text-collapsed`),t=this.getAttribute(`data-btn-text-not-collapsed`),r=this.getAttribute(`data-target`);if(!(r&&e&&t))return;let i=document.querySelector(r);n(i);let a=this.classList.contains(`collapsed`),o=a?t:e,s=a?e:t;this.innerHTML=this.innerHTML.replace(s,o),this.classList.toggle(`collapsed`),i.classList.toggle(`invisible`)}),t(`click`,`.media-loader`,function(){let e=this.getAttribute(`data-target`);if(!e)return;let t=document.querySelector(`${e} > iframe`);if(n(t),!t.getAttribute(`src`)){let e=t.getAttribute(`data-src`);e&&t.setAttribute(`src`,e)}}),t(`click`,`#copy_url`,async function(){let e=this.parentElement?.querySelector(`pre`);if(n(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}let t=this.dataset.copiedText;t&&(this.innerText=t)}),t(`click`,`.result-detail-close`,t=>{t.preventDefault(),e.closeDetail?.()}),t(`click`,`.result-detail-previous`,t=>{t.preventDefault(),e.selectPrevious?.(!1)}),t(`click`,`.result-detail-next`,t=>{t.preventDefault(),e.selectNext?.(!1)}),window.addEventListener(`hashchange`,()=>{window.location.hash!==`#image-viewer`&&e.closeDetail?.()});var s=document.querySelectorAll(`.swipe-horizontal`);for(let n of s)t(`swiped-left`,n,()=>{e.selectNext?.(!1)}),t(`swiped-right`,n,()=>{e.selectPrevious?.(!1)});window.addEventListener(`scroll`,()=>{let e=document.getElementById(`backToTop`),t=document.getElementById(`results`);if(e&&t){let e=(document.documentElement.scrollTop||document.body.scrollTop)>=100;t.classList.toggle(`scrolling`,e)}},!0);
//# sourceMappingURL=results.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
import{i as e,n as t,o as n}from"./searxng.core.min.js";var r=e=>{e.value.length>0&&document.getElementById(`search`)?.submit()},i=(e,t)=>{t.classList.toggle(`empty`,e.value.length===0)},a=n=>{let r=document.getElementById(`clear_search`);t(r),i(n,r),e(`click`,r,e=>{e.preventDefault(),n.value=``,n.focus(),i(n,r)}),e(`input`,n,()=>i(n,r),{passive:!0})},o=document.getElementById(`q`);t(o);var s=window.matchMedia(`(max-width: 50em)`).matches,c=document.querySelector(`main`)?.id===`main_results`;if(s||c||o.focus(),s&&e(`focus`,o,()=>{requestAnimationFrame(()=>{let e=o.value.length;o.setSelectionRange(e,e),o.scrollLeft=o.scrollWidth})}),a(o),n.search_on_category_select&&document.querySelector(`.search_filters`)){let t=document.getElementById(`safesearch`);t&&e(`change`,t,()=>r(o));let n=document.getElementById(`time_range`);n&&e(`change`,n,()=>r(o));let i=document.getElementById(`language`);i&&e(`change`,i,()=>r(o))}var l=[...document.querySelectorAll(`button.category_button`)];for(let t of l)e(`click`,t,e=>{if(e.shiftKey){e.preventDefault(),t.classList.toggle(`selected`);return}for(let e of l)e.classList.toggle(`selected`,e===t)});var u=document.querySelector(`#search`);t(u),e(`submit`,u,e=>{e.preventDefault();let t=document.querySelector(`#selected-categories`);t&&(t.value=l.filter(e=>e.classList.contains(`selected`)).map(e=>e.name.replace(`category_`,``)).join(`,`)),u.submit()});
//# sourceMappingURL=search.min.js.map

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More