mirror of
https://github.com/searxng/searxng.git
synced 2026-07-25 01:11:24 +00:00
Compare commits
509 Commits
b8e4ebdc0c
...
update_dat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6707dc0b6 | ||
|
|
8605230eb8 | ||
|
|
a54722e692 | ||
|
|
7e9a6c2861 | ||
|
|
357662d86d | ||
|
|
bfa76e4cd9 | ||
|
|
a9d49a3349 | ||
|
|
f8ffbf36f9 | ||
|
|
e3126b89e6 | ||
|
|
f9f3d089ec | ||
|
|
e3713717f2 | ||
|
|
952896d29e | ||
|
|
4cc32b2457 | ||
|
|
cce0957f54 | ||
|
|
9375c0a6b6 | ||
|
|
a702741e4e | ||
|
|
aeced67249 | ||
|
|
199e03de1d | ||
|
|
9cd2439e5e | ||
|
|
9f4d8bca02 | ||
|
|
de76a4a39b | ||
|
|
a85a5e2794 | ||
|
|
92abd98a55 | ||
|
|
93e867c6b1 | ||
|
|
75c1b1dade | ||
|
|
097ab64c70 | ||
|
|
0e9f513efc | ||
|
|
fd42d4fda1 | ||
|
|
5c38d2feab | ||
|
|
38b678c493 | ||
|
|
fe1848673f | ||
|
|
8b10095e8a | ||
|
|
b5ef7ec8f3 | ||
|
|
bd73cc09ea | ||
|
|
4dfdc822cf | ||
|
|
502c820a25 | ||
|
|
4fb49b4498 | ||
|
|
cf1410af8d | ||
|
|
6c9dcd4242 | ||
|
|
b3e08f2a44 | ||
|
|
a857041afc | ||
|
|
31a8a22aa6 | ||
|
|
a29cda858c | ||
|
|
2e10a2f614 | ||
|
|
2100eb04e1 | ||
|
|
c58391d673 | ||
|
|
c3284c8238 | ||
|
|
290d3e0c6a | ||
|
|
0608dfa4d1 | ||
|
|
1184b3212f | ||
|
|
65e0e4c069 | ||
|
|
d14fa1f6e2 | ||
|
|
2d248704fa | ||
|
|
3096b1218f | ||
|
|
82a8a90230 | ||
|
|
e3d4fbe570 | ||
|
|
031747f29e | ||
|
|
e3bd7f5df1 | ||
|
|
b48205b384 | ||
|
|
8522638b00 | ||
|
|
ab81c77533 | ||
|
|
cc196f2a5b | ||
|
|
dd3022d680 | ||
|
|
de8a3de15a | ||
|
|
4dd0bf4867 | ||
|
|
1957876dd6 | ||
|
|
ab13451086 | ||
|
|
a1490676e3 | ||
|
|
3a382cb3f3 | ||
|
|
9d9d605b15 | ||
|
|
de03f4eb11 | ||
|
|
00f7c68a6f | ||
|
|
41c98b3b41 | ||
|
|
f4c63c8eb0 | ||
|
|
26801e92af | ||
|
|
f3fab143be | ||
|
|
72a827ae93 | ||
|
|
6ca9d3784c | ||
|
|
63f264220b | ||
|
|
41fcf0be4b | ||
|
|
86903a2c66 | ||
|
|
70de3cc561 | ||
|
|
51b6fd4f23 | ||
|
|
9d49a9f344 | ||
|
|
e260a732c8 | ||
|
|
0429198415 | ||
|
|
e7cf57e9ae | ||
|
|
ed369ac0ec | ||
|
|
94bdbb5c63 | ||
|
|
465b5229c6 | ||
|
|
cbf97fd262 | ||
|
|
37187dc2d8 | ||
|
|
2f049cb037 | ||
|
|
eb39bc0dc1 | ||
|
|
007a4e2155 | ||
|
|
13ce187e64 | ||
|
|
26fa181b84 | ||
|
|
0f35ef7cd6 | ||
|
|
b1ae576b2d | ||
|
|
e6559c9ad6 | ||
|
|
5bae05514b | ||
|
|
00ca5776f2 | ||
|
|
577f5f2f30 | ||
|
|
253dc86c10 | ||
|
|
3066bc19eb | ||
|
|
e964708c00 | ||
|
|
7159b8aed3 | ||
|
|
246f5a5499 | ||
|
|
300695de5c | ||
|
|
bd863f16b1 | ||
|
|
4ac822fd7f | ||
|
|
e1d25c5078 | ||
|
|
01159b82fe | ||
|
|
780ee32564 | ||
|
|
217c9a1597 | ||
|
|
70e810bd7b | ||
|
|
baab1c160a | ||
|
|
dd4664e03a | ||
|
|
4ea5c57a84 | ||
|
|
6917395dc1 | ||
|
|
128e28fe3f | ||
|
|
fb3ed5b081 | ||
|
|
4ebe6b90d6 | ||
|
|
0657217a3e | ||
|
|
0037d43d87 | ||
|
|
f5be39e245 | ||
|
|
1574939441 | ||
|
|
f1a22dec9e | ||
|
|
3db8b424a8 | ||
|
|
a16a3dedb4 | ||
|
|
c629dd4f3c | ||
|
|
28ef4f7447 | ||
|
|
cb4b70ac50 | ||
|
|
e29e861e2c | ||
|
|
89b89a88fe | ||
|
|
46071a011a | ||
|
|
b0d8af96bf | ||
|
|
dd27fce3b7 | ||
|
|
efc305b7f9 | ||
|
|
323ce76004 | ||
|
|
dfc2da707b | ||
|
|
fc90c5b09c | ||
|
|
c57f772ad0 | ||
|
|
b8498d7891 | ||
|
|
d791a3906a | ||
|
|
295e0bffaf | ||
|
|
b9340f50c2 | ||
|
|
d3deacc6d4 | ||
|
|
d8f74af3d1 | ||
|
|
24b1a1b6a8 | ||
|
|
d7e8b7cd18 | ||
|
|
f26e450778 | ||
|
|
dce3bb69bb | ||
|
|
de49d27846 | ||
|
|
16a7537bfd | ||
|
|
afafca93f3 | ||
|
|
240f403d93 | ||
|
|
9b30ae005b | ||
|
|
790683bbd7 | ||
|
|
52b446b4ad | ||
|
|
6cee4b8947 | ||
|
|
8e5aa9d394 | ||
|
|
cf4d7e31c4 | ||
|
|
471f2b205f | ||
|
|
09829b1ccc | ||
|
|
df1f24fb7f | ||
|
|
0cba32c15f | ||
|
|
849e17e431 | ||
|
|
d8ab61a9e0 | ||
|
|
7eb130b1a8 | ||
|
|
ef6290c8cd | ||
|
|
89aa588b27 | ||
|
|
50626ef150 | ||
|
|
2f72fc4df2 | ||
|
|
130cea600d | ||
|
|
ea63c19b27 | ||
|
|
a9909c4977 | ||
|
|
a480560371 | ||
|
|
330d56bba9 | ||
|
|
36bcd6b551 | ||
|
|
8fabaf86b6 | ||
|
|
d501b0420a | ||
|
|
0ac5254b8e | ||
|
|
d3566fe8a8 | ||
|
|
93fe8cac1e | ||
|
|
12b7fd341c | ||
|
|
aefc3c3166 | ||
|
|
cd75013c92 | ||
|
|
cba0cffa8f | ||
|
|
f96ac331ed | ||
|
|
616d6f4818 | ||
|
|
6fbb24b32d | ||
|
|
3674d915f5 | ||
|
|
d4254d5e8f | ||
|
|
9a394b41ee | ||
|
|
9240b38d8a | ||
|
|
3880960471 | ||
|
|
494a7575d3 | ||
|
|
ed5955a5c7 | ||
|
|
a7ac696b4a | ||
|
|
9504772f38 | ||
|
|
1e74a92419 | ||
|
|
74f1ca203f | ||
|
|
e8299a4c37 | ||
|
|
4c16b247d4 | ||
|
|
871ed47f89 | ||
|
|
fc1e1e9c3e | ||
|
|
aad1ab5e56 | ||
|
|
8579974f5e | ||
|
|
ae0b0e56ac | ||
|
|
4f9c80160f | ||
|
|
ee66b070a9 | ||
|
|
9e08a6771f | ||
|
|
919aeae42a | ||
|
|
20fc6fe80d | ||
|
|
fb68010f72 | ||
|
|
e90c036ed6 | ||
|
|
e094ed3976 | ||
|
|
7737a0da1a | ||
|
|
67af4894d4 | ||
|
|
08ef7a63d7 | ||
|
|
346a467077 | ||
|
|
e12b722ddc | ||
|
|
24948350f6 | ||
|
|
474b0a55b0 | ||
|
|
69f04d59dc | ||
|
|
7adace694c | ||
|
|
e92f6b70a0 | ||
|
|
c980fa1efc | ||
|
|
53141be387 | ||
|
|
953933d472 | ||
|
|
bab3879cba | ||
|
|
7ac4ff39fe | ||
|
|
ea6f491c93 | ||
|
|
5ecf7515dc | ||
|
|
c8208fa8bb | ||
|
|
40e5ba9fdc | ||
|
|
cf5389afd4 | ||
|
|
ae51c349fd | ||
|
|
6b9856d643 | ||
|
|
e58516daf5 | ||
|
|
265858ee2b | ||
|
|
d7a83b8d8a | ||
|
|
e734424006 | ||
|
|
fe1d6d9c48 | ||
|
|
0dc032435a | ||
|
|
bd8106e40e | ||
|
|
541c6c3cb1 | ||
|
|
02ba387865 | ||
|
|
8c1570af1c | ||
|
|
c52d23f262 | ||
|
|
ae4b73039f | ||
|
|
220c42c8e9 | ||
|
|
99ec6f296e | ||
|
|
054174a19d | ||
|
|
5e8255f28a | ||
|
|
3dc4d5daa8 | ||
|
|
924fc52f54 | ||
|
|
8d44ff51e2 | ||
|
|
f8056b5e44 | ||
|
|
71cea1d87f | ||
|
|
c4f51aa4ac | ||
|
|
2c1ce3bd37 | ||
|
|
6c7e9c1976 | ||
|
|
8ad72872b5 | ||
|
|
2bf5f00e7d | ||
|
|
5ab3ef774b | ||
|
|
3810dc9d1c | ||
|
|
c589b56d69 | ||
|
|
6521190bb4 | ||
|
|
2bb8ac17c6 | ||
|
|
4c4ed4b198 | ||
|
|
3c1f68c59e | ||
|
|
23fb76f08f | ||
|
|
867a43ac1f | ||
|
|
3d3a78f3aa | ||
|
|
3a032d63a9 | ||
|
|
e30d490429 | ||
|
|
8b95b2058b | ||
|
|
d4954a0646 | ||
|
|
a563127a26 | ||
|
|
0716de6bc8 | ||
|
|
68ff08f224 | ||
|
|
c7ba2158f9 | ||
|
|
c1b211aeea | ||
|
|
08d2b92b2a | ||
|
|
7cc4eedc1a | ||
|
|
25e457c8d5 | ||
|
|
56e565a582 | ||
|
|
380f1c4a49 | ||
|
|
b5c1c28048 | ||
|
|
fc53162eec | ||
|
|
2b03a61832 | ||
|
|
a9f3baefe6 | ||
|
|
d1f9409afc | ||
|
|
f6e360a134 | ||
|
|
bc31c29d8a | ||
|
|
65ae6ad902 | ||
|
|
dd98f761ad | ||
|
|
162c04a1c4 | ||
|
|
e084139ca9 | ||
|
|
8fc549574f | ||
|
|
0eb4970621 | ||
|
|
5a72560319 | ||
|
|
5e7aae3f16 | ||
|
|
62b153f5c1 | ||
|
|
a2108ce2e5 | ||
|
|
31cc7ef7f0 | ||
|
|
2728331362 | ||
|
|
ebb300424c | ||
|
|
0c284b5b09 | ||
|
|
8e9ed5f9be | ||
|
|
d6c80e4dd1 | ||
|
|
2756341b21 | ||
|
|
c3e3d2d85d | ||
|
|
4964d664f0 | ||
|
|
845a78daf8 | ||
|
|
191818b865 | ||
|
|
5054e69844 | ||
|
|
029b74e4f5 | ||
|
|
2e6eeb1d79 | ||
|
|
490f28f0a6 | ||
|
|
cc39cf7df3 | ||
|
|
76f0712319 | ||
|
|
89a63114c4 | ||
|
|
9754e7b4e2 | ||
|
|
93ac6ceb35 | ||
|
|
4e6e8425ca | ||
|
|
062b1320a2 | ||
|
|
95f95e96c4 | ||
|
|
8f824d34a8 | ||
|
|
4027ff5699 | ||
|
|
17544140fb | ||
|
|
8e824017dc | ||
|
|
da9c0815ac | ||
|
|
39ac4d4387 | ||
|
|
51ded5e690 | ||
|
|
97e572728f | ||
|
|
a2db6f6500 | ||
|
|
61d5456852 | ||
|
|
0277ce9bca | ||
|
|
970f2b8430 | ||
|
|
b5bb27f231 | ||
|
|
578e59a54e | ||
|
|
9ab8c36161 | ||
|
|
bd69e9c7ab | ||
|
|
aeb3c0e274 | ||
|
|
9f5dedabba | ||
|
|
f7a608703d | ||
|
|
92d37152c2 | ||
|
|
ad42b553bf | ||
|
|
1c292dd9a4 | ||
|
|
2ad404a3bf | ||
|
|
ca67782626 | ||
|
|
0a4ea004cf | ||
|
|
15133c2f25 | ||
|
|
a7f15f4289 | ||
|
|
ac4b1275a3 | ||
|
|
bf2e7f8fc5 | ||
|
|
cdbf41bb9b | ||
|
|
966988e360 | ||
|
|
a9877aba3e | ||
|
|
da6ab39049 | ||
|
|
5271c3b9e1 | ||
|
|
eea1892863 | ||
|
|
c97d4d9b6c | ||
|
|
38de71b3e0 | ||
|
|
a77d5d1258 | ||
|
|
68944b4484 | ||
|
|
c60f59609e | ||
|
|
8473b030c5 | ||
|
|
9dd3baf45c | ||
|
|
410996df9e | ||
|
|
313fda426c | ||
|
|
277be51bc0 | ||
|
|
2d9f213ca8 | ||
|
|
97814c62a8 | ||
|
|
ee4943ebd7 | ||
|
|
086c1533a4 | ||
|
|
55c01c0757 | ||
|
|
279e098168 | ||
|
|
c4f1b216d1 | ||
|
|
b93a68bb48 | ||
|
|
3f80eb80d0 | ||
|
|
697f53ecde | ||
|
|
cf74e1d9e9 | ||
|
|
44405bd03c | ||
|
|
26e275222b | ||
|
|
ae48f50245 | ||
|
|
b83e88ea78 | ||
|
|
a684b91fe7 | ||
|
|
27d965234a | ||
|
|
c769c194d6 | ||
|
|
65186c3301 | ||
|
|
f6a428b60d | ||
|
|
3d88876a32 | ||
|
|
09bedef409 | ||
|
|
a5c946a321 | ||
|
|
29042d8e5a | ||
|
|
c57db45672 | ||
|
|
9491b514c9 | ||
|
|
320c317719 | ||
|
|
abae17e6fc | ||
|
|
3baf5c38fc | ||
|
|
ce46f30739 | ||
|
|
65a95539f1 | ||
|
|
874dc3f5ea | ||
|
|
7941719371 | ||
|
|
fa9729226b | ||
|
|
9df177af85 | ||
|
|
f45123356b | ||
|
|
8851f4d6b1 | ||
|
|
f954423101 | ||
|
|
95e63ac32d | ||
|
|
fc6e59d3ec | ||
|
|
da45859f32 | ||
|
|
8bf600cc62 | ||
|
|
aa607a379a | ||
|
|
6ebd3f4d35 | ||
|
|
9072c77aea | ||
|
|
c32b8100c3 | ||
|
|
f93257941e | ||
|
|
896863802e | ||
|
|
920b40253c | ||
|
|
07440e3332 | ||
|
|
1827dfc071 | ||
|
|
c46aecd4e3 | ||
|
|
21bf8a6973 | ||
|
|
f5475ba782 | ||
|
|
265f15498c | ||
|
|
666409ec7e | ||
|
|
b719d559b6 | ||
|
|
9d3ec9a2a2 | ||
|
|
74ec225ad1 | ||
|
|
b5a1a092f1 | ||
|
|
ddc6d68114 | ||
|
|
32eb84d6d3 | ||
|
|
da6c635ea2 | ||
|
|
e34c356e64 | ||
|
|
7017393647 | ||
|
|
aa49f5b933 | ||
|
|
3f91ac47e6 | ||
|
|
8c631b92ce | ||
|
|
0ebac144f5 | ||
|
|
5e0e1c6b31 | ||
|
|
3c7545c6ce | ||
|
|
aba839195b | ||
|
|
1f6ea41272 | ||
|
|
5450d22796 | ||
|
|
1174fde1f3 | ||
|
|
fb089ae297 | ||
|
|
ab8224c939 | ||
|
|
c954e71f87 | ||
|
|
cbc04a839a | ||
|
|
cb4a5abc8c | ||
|
|
07ff6e3ccc | ||
|
|
cdaab944b4 | ||
|
|
6ecf32fd4a | ||
|
|
20de10df4e | ||
|
|
673c29efeb | ||
|
|
c4abf40e6e | ||
|
|
39b9922609 | ||
|
|
7018e6583b | ||
|
|
b957e587da | ||
|
|
ebb9ea4571 | ||
|
|
54a97e1043 | ||
|
|
0ee78c19dd | ||
|
|
bcc7a5eb2e | ||
|
|
2313b972a3 | ||
|
|
989b49335c | ||
|
|
3f30831640 | ||
|
|
5fcee9bc30 | ||
|
|
2f0e52d6eb | ||
|
|
c0d69cec4e | ||
|
|
c852b9a90a | ||
|
|
b876d0bed0 | ||
|
|
e245cade25 | ||
|
|
7c223b32a7 | ||
|
|
33a176813d | ||
|
|
20ec01c5f7 | ||
|
|
6376601ba1 | ||
|
|
ca441f419c | ||
|
|
04e66a2bb4 | ||
|
|
b299386d3e | ||
|
|
21a4622f23 | ||
|
|
041f457dfa | ||
|
|
af111e413c | ||
|
|
431bf5d235 | ||
|
|
576c8ca99c | ||
|
|
45a4b8ad1c | ||
|
|
d14d695966 | ||
|
|
a2a47337cb | ||
|
|
ba98030438 | ||
|
|
1e200a1107 | ||
|
|
7a1b959646 | ||
|
|
b9b46431be | ||
|
|
3f18c0f40f | ||
|
|
1cfbd32a1d | ||
|
|
a15b594003 | ||
|
|
24d27a7a21 | ||
|
|
7af922c9df | ||
|
|
b1918dd121 | ||
|
|
1be19f8b58 | ||
|
|
3763b4bff4 | ||
|
|
52ffc4c7f4 | ||
|
|
0245327fc5 | ||
|
|
b155e66fe5 | ||
|
|
5712827703 | ||
|
|
7ba53d302d |
@@ -1,163 +0,0 @@
|
|||||||
;;; .dir-locals.el
|
|
||||||
;;
|
|
||||||
;; Per-Directory Local Variables:
|
|
||||||
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html
|
|
||||||
;;
|
|
||||||
;; For full fledge developer tools install emacs packages:
|
|
||||||
;;
|
|
||||||
;; M-x package-install ...
|
|
||||||
;;
|
|
||||||
;; magit gitconfig
|
|
||||||
;; nvm lsp-mode lsp-pyright lsp-eslint
|
|
||||||
;; pyvenv pylint pip-requirements
|
|
||||||
;; jinja2-mode
|
|
||||||
;; json-mode
|
|
||||||
;; company company-jedi company-quickhelp company-shell
|
|
||||||
;; realgud
|
|
||||||
;; sphinx-doc markdown-mode graphviz-dot-mode
|
|
||||||
;; apache-mode nginx-mode
|
|
||||||
;;
|
|
||||||
;; To setup a developer environment, build target::
|
|
||||||
;;
|
|
||||||
;; $ make node.env.dev pyenv.install
|
|
||||||
;;
|
|
||||||
;; Some buffer locals are referencing the project environment:
|
|
||||||
;;
|
|
||||||
;; - prj-root --> <repo>/
|
|
||||||
;; - nvm-dir --> <repo>/.nvm
|
|
||||||
;; - python-environment-directory --> <repo>/local
|
|
||||||
;; - python-environment-default-root-name --> py3
|
|
||||||
;; - python-shell-virtualenv-root --> <repo>/local/py3
|
|
||||||
;; When this variable is set with the path of the virtualenv to use,
|
|
||||||
;; `process-environment' and `exec-path' get proper values in order to run
|
|
||||||
;; shells inside the specified virtualenv, example::
|
|
||||||
;; (setq python-shell-virtualenv-root "/path/to/env/")
|
|
||||||
;; - python-shell-interpreter --> <repo>/local/py3/bin/python
|
|
||||||
;;
|
|
||||||
;; Python development:
|
|
||||||
;;
|
|
||||||
;; Jedi, flycheck & other python stuff should use the 'python-shell-interpreter'
|
|
||||||
;; from the local py3 environment.
|
|
||||||
;;
|
|
||||||
|
|
||||||
((nil
|
|
||||||
. ((fill-column . 80)
|
|
||||||
(indent-tabs-mode . nil)
|
|
||||||
(eval . (progn
|
|
||||||
|
|
||||||
(add-to-list 'auto-mode-alist '("\\.html\\'" . jinja2-mode))
|
|
||||||
|
|
||||||
;; project root folder is where the `.dir-locals.el' is located
|
|
||||||
(setq-local prj-root
|
|
||||||
(locate-dominating-file default-directory ".dir-locals.el"))
|
|
||||||
|
|
||||||
(setq-local python-environment-directory
|
|
||||||
(expand-file-name "./local" prj-root))
|
|
||||||
|
|
||||||
;; to get in use of NVM environment, install https://github.com/rejeep/nvm.el
|
|
||||||
(setq-local nvm-dir (expand-file-name "./.nvm" prj-root))
|
|
||||||
|
|
||||||
;; use nodejs from the (local) NVM environment (see nvm-dir)
|
|
||||||
(nvm-use-for-buffer)
|
|
||||||
(ignore-errors (require 'lsp))
|
|
||||||
(setq-local lsp-server-install-dir (car (cdr nvm-current-version)))
|
|
||||||
(setq-local lsp-enable-file-watchers nil)
|
|
||||||
|
|
||||||
;; use 'py3' environment as default
|
|
||||||
(setq-local python-environment-default-root-name
|
|
||||||
"py3")
|
|
||||||
|
|
||||||
(setq-local python-shell-virtualenv-root
|
|
||||||
(expand-file-name
|
|
||||||
python-environment-default-root-name python-environment-directory))
|
|
||||||
|
|
||||||
(setq-local python-shell-interpreter
|
|
||||||
(expand-file-name
|
|
||||||
"bin/python" python-shell-virtualenv-root))))))
|
|
||||||
(makefile-gmake-mode
|
|
||||||
. ((indent-tabs-mode . t)))
|
|
||||||
|
|
||||||
(yaml-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
|
|
||||||
;; flycheck should use the local py3 environment
|
|
||||||
(setq-local flycheck-yaml-yamllint-executable
|
|
||||||
(expand-file-name "bin/yamllint" python-shell-virtualenv-root))
|
|
||||||
|
|
||||||
(setq-local flycheck-yamllintrc
|
|
||||||
(expand-file-name ".yamllint.yml" prj-root))
|
|
||||||
|
|
||||||
(flycheck-checker . yaml-yamllint)))))
|
|
||||||
|
|
||||||
(json-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
(setq-local js-indent-level 4)
|
|
||||||
(flycheck-checker . json-python-json)))))
|
|
||||||
|
|
||||||
(js-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
(ignore-errors (require 'lsp-eslint))
|
|
||||||
(setq-local js-indent-level 2)
|
|
||||||
;; flycheck should use the eslint checker from developer tools
|
|
||||||
(setq-local flycheck-javascript-eslint-executable
|
|
||||||
(expand-file-name "node_modules/.bin/eslint" prj-root))
|
|
||||||
;; (flycheck-mode)
|
|
||||||
|
|
||||||
(if (featurep 'lsp-eslint)
|
|
||||||
(lsp))
|
|
||||||
))))
|
|
||||||
|
|
||||||
(python-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
(ignore-errors (require 'jedi-core))
|
|
||||||
(ignore-errors (require 'lsp-pyright))
|
|
||||||
(ignore-errors (sphinx-doc-mode))
|
|
||||||
(setq-local python-environment-virtualenv
|
|
||||||
(list (expand-file-name "bin/virtualenv" python-shell-virtualenv-root)
|
|
||||||
;;"--system-site-packages"
|
|
||||||
"--quiet"))
|
|
||||||
|
|
||||||
(setq-local pylint-command
|
|
||||||
(expand-file-name "bin/pylint" python-shell-virtualenv-root))
|
|
||||||
|
|
||||||
(if (featurep 'lsp-pyright)
|
|
||||||
(lsp))
|
|
||||||
|
|
||||||
;; pylint will find the '.pylintrc' file next to the CWD
|
|
||||||
;; https://pylint.readthedocs.io/en/latest/user_guide/run.html#command-line-options
|
|
||||||
(setq-local flycheck-pylintrc
|
|
||||||
".pylintrc")
|
|
||||||
|
|
||||||
;; flycheck & other python stuff should use the local py3 environment
|
|
||||||
(setq-local flycheck-python-pylint-executable
|
|
||||||
python-shell-interpreter)
|
|
||||||
|
|
||||||
;; use 'M-x jedi:show-setup-info' and 'M-x epc:controller' to inspect jedi server
|
|
||||||
;; https://tkf.github.io/emacs-jedi/latest/#jedi:environment-root -- You
|
|
||||||
;; can specify a full path instead of a name (relative path). In that case,
|
|
||||||
;; python-environment-directory is ignored and Python virtual environment
|
|
||||||
;; is created at the specified path.
|
|
||||||
(setq-local jedi:environment-root
|
|
||||||
python-shell-virtualenv-root)
|
|
||||||
|
|
||||||
;; https://tkf.github.io/emacs-jedi/latest/#jedi:server-command
|
|
||||||
(setq-local jedi:server-command
|
|
||||||
(list python-shell-interpreter
|
|
||||||
jedi:server-script))
|
|
||||||
|
|
||||||
;; jedi:environment-virtualenv --> see above 'python-environment-virtualenv'
|
|
||||||
;; is set buffer local! No need to setup jedi:environment-virtualenv:
|
|
||||||
;;
|
|
||||||
;; Virtualenv command to use. A list of string. If it is nil,
|
|
||||||
;; python-environment-virtualenv is used instead. You must set non-nil
|
|
||||||
;; value to jedi:environment-root in order to make this setting work.
|
|
||||||
;;
|
|
||||||
;; https://tkf.github.io/emacs-jedi/latest/#jedi:environment-virtualenv
|
|
||||||
;;
|
|
||||||
;; (setq-local jedi:environment-virtualenv
|
|
||||||
;; (list (expand-file-name "bin/virtualenv" python-shell-virtualenv-root)
|
|
||||||
;; "--python"
|
|
||||||
;; "/usr/bin/python3.4"
|
|
||||||
;; ))
|
|
||||||
))))
|
|
||||||
)
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
*
|
*
|
||||||
|
|
||||||
|
!container/*.template.*
|
||||||
!container/entrypoint.sh
|
!container/entrypoint.sh
|
||||||
!searx/**
|
!searx/**
|
||||||
!requirements*.txt
|
!requirements*.txt
|
||||||
|
|||||||
65
.github/ISSUE_TEMPLATE/bug-report.md
vendored
65
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,39 +1,50 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: "Bug report"
|
||||||
about: Report a bug in SearXNG
|
about: Report a bug in SearXNG"
|
||||||
title: ''
|
labels: ["bug"]
|
||||||
labels: bug
|
type: "bug"
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->
|
|
||||||
|
|
||||||
**Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG**
|
_Replace this placeholder with a meaningful and precise description of the bug._
|
||||||
<!-- If you are running on master branch using git execute this command
|
|
||||||
in order to fetch the latest commit ID:
|
|
||||||
```
|
|
||||||
git log -1
|
|
||||||
```
|
|
||||||
If you are using searxng-docker then look at the bottom of the SearXNG page
|
|
||||||
and check for the version after "Powered by SearXNG"
|
|
||||||
|
|
||||||
Please also stipulate if you are using a forked version of SearXNG and
|
<!-- FILL IN THESE FIELDS .. and delete the comments after reading.
|
||||||
include a link to the fork source code.
|
|
||||||
|
Use Markdown for formatting -> https://www.markdowntools.io/cheat-sheet
|
||||||
-->
|
-->
|
||||||
**How did you install SearXNG?**
|
|
||||||
<!-- Did you install SearXNG using the official wiki or using searxng-docker
|
|
||||||
or manually by executing the searx/webapp.py file? -->
|
|
||||||
**What happened?**
|
|
||||||
<!-- A clear and concise description of what the bug is. -->
|
|
||||||
|
|
||||||
**How To Reproduce**
|
### How To Reproduce?
|
||||||
<!-- How can we reproduce this issue? (as minimally and as precisely as possible) -->
|
|
||||||
|
<!-- How can we reproduce this issue? (as minimally and as precisely as
|
||||||
|
possible) -->
|
||||||
|
|
||||||
|
### Expected behavior
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
<!-- A clear and concise description of what you expected to happen. -->
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
**Screenshots & Logs**
|
### Screenshots & Logs
|
||||||
|
|
||||||
<!-- If applicable, add screenshots, logs to help explain your problem. -->
|
<!-- If applicable, add screenshots, logs to help explain your problem. -->
|
||||||
|
|
||||||
**Additional context**
|
### Version of SearXNG
|
||||||
|
|
||||||
|
<!-- Commit number if you are using on master branch and stipulate if you forked
|
||||||
|
SearXNG -->
|
||||||
|
|
||||||
|
<!-- Look at the bottom of the SearXNG page and check for the version after
|
||||||
|
"Powered by SearXNG" If you are using a forked version of SearXNG include a
|
||||||
|
link to the fork source code. -->
|
||||||
|
|
||||||
|
### How did you install SearXNG?
|
||||||
|
|
||||||
|
<!-- Did you install SearXNG using the official documentation or using
|
||||||
|
searxng-docker? -->
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
<!-- Add any other context about the problem here. -->
|
<!-- Add any other context about the problem here. -->
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
[AI Policy]: https://github.com/searxng/searxng/blob/master/AI_POLICY.rst
|
||||||
|
|
||||||
|
- [ ] I read the [AI Policy] and hereby confirm that this issue conforms with the policy.
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Questions & Answers (Q&A)
|
- name: Questions & Answers (Q&A)
|
||||||
url: https://github.com/searxng/searxng/discussions/categories/q-a
|
url: https://github.com/searxng/searxng/discussions/categories/q-a
|
||||||
|
|||||||
61
.github/ISSUE_TEMPLATE/engine-request.md
vendored
61
.github/ISSUE_TEMPLATE/engine-request.md
vendored
@@ -1,31 +1,46 @@
|
|||||||
---
|
---
|
||||||
name: Engine request
|
name: Engine request"
|
||||||
about: Request a new engine in SearXNG
|
about: Request a new engine in SearXNG"
|
||||||
title: ''
|
labels: ["engine request"]
|
||||||
labels: enhancement, engine request
|
type: "feature"
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->
|
|
||||||
|
|
||||||
**Working URL to the engine**
|
<!-- FILL IN THESE FIELDS .. and delete the comments after reading.
|
||||||
<!-- Please check if the engine is responding correctly before submitting it. -->
|
|
||||||
|
|
||||||
**Why do you want to add this engine?**
|
Use Markdown for formatting -> https://www.markdowntools.io/cheat-sheet
|
||||||
<!-- What's special about this engine? Is it open source or libre? -->
|
-->
|
||||||
|
|
||||||
**Features of this engine**
|
### Working URL to the engine
|
||||||
<!-- Features of this engine: Doesn't track its users, fast, easy to integrate, ... -->
|
|
||||||
|
|
||||||
**How can SearXNG fetch the information from this engine?**
|
<!-- Please check if the engine is responding correctly before submitting -->
|
||||||
<!-- List API URL, example code (using the correct markdown) and more
|
|
||||||
that could be useful for the developers in order to implement this engine.
|
|
||||||
If you don't know what to write, let this part blank. -->
|
|
||||||
|
|
||||||
**Applicable category of this engine**
|
### Why do you want to add this engine?
|
||||||
<!-- Where should this new engine fit in SearXNG? Current categories in SearXNG:
|
|
||||||
general, files, images, it, map, music, news, science, social media and videos.
|
|
||||||
You can add multiple categories at the same time. -->
|
|
||||||
|
|
||||||
**Additional context**
|
<!-- What's special about this engine? -->
|
||||||
<!-- Add any other context about this engine here. -->
|
|
||||||
|
### Features of this engine
|
||||||
|
|
||||||
|
<!-- Features of this engine: Serves special content, is fast, is easy to
|
||||||
|
integrate, ... ? -->
|
||||||
|
|
||||||
|
### How can SearXNG fetch results from this engine?
|
||||||
|
|
||||||
|
<!-- List API URL, example code and more that could be useful for the developers
|
||||||
|
in order to implement this engine. If you don't know what to write, let
|
||||||
|
this part blank. -->
|
||||||
|
|
||||||
|
### Applicable category of this engine
|
||||||
|
|
||||||
|
<!-- Where should this new engine fit in SearXNG? Current categories in
|
||||||
|
SearXNG: general, files, images, it, map, music, news, science, social
|
||||||
|
media and videos. -->
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
|
||||||
|
<!-- Add any other context about the problem here. -->
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
[AI Policy]: https://github.com/searxng/searxng/blob/master/AI_POLICY.rst
|
||||||
|
|
||||||
|
- [ ] I read the [AI Policy] and hereby confirm that this issue conforms with the policy.
|
||||||
|
|||||||
37
.github/ISSUE_TEMPLATE/feature-request.md
vendored
37
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -1,21 +1,32 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: "Feature request"
|
||||||
about: Request a new feature in SearXNG
|
about: "Request a new feature in SearXNG"
|
||||||
title: ''
|
labels: ["new feature"]
|
||||||
labels: enhancement
|
type: "feature"
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- PLEASE FILL THESE FIELDS, IT REALLY HELPS THE MAINTAINERS OF SearXNG -->
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
_Replace this placeholder with a concise description of the feature._
|
||||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
|
||||||
|
<!-- FILL IN THESE FIELDS .. and delete the comments after reading.
|
||||||
|
|
||||||
|
Use Markdown for formatting -> https://www.markdowntools.io/cheat-sheet
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Is your feature request related to a problem?
|
||||||
|
|
||||||
|
<!-- A clear and concise description of what the problem is. Ex. I'm always
|
||||||
|
frustrated when [...] -->
|
||||||
|
|
||||||
|
### Describe the solution you'd like
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
<!-- A clear and concise description of what you want to happen. -->
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
### Describe alternatives you've considered
|
||||||
|
|
||||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
**Additional context**
|
### Code of Conduct
|
||||||
<!-- Add any other context or screenshots about the feature request here. -->
|
|
||||||
|
[AI Policy]: https://github.com/searxng/searxng/blob/master/AI_POLICY.rst
|
||||||
|
|
||||||
|
- [ ] I read the [AI Policy] and hereby confirm that this issue conforms with the policy.
|
||||||
|
|||||||
47
.github/workflows/checker.yml
vendored
47
.github/workflows/checker.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
name: Checker
|
|
||||||
|
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 4 * * 5"
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
PYTHON_VERSION: "3.14"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
search:
|
|
||||||
if: github.repository_owner == 'searxng' || github.event_name == 'workflow_dispatch'
|
|
||||||
name: Search
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
steps:
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
|
||||||
with:
|
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: "false"
|
|
||||||
|
|
||||||
- name: Setup cache Python
|
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
|
||||||
with:
|
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
|
||||||
path: "./local"
|
|
||||||
|
|
||||||
- name: Setup venv
|
|
||||||
run: make V=1 install
|
|
||||||
|
|
||||||
- name: Search checker
|
|
||||||
run: make search.checker
|
|
||||||
42
.github/workflows/container.yml
vendored
42
.github/workflows/container.yml
vendored
@@ -56,13 +56,8 @@ jobs:
|
|||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
- name: Setup podman
|
- name: Setup podman
|
||||||
env:
|
env:
|
||||||
PODMAN_VERSION: "v5.6.2"
|
PODMAN_VERSION: "v5.7.1"
|
||||||
run: |
|
run: |
|
||||||
# dpkg man-db trigger is very slow on GHA runners
|
|
||||||
# https://github.com/actions/runner-images/issues/10977
|
|
||||||
# https://github.com/actions/runner/issues/4030
|
|
||||||
sudo rm -f /var/lib/man-db/auto-update
|
|
||||||
|
|
||||||
sudo apt-get purge -y podman runc crun conmon
|
sudo apt-get purge -y podman runc crun conmon
|
||||||
|
|
||||||
curl -fsSLO "https://github.com/mgoltzsche/podman-static/releases/download/${{ env.PODMAN_VERSION }}/podman-linux-${{ matrix.march }}.tar.gz"
|
curl -fsSLO "https://github.com/mgoltzsche/podman-static/releases/download/${{ env.PODMAN_VERSION }}/podman-linux-${{ matrix.march }}.tar.gz"
|
||||||
@@ -78,21 +73,22 @@ jobs:
|
|||||||
# yamllint enable rule:line-length
|
# yamllint enable rule:line-length
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
- name: Get date
|
- name: Get date
|
||||||
@@ -100,20 +96,20 @@ jobs:
|
|||||||
run: echo "date=$(date +'%Y%m%d')" >>$GITHUB_OUTPUT
|
run: echo "date=$(date +'%Y%m%d')" >>$GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Setup cache container
|
- name: Setup cache container
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
"container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-"
|
container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-
|
||||||
"container-${{ matrix.arch }}-"
|
container-${{ matrix.arch }}-
|
||||||
path: "/var/tmp/buildah-cache-*/*"
|
path: "/var/tmp/buildah-cache-*/*"
|
||||||
|
|
||||||
- if: ${{ matrix.emulation }}
|
- if: ${{ matrix.emulation }}
|
||||||
name: Setup QEMU
|
name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "ghcr.io"
|
registry: "ghcr.io"
|
||||||
username: "${{ github.repository_owner }}"
|
username: "${{ github.repository_owner }}"
|
||||||
@@ -145,16 +141,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- if: ${{ matrix.emulation }}
|
- if: ${{ matrix.emulation }}
|
||||||
name: Setup QEMU
|
name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "ghcr.io"
|
registry: "ghcr.io"
|
||||||
username: "${{ github.repository_owner }}"
|
username: "${{ github.repository_owner }}"
|
||||||
@@ -179,23 +175,23 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "ghcr.io"
|
registry: "ghcr.io"
|
||||||
username: "${{ github.repository_owner }}"
|
username: "${{ github.repository_owner }}"
|
||||||
password: "${{ secrets.GITHUB_TOKEN }}"
|
password: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "docker.io"
|
registry: "docker.io"
|
||||||
username: "${{ secrets.DOCKERHUB_USERNAME }}"
|
username: "${{ secrets.DOCKER_USER }}"
|
||||||
password: "${{ secrets.DOCKERHUB_TOKEN }}"
|
password: "${{ secrets.DOCKER_TOKEN }}"
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
env:
|
env:
|
||||||
|
|||||||
12
.github/workflows/data-update.yml
vendored
12
.github/workflows/data-update.yml
vendored
@@ -33,6 +33,7 @@ jobs:
|
|||||||
- update_engine_traits.py
|
- update_engine_traits.py
|
||||||
- update_wikidata_units.py
|
- update_wikidata_units.py
|
||||||
- update_engine_descriptions.py
|
- update_engine_descriptions.py
|
||||||
|
- update_gsa_useragents.py
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -40,20 +41,21 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
@@ -64,7 +66,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create PR
|
- name: Create PR
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||||
with:
|
with:
|
||||||
author: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
author: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
||||||
committer: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
committer: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
||||||
|
|||||||
16
.github/workflows/documentation.yml
vendored
16
.github/workflows/documentation.yml
vendored
@@ -32,32 +32,36 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
|
- name: Setup dependencies
|
||||||
|
run: sudo ./utils/searxng.sh install buildhost
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
run: make V=1 install
|
run: make V=1 install
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: make V=1 docs.clean docs.html
|
run: make V=1 docs.html
|
||||||
|
|
||||||
- if: github.ref_name == 'master'
|
- if: github.ref_name == 'master'
|
||||||
name: Release
|
name: Release
|
||||||
uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # v4.7.3
|
uses: JamesIves/github-pages-deploy-action@d92aa235d04922e8f08b40ce78cc5442fcfbfa2f # v4.8.0
|
||||||
with:
|
with:
|
||||||
folder: "dist/docs"
|
folder: "dist/docs"
|
||||||
branch: "gh-pages"
|
branch: "gh-pages"
|
||||||
|
|||||||
23
.github/workflows/integration.yml
vendored
23
.github/workflows/integration.yml
vendored
@@ -27,7 +27,6 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version:
|
python-version:
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
- "3.11"
|
||||||
- "3.12"
|
- "3.12"
|
||||||
- "3.13"
|
- "3.13"
|
||||||
@@ -35,20 +34,21 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python-version }}"
|
python-version: "${{ matrix.python-version }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ matrix.python-version }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ matrix.python-version }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ matrix.python-version }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ matrix.python-version }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
@@ -62,31 +62,32 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04-arm
|
runs-on: ubuntu-24.04-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: "./.nvmrc"
|
node-version-file: "./.nvmrc"
|
||||||
|
|
||||||
- name: Setup cache Node.js
|
- name: Setup cache Node.js
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "nodejs-${{ runner.arch }}-${{ hashFiles('./.nvmrc', './package.json') }}"
|
key: "nodejs-${{ runner.arch }}-${{ hashFiles('./.nvmrc', './package.json') }}"
|
||||||
path: "./client/simple/node_modules/"
|
path: "./client/simple/node_modules/"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
|
|||||||
22
.github/workflows/l10n.yml
vendored
22
.github/workflows/l10n.yml
vendored
@@ -35,21 +35,22 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
@@ -82,21 +83,22 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||||
with:
|
with:
|
||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
|
|
||||||
- name: Setup cache Python
|
- name: Setup cache Python
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
||||||
with:
|
with:
|
||||||
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
key: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-${{ hashFiles('./requirements*.txt') }}"
|
||||||
restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-"
|
restore-keys: |
|
||||||
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
@@ -117,7 +119,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create PR
|
- name: Create PR
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||||
with:
|
with:
|
||||||
author: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
author: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
||||||
committer: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
committer: "searxng-bot <searxng-bot@users.noreply.github.com>"
|
||||||
@@ -130,7 +132,7 @@ jobs:
|
|||||||
body: |
|
body: |
|
||||||
[l10n] update translations from Weblate
|
[l10n] update translations from Weblate
|
||||||
labels: |
|
labels: |
|
||||||
translation
|
area:i18n
|
||||||
|
|
||||||
- name: Display information
|
- name: Display information
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
10
.github/workflows/security.yml
vendored
10
.github/workflows/security.yml
vendored
@@ -24,16 +24,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Sync GHCS from Docker Scout
|
- name: Sync GHCS from Docker Scout
|
||||||
uses: docker/scout-action@f8c776824083494ab0d56b8105ba2ca85c86e4de # v1.18.2
|
uses: docker/scout-action@7520205ff60037fdc436b40b6a1d1e55a839ec2d # v1.22.0
|
||||||
with:
|
with:
|
||||||
organization: "searxng"
|
organization: "searxng"
|
||||||
dockerhub-user: "${{ secrets.DOCKERHUB_USERNAME }}"
|
dockerhub-user: "${{ secrets.DOCKER_USER }}"
|
||||||
dockerhub-password: "${{ secrets.DOCKERHUB_TOKEN }}"
|
dockerhub-password: "${{ secrets.DOCKER_TOKEN }}"
|
||||||
image: "registry://ghcr.io/searxng/searxng:latest"
|
image: "registry://ghcr.io/searxng/searxng:latest"
|
||||||
command: "cves"
|
command: "cves"
|
||||||
sarif-file: "./scout.sarif"
|
sarif-file: "./scout.sarif"
|
||||||
@@ -41,6 +41,6 @@ jobs:
|
|||||||
write-comment: "false"
|
write-comment: "false"
|
||||||
|
|
||||||
- name: Upload SARIFs
|
- name: Upload SARIFs
|
||||||
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
|
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
|
||||||
with:
|
with:
|
||||||
sarif_file: "./scout.sarif"
|
sarif_file: "./scout.sarif"
|
||||||
|
|||||||
11
.helix/languages.toml
Normal file
11
.helix/languages.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[[language]]
|
||||||
|
name = "python"
|
||||||
|
language-servers = ["basedpyright", "pylsp"]
|
||||||
|
auto-format = true
|
||||||
|
|
||||||
|
[language-server.pylsp.config.pylsp]
|
||||||
|
plugins.pylint.enabled = true
|
||||||
|
plugins.isort.enabled = true
|
||||||
|
plugins.black.enabled = true
|
||||||
|
plugins.black.skip_string_normalization = true
|
||||||
|
plugins.black.line_length = 120
|
||||||
20
AI_POLICY.rst
Normal file
20
AI_POLICY.rst
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.. SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
AI Policy
|
||||||
|
=========
|
||||||
|
|
||||||
|
Restrictions on Generative AI Usage
|
||||||
|
-----------------------------------
|
||||||
|
- **All AI usage in any form must be disclosed.** You must state the tool you used (e.g. Claude Code, Cursor, Amp) along with the extent that the work was AI-assisted.
|
||||||
|
- **The human-in-the-loop must fully understand all code.** If you use generative AI tools as an aid in developing code or documentation changes, ensure that you fully understand the proposed changes and can explain why they are the correct approach.
|
||||||
|
- **AI should never be the main author of the PR.** AI may be used as a tool to help with developing, but the human contribution to the code changes should always be reasonably larger than the part written by AI. For example, you should be the one that decides about the structure of the PR, not the LLM.
|
||||||
|
- **Issues and PR descriptions must be fully human-written.** Do not post output from Large Language Models or similar generative AI as comments on any of our discussion forums (e.g. GitHub Issues, Matrix, ...), as such comments tend to be formulaic and low content. If you're a not a native English speaker, using AI for translating self-written issue texts to English is okay, but please keep the wording as close as possible to the original wording.
|
||||||
|
- **Bad AI drivers will be denounced.** People who produce bad contributions that are clearly AI (slop) will be blocked for all future contributions.
|
||||||
|
|
||||||
|
There are Humans Here
|
||||||
|
---------------------
|
||||||
|
Every discussion, issue, and pull request is read and reviewed by humans. It is a boundary point at which people interact with each other and the work done. It is rude and disrespectful to approach this boundary with low-effort, unqualified work, since it puts the burden of validation on the maintainer.
|
||||||
|
|
||||||
|
It takes a lot of maintainer time and energy to review AI-generated contributions! Sending the output of an LLM to open source project maintainers extracts work from them in the form of design and code review, so we call this kind of contribution an "extractive contribution".
|
||||||
|
|
||||||
|
The *golden rule* is that a contribution should be worth more to the project than the time it takes to review it, which is usually not the case if large parts of your PR were written by LLMs.
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
.. _Quickstart guide: https://docs.searxng.org/dev/quickstart.html
|
.. _Quickstart guide: https://docs.searxng.org/dev/quickstart.html
|
||||||
.. _Commits guide: https://docs.searxng.org/dev/commits.html
|
.. _Commits guide: https://docs.searxng.org/dev/commits.html
|
||||||
|
.. _AI Policy: https://github.com/searxng/searxng/blob/master/AI_POLICY.rst
|
||||||
.. _Weblate: https://translate.codeberg.org/projects/searxng/searxng/
|
.. _Weblate: https://translate.codeberg.org/projects/searxng/searxng/
|
||||||
.. _GitHub Codespaces: https://docs.github.com/en/codespaces/overview
|
.. _GitHub Codespaces: https://docs.github.com/en/codespaces/overview
|
||||||
.. _120 hours per month: https://github.com/settings/billing
|
.. _120 hours per month: https://github.com/settings/billing
|
||||||
@@ -90,3 +91,8 @@ rules in this project are:
|
|||||||
- Prefer fewer arguments.
|
- Prefer fewer arguments.
|
||||||
- Do not add obvious comments to code.
|
- Do not add obvious comments to code.
|
||||||
- Do not comment out code, delete lines instead.
|
- Do not comment out code, delete lines instead.
|
||||||
|
|
||||||
|
AI Policy
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
For our policy on the use of AI tools, please read `AI Policy`_.
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -17,7 +17,6 @@ help:
|
|||||||
@echo 'install - developer install of SearxNG into virtualenv'
|
@echo 'install - developer install of SearxNG into virtualenv'
|
||||||
@echo 'uninstall - uninstall developer installation'
|
@echo 'uninstall - uninstall developer installation'
|
||||||
@echo 'clean - clean up working tree'
|
@echo 'clean - clean up working tree'
|
||||||
@echo 'search.checker - check search engines'
|
|
||||||
@echo 'test - run shell & CI tests'
|
@echo 'test - run shell & CI tests'
|
||||||
@echo 'test.shell - test shell scripts'
|
@echo 'test.shell - test shell scripts'
|
||||||
@echo 'ci.test - run CI tests'
|
@echo 'ci.test - run CI tests'
|
||||||
@@ -39,12 +38,6 @@ clean: py.clean docs.clean node.clean nvm.clean go.clean test.clean
|
|||||||
$(Q)find . -name '*~' -exec rm -f {} +
|
$(Q)find . -name '*~' -exec rm -f {} +
|
||||||
$(Q)find . -name '*.bak' -exec rm -f {} +
|
$(Q)find . -name '*.bak' -exec rm -f {} +
|
||||||
|
|
||||||
PHONY += search.checker search.checker.%
|
|
||||||
search.checker: install
|
|
||||||
$(Q)./manage pyenv.cmd searxng-checker -v
|
|
||||||
|
|
||||||
search.checker.%: install
|
|
||||||
$(Q)./manage pyenv.cmd searxng-checker -v "$(subst _, ,$(patsubst search.checker.%,%,$@))"
|
|
||||||
|
|
||||||
PHONY += test ci.test test.shell
|
PHONY += test ci.test test.shell
|
||||||
test: test.yamllint test.black test.pyright_modified test.pylint test.unit test.robot test.rst test.shell test.shfmt
|
test: test.yamllint test.black test.pyright_modified test.pylint test.unit test.robot test.rst test.shell test.shfmt
|
||||||
@@ -70,7 +63,7 @@ format: format.python format.shell
|
|||||||
# wrap ./manage script
|
# wrap ./manage script
|
||||||
|
|
||||||
MANAGE += weblate.translations.commit weblate.push.translations
|
MANAGE += weblate.translations.commit weblate.push.translations
|
||||||
MANAGE += data.all data.traits data.useragents data.locales data.currencies
|
MANAGE += data.all data.traits data.useragents data.gsa_useragents data.locales data.currencies
|
||||||
MANAGE += docs.html docs.live docs.gh-pages docs.prebuild docs.clean
|
MANAGE += docs.html docs.live docs.gh-pages docs.prebuild docs.clean
|
||||||
MANAGE += podman.build
|
MANAGE += podman.build
|
||||||
MANAGE += docker.build docker.buildx
|
MANAGE += docker.build docker.buildx
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
## What does this PR do?
|
<!-- FILL IN THESE FIELDS .. and delete the comments after reading.
|
||||||
|
|
||||||
<!-- MANDATORY -->
|
Use Markdown for formatting -> https://www.markdowntools.io/cheat-sheet
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- explain the changes in your PR, algorithms, design, architecture -->
|
### What does this PR do?
|
||||||
|
|
||||||
## Why is this change important?
|
<!-- Explain the motivation and changes in your pull request. -->
|
||||||
|
|
||||||
<!-- MANDATORY -->
|
### How to test this PR locally?
|
||||||
|
|
||||||
<!-- explain the motivation behind your PR -->
|
<!-- Commands to run the tests or instructions to test the changes. Are there
|
||||||
|
any edge cases (environment, language, or other contexts) to take into
|
||||||
|
account? -->
|
||||||
|
|
||||||
## How to test this PR locally?
|
### Related issues
|
||||||
|
|
||||||
<!-- commands to run the tests or instructions to test the changes -->
|
|
||||||
|
|
||||||
## Author's checklist
|
|
||||||
|
|
||||||
<!-- additional notes for reviewers -->
|
|
||||||
|
|
||||||
## Related issues
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Closes #234
|
Closes: #234
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
<!-- ⚠️ Bad AI drivers will be denounced: People who produce bad contributions
|
||||||
|
that are clearly AI (slop) will be blocked for all future contributions.
|
||||||
|
-->
|
||||||
|
|
||||||
|
[AI Policy]: https://github.com/searxng/searxng/blob/master/AI_POLICY.rst
|
||||||
|
|
||||||
|
- [ ] **I hereby confirm that this PR conforms with the [AI Policy].**
|
||||||
|
|
||||||
|
If I have used AI tools for working on the changes in this PR, I will
|
||||||
|
attach a list of all AI tools I used and how I used them. I hereby confirm
|
||||||
|
that I haven't used any other tools than the ones I mention below.
|
||||||
|
|||||||
@@ -46,11 +46,9 @@ Further information on *how-to* can be found `here <https://docs.searxng.org/adm
|
|||||||
Connect
|
Connect
|
||||||
=======
|
=======
|
||||||
|
|
||||||
If you have questions or want to connect with others in the community,
|
If you have questions or want to connect with others in the community:
|
||||||
we have two official channels:
|
|
||||||
|
|
||||||
- `#searxng:matrix.org <https://matrix.to/#/#searxng:matrix.org>`_
|
- `#searxng:matrix.org <https://matrix.to/#/#searxng:matrix.org>`_
|
||||||
- `#searxng @ libera.chat <https://web.libera.chat/?channel=#searxng>`_ (bridged to Matrix)
|
|
||||||
|
|
||||||
Contributing
|
Contributing
|
||||||
============
|
============
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
"extends": ["stylelint-config-standard-less"],
|
"extends": ["stylelint-config-standard-less"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-no-vendor-prefix": null,
|
"at-rule-no-vendor-prefix": null,
|
||||||
|
"at-rule-prelude-no-invalid": null,
|
||||||
"declaration-empty-line-before": null,
|
"declaration-empty-line-before": null,
|
||||||
|
"declaration-property-value-no-unknown": null,
|
||||||
"no-invalid-position-at-import-rule": null,
|
"no-invalid-position-at-import-rule": null,
|
||||||
"prettier/prettier": true,
|
"prettier/prettier": true,
|
||||||
"property-no-vendor-prefix": null,
|
"property-no-vendor-prefix": null,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/2.2.7/schema.json",
|
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||||
"files": {
|
"files": {
|
||||||
"ignoreUnknown": true,
|
"ignoreUnknown": true,
|
||||||
"includes": ["**", "!dist", "!node_modules"]
|
"includes": ["**", "!node_modules", "!src/brand", "!src/svg"]
|
||||||
},
|
},
|
||||||
"assist": {
|
"assist": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"actions": {
|
"actions": {
|
||||||
"recommended": true,
|
"preset": "recommended",
|
||||||
"source": {
|
"source": {
|
||||||
"useSortedAttributes": "on",
|
"useSortedAttributes": "on",
|
||||||
"useSortedProperties": "on"
|
"useSortedProperties": "on"
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
"bracketSameLine": false,
|
"bracketSameLine": false,
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"enabled": true,
|
|
||||||
"formatWithErrors": false,
|
"formatWithErrors": false,
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 2,
|
"indentWidth": 2,
|
||||||
@@ -27,32 +27,29 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true,
|
"preset": "recommended",
|
||||||
"complexity": {
|
"complexity": {
|
||||||
"noForEach": "error",
|
"noForEach": "error",
|
||||||
"noImplicitCoercions": "error",
|
"noImplicitCoercions": "error",
|
||||||
|
"noRedundantDefaultExport": "error",
|
||||||
|
"noUselessCatchBinding": "error",
|
||||||
|
"noUselessUndefined": "error",
|
||||||
|
"useArrayFind": "error",
|
||||||
"useSimplifiedLogicExpression": "error"
|
"useSimplifiedLogicExpression": "error"
|
||||||
},
|
},
|
||||||
"correctness": {
|
"correctness": {
|
||||||
"noGlobalDirnameFilename": "error",
|
"noGlobalDirnameFilename": "error",
|
||||||
"noUndeclaredVariables": {
|
|
||||||
"level": "error",
|
|
||||||
"options": {
|
|
||||||
"checkTypes": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"useImportExtensions": "error",
|
"useImportExtensions": "error",
|
||||||
"useJsonImportAttributes": "error",
|
"useJsonImportAttributes": "error",
|
||||||
"useSingleJsDocAsterisk": "error"
|
"useSingleJsDocAsterisk": "error"
|
||||||
},
|
},
|
||||||
"nursery": {
|
"nursery": {
|
||||||
"noDeprecatedImports": "warn",
|
"noFloatingPromises": "warn",
|
||||||
"noImportCycles": "warn",
|
|
||||||
"noMisusedPromises": "warn",
|
"noMisusedPromises": "warn",
|
||||||
"noUselessCatchBinding": "warn",
|
"useAwaitThenable": "off",
|
||||||
"noUselessUndefined": "warn",
|
|
||||||
"useExhaustiveSwitchCases": "warn",
|
"useExhaustiveSwitchCases": "warn",
|
||||||
"useExplicitType": "warn"
|
"useExplicitType": "off",
|
||||||
|
"useRegexpExec": "warn"
|
||||||
},
|
},
|
||||||
"performance": {
|
"performance": {
|
||||||
"noAwaitInLoops": "error",
|
"noAwaitInLoops": "error",
|
||||||
@@ -65,23 +62,16 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"noCommonJs": "error",
|
"noCommonJs": "error",
|
||||||
"noEnum": "error",
|
"noEnum": "error",
|
||||||
|
"noImplicitBoolean": "error",
|
||||||
|
"noIncrementDecrement": "error",
|
||||||
"noInferrableTypes": "error",
|
"noInferrableTypes": "error",
|
||||||
|
"noMultiAssign": "error",
|
||||||
|
"noMultilineString": "error",
|
||||||
"noNamespace": "error",
|
"noNamespace": "error",
|
||||||
"noNegationElse": "error",
|
"noNegationElse": "error",
|
||||||
"noNestedTernary": "error",
|
"noNestedTernary": "error",
|
||||||
"noParameterAssign": "error",
|
"noParameterAssign": "error",
|
||||||
"noParameterProperties": "error",
|
"noParameterProperties": "error",
|
||||||
"noRestrictedTypes": {
|
|
||||||
"level": "error",
|
|
||||||
"options": {
|
|
||||||
"types": {
|
|
||||||
"Element": {
|
|
||||||
"message": "Element is too generic",
|
|
||||||
"use": "HTMLElement"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"noSubstr": "error",
|
"noSubstr": "error",
|
||||||
"noUnusedTemplateLiteral": "error",
|
"noUnusedTemplateLiteral": "error",
|
||||||
"noUselessElse": "error",
|
"noUselessElse": "error",
|
||||||
@@ -97,6 +87,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"useConsistentBuiltinInstantiation": "error",
|
"useConsistentBuiltinInstantiation": "error",
|
||||||
|
"useConsistentEnumValueType": "error",
|
||||||
"useConsistentMemberAccessibility": {
|
"useConsistentMemberAccessibility": {
|
||||||
"level": "error",
|
"level": "error",
|
||||||
"options": {
|
"options": {
|
||||||
@@ -109,7 +100,14 @@
|
|||||||
"syntax": "explicit"
|
"syntax": "explicit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"useConsistentTypeDefinitions": {
|
||||||
|
"level": "error",
|
||||||
|
"options": {
|
||||||
|
"style": "type"
|
||||||
|
}
|
||||||
|
},
|
||||||
"useDefaultSwitchClause": "error",
|
"useDefaultSwitchClause": "error",
|
||||||
|
"useDestructuring": "error",
|
||||||
"useExplicitLengthCheck": "error",
|
"useExplicitLengthCheck": "error",
|
||||||
"useForOf": "error",
|
"useForOf": "error",
|
||||||
"useGroupedAccessorPairs": "error",
|
"useGroupedAccessorPairs": "error",
|
||||||
@@ -117,6 +115,7 @@
|
|||||||
"useNumericSeparators": "error",
|
"useNumericSeparators": "error",
|
||||||
"useObjectSpread": "error",
|
"useObjectSpread": "error",
|
||||||
"useReadonlyClassProperties": "error",
|
"useReadonlyClassProperties": "error",
|
||||||
|
"useSelfClosingElements": "error",
|
||||||
"useShorthandAssign": "error",
|
"useShorthandAssign": "error",
|
||||||
"useSingleVarDeclarator": "error",
|
"useSingleVarDeclarator": "error",
|
||||||
"useThrowNewError": "error",
|
"useThrowNewError": "error",
|
||||||
@@ -125,11 +124,17 @@
|
|||||||
"useUnifiedTypeSignatures": "error"
|
"useUnifiedTypeSignatures": "error"
|
||||||
},
|
},
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noAlert": "error",
|
|
||||||
"noBitwiseOperators": "error",
|
"noBitwiseOperators": "error",
|
||||||
"noConstantBinaryExpressions": "error",
|
"noConstantBinaryExpressions": "error",
|
||||||
|
"noDeprecatedImports": "error",
|
||||||
"noEmptyBlockStatements": "error",
|
"noEmptyBlockStatements": "error",
|
||||||
|
"noEqualsToNull": "error",
|
||||||
"noEvolvingTypes": "error",
|
"noEvolvingTypes": "error",
|
||||||
|
"noForIn": "error",
|
||||||
|
"noImportCycles": "error",
|
||||||
|
"noNestedPromises": "error",
|
||||||
|
"noParametersOnlyUsedInRecursion": "error",
|
||||||
|
"noReturnAssign": "error",
|
||||||
"noUnassignedVariables": "error",
|
"noUnassignedVariables": "error",
|
||||||
"noVar": "error",
|
"noVar": "error",
|
||||||
"useNumberToFixedDigitsArgument": "error",
|
"useNumberToFixedDigitsArgument": "error",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
this file is generated automatically by searxng_extra/update/update_pygments.py
|
this file is generated automatically by searxng_extra/update/update_pygments.py
|
||||||
using pygments version 2.19.2:
|
using pygments version 2.20.0:
|
||||||
|
|
||||||
./manage templates.simple.pygments
|
./manage templates.simple.pygments
|
||||||
*/
|
*/
|
||||||
|
|||||||
2308
client/simple/package-lock.json
generated
2308
client/simple/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -19,33 +19,31 @@
|
|||||||
"lint:tsc": "tsc --noEmit"
|
"lint:tsc": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"Chrome >= 93",
|
"baseline 2022",
|
||||||
"Firefox >= 92",
|
|
||||||
"Safari >= 15.4",
|
|
||||||
"not dead"
|
"not dead"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ionicons": "~8.0.13",
|
"ionicons": "^8.0.13",
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"ol": "~10.6.1",
|
"ol": "^10.9.0",
|
||||||
"swiped-events": "1.2.0"
|
"swiped-events": "1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.2.7",
|
"@biomejs/biome": "2.5.0",
|
||||||
"@types/node": "~24.9.1",
|
"@types/node": "^26.0.0",
|
||||||
"browserslist": "~4.27.0",
|
"browserslist": "^4.28.2",
|
||||||
"browserslist-to-esbuild": "~2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"edge.js": "~6.3.0",
|
"edge.js": "^6.5.1",
|
||||||
"less": "~4.4.2",
|
"less": "^4.6.6",
|
||||||
"lightningcss": "~1.30.2",
|
"mathjs": "^15.2.0",
|
||||||
"sharp": "~0.34.4",
|
"sharp": "~0.35.1",
|
||||||
"sort-package-json": "~3.4.0",
|
"sort-package-json": "^4.0.0",
|
||||||
"stylelint": "~16.25.0",
|
"stylelint": "^17.13.0",
|
||||||
"stylelint-config-standard-less": "~3.0.1",
|
"stylelint-config-standard-less": "^4.1.0",
|
||||||
"stylelint-prettier": "~5.0.3",
|
"stylelint-prettier": "^5.0.3",
|
||||||
"svgo": "~4.0.0",
|
"svgo": "^4.0.1",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~6.0.3",
|
||||||
"vite": "npm:rolldown-vite@7.1.19",
|
"vite": "^8.0.16",
|
||||||
"vite-bundle-analyzer": "~1.2.3"
|
"vite-bundle-analyzer": "^1.3.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
client/simple/src/js/Plugin.ts
Normal file
66
client/simple/src/js/Plugin.ts
Normal 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;
|
||||||
|
|
||||||
|
queueMicrotask(() => 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>;
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
import "./nojs.ts";
|
|
||||||
import "./router.ts";
|
|
||||||
import "./toolkit.ts";
|
|
||||||
import "./listener.ts";
|
|
||||||
@@ -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");
|
|
||||||
});
|
|
||||||
@@ -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");
|
|
||||||
});
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
import { Endpoints, endpoint, ready, settings } from "./toolkit.ts";
|
|
||||||
|
|
||||||
ready(
|
|
||||||
() => {
|
|
||||||
import("../main/keyboard.ts");
|
|
||||||
import("../main/search.ts");
|
|
||||||
|
|
||||||
if (settings.autocomplete) {
|
|
||||||
import("../main/autocomplete.ts");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ on: [endpoint === Endpoints.index] }
|
|
||||||
);
|
|
||||||
|
|
||||||
ready(
|
|
||||||
() => {
|
|
||||||
import("../main/keyboard.ts");
|
|
||||||
import("../main/mapresult.ts");
|
|
||||||
import("../main/results.ts");
|
|
||||||
import("../main/search.ts");
|
|
||||||
|
|
||||||
if (settings.infinite_scroll) {
|
|
||||||
import("../main/infinite_scroll.ts");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.autocomplete) {
|
|
||||||
import("../main/autocomplete.ts");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ on: [endpoint === Endpoints.results] }
|
|
||||||
);
|
|
||||||
|
|
||||||
ready(
|
|
||||||
() => {
|
|
||||||
import("../main/preferences.ts");
|
|
||||||
},
|
|
||||||
{ on: [endpoint === Endpoints.preferences] }
|
|
||||||
);
|
|
||||||
4
client/simple/src/js/index.ts
Normal file
4
client/simple/src/js/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
// core
|
||||||
|
void import.meta.glob(["./*.ts", "./util/**/.ts"], { eager: true });
|
||||||
36
client/simple/src/js/loader.ts
Normal file
36
client/simple/src/js/loader.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// 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> => {
|
const fetchResults = async (qInput: HTMLInputElement, query: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
@@ -43,8 +44,6 @@ const fetchResults = async (qInput: HTMLInputElement, query: string): Promise<vo
|
|||||||
|
|
||||||
const form = document.querySelector<HTMLFormElement>("#search");
|
const form = document.querySelector<HTMLFormElement>("#search");
|
||||||
form?.submit();
|
form?.submit();
|
||||||
|
|
||||||
autocomplete.classList.remove("open");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
fragment.append(li);
|
fragment.append(li);
|
||||||
@@ -79,6 +78,11 @@ listen("input", qInput, () => {
|
|||||||
const autocomplete: HTMLElement | null = document.querySelector<HTMLElement>(".autocomplete");
|
const autocomplete: HTMLElement | null = document.querySelector<HTMLElement>(".autocomplete");
|
||||||
const autocompleteList: HTMLUListElement | null = document.querySelector<HTMLUListElement>(".autocomplete ul");
|
const autocompleteList: HTMLUListElement | null = document.querySelector<HTMLUListElement>(".autocomplete ul");
|
||||||
if (autocompleteList) {
|
if (autocompleteList) {
|
||||||
|
listen("keydown", qInput, (event: KeyboardEvent) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
autocomplete?.classList.remove("open");
|
||||||
|
}
|
||||||
|
});
|
||||||
listen("keyup", qInput, (event: KeyboardEvent) => {
|
listen("keyup", qInput, (event: KeyboardEvent) => {
|
||||||
const listItems = [...autocompleteList.children] as HTMLElement[];
|
const listItems = [...autocompleteList.children] as HTMLElement[];
|
||||||
|
|
||||||
@@ -104,7 +108,6 @@ if (autocompleteList) {
|
|||||||
newCurrentIndex = (currentIndex + 1) % listItems.length;
|
newCurrentIndex = (currentIndex + 1) % listItems.length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Tab":
|
|
||||||
case "Enter":
|
case "Enter":
|
||||||
if (autocomplete) {
|
if (autocomplete) {
|
||||||
autocomplete.classList.remove("open");
|
autocomplete.classList.remove("open");
|
||||||
@@ -128,4 +131,12 @@ if (autocompleteList) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
listen("blur", qInput, () => {
|
||||||
|
autocomplete?.classList.remove("open");
|
||||||
|
});
|
||||||
|
|
||||||
|
listen("focus", qInput, () => {
|
||||||
|
autocomplete?.classList.add("open");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// 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";
|
export type KeyBindingLayout = "default" | "vim";
|
||||||
|
|
||||||
@@ -189,9 +190,7 @@ const highlightResult =
|
|||||||
|
|
||||||
let next: HTMLElement | undefined;
|
let next: HTMLElement | undefined;
|
||||||
|
|
||||||
if (typeof effectiveWhich !== "string") {
|
if (typeof effectiveWhich === "string") {
|
||||||
next = effectiveWhich;
|
|
||||||
} else {
|
|
||||||
switch (effectiveWhich) {
|
switch (effectiveWhich) {
|
||||||
case "visible": {
|
case "visible": {
|
||||||
const top = document.documentElement.scrollTop || document.body.scrollTop;
|
const top = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
@@ -219,8 +218,10 @@ const highlightResult =
|
|||||||
// biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended
|
// biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended
|
||||||
case "top":
|
case "top":
|
||||||
default:
|
default:
|
||||||
next = results[0];
|
[next] = results;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
next = effectiveWhich;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next) {
|
if (next) {
|
||||||
@@ -342,7 +343,7 @@ const initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBin
|
|||||||
const categories: Record<string, KeyBinding[]> = {};
|
const categories: Record<string, KeyBinding[]> = {};
|
||||||
|
|
||||||
for (const binding of Object.values(keyBindings)) {
|
for (const binding of Object.values(keyBindings)) {
|
||||||
const cat = binding.cat;
|
const { cat } = binding;
|
||||||
categories[cat] ??= [];
|
categories[cat] ??= [];
|
||||||
categories[cat].push(binding);
|
categories[cat].push(binding);
|
||||||
}
|
}
|
||||||
@@ -399,7 +400,7 @@ const toggleHelp = (keyBindings: typeof baseKeyBinding): void => {
|
|||||||
className: "dialog-modal"
|
className: "dialog-modal"
|
||||||
});
|
});
|
||||||
initHelpContent(helpPanel, keyBindings);
|
initHelpContent(helpPanel, keyBindings);
|
||||||
const body = document.getElementsByTagName("body")[0];
|
const [body] = document.getElementsByTagName("body");
|
||||||
if (body) {
|
if (body) {
|
||||||
body.appendChild(helpPanel);
|
body.appendChild(helpPanel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// 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;
|
let engineDescriptions: Record<string, [string, string]> | undefined;
|
||||||
|
|
||||||
@@ -69,8 +70,7 @@ listen("click", "#copy-hash", async function (this: HTMLElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const copiedText = this.dataset.copiedText;
|
if (this.dataset.copiedText) {
|
||||||
if (copiedText) {
|
this.innerText = this.dataset.copiedText;
|
||||||
this.innerText = copiedText;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import "../../../node_modules/swiped-events/src/swiped-events.js";
|
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;
|
let imgTimeoutID: number;
|
||||||
|
|
||||||
@@ -134,9 +135,8 @@ listen("click", "#copy_url", async function (this: HTMLElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const copiedText = this.dataset.copiedText;
|
if (this.dataset.copiedText) {
|
||||||
if (copiedText) {
|
this.innerText = this.dataset.copiedText;
|
||||||
this.innerText = copiedText;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +1,51 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// 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 => {
|
const searchForm: HTMLFormElement = getElement<HTMLFormElement>("search");
|
||||||
if (qInput.value.length > 0) {
|
const searchInput: HTMLInputElement = getElement<HTMLInputElement>("q");
|
||||||
const search = document.getElementById("search") as HTMLFormElement | null;
|
const searchReset: HTMLButtonElement = getElement<HTMLButtonElement>("clear_search");
|
||||||
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 isMobile: boolean = window.matchMedia("(max-width: 50em)").matches;
|
const isMobile: boolean = window.matchMedia("(max-width: 50em)").matches;
|
||||||
const isResultsPage: boolean = document.querySelector("main")?.id === "main_results";
|
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
|
// focus search input on large screens
|
||||||
if (!(isMobile || isResultsPage)) {
|
if (!(isMobile || isResultsPage)) {
|
||||||
qInput.focus();
|
searchInput.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// On mobile, move cursor to the end of the input on focus
|
// On mobile, move cursor to the end of the input on focus
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
listen("focus", qInput, () => {
|
listen("focus", searchInput, () => {
|
||||||
// Defer cursor move until the next frame to prevent a visual jump
|
// Defer cursor move until the next frame to prevent a visual jump
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const end = qInput.value.length;
|
const end = searchInput.value.length;
|
||||||
qInput.setSelectionRange(end, end);
|
searchInput.setSelectionRange(end, end);
|
||||||
qInput.scrollLeft = qInput.scrollWidth;
|
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
|
listen("click", searchReset, (event: MouseEvent) => {
|
||||||
// automatically start a new search request when the user changes a search
|
event.preventDefault();
|
||||||
// filter (safesearch, time range or language) (this requires JavaScript
|
searchInput.value = "";
|
||||||
// though)
|
searchInput.focus();
|
||||||
if (
|
searchReset.classList.add("empty");
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
for (const button of categoryButtons) {
|
||||||
listen("click", button, (event: MouseEvent) => {
|
listen("click", button, (event: MouseEvent) => {
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
@@ -98,21 +61,34 @@ for (const button of categoryButtons) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const form: HTMLFormElement | null = document.querySelector<HTMLFormElement>("#search");
|
if (document.querySelector("div.search_filters")) {
|
||||||
assertElement(form);
|
const safesearchElement = document.getElementById("safesearch");
|
||||||
|
if (safesearchElement) {
|
||||||
// override form submit action to update the actually selected categories
|
listen("change", safesearchElement, () => searchForm.submit());
|
||||||
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(",");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
|
||||||
};
|
|
||||||
93
client/simple/src/js/plugin/Calculator.ts
Normal file
93
client/simple/src/js/plugin/Calculator.ts
Normal 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");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const node = Calculator.math.parse(searchInput.value);
|
||||||
|
return `${node.toString()} = ${node.evaluate()}`;
|
||||||
|
} catch {
|
||||||
|
// not a compatible math expression
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async post(result: string): Promise<void> {
|
||||||
|
appendAnswerElement(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
client/simple/src/js/plugin/InfiniteScroll.ts
Normal file
110
client/simple/src/js/plugin/InfiniteScroll.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
93
client/simple/src/js/plugin/MapView.ts
Normal file
93
client/simple/src/js/plugin/MapView.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import "ol/ol.css";
|
||||||
|
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 = "0", mapLat = "0", mapGeojson } = this.map.dataset;
|
||||||
|
|
||||||
|
const lon = Number.parseFloat(mapLon);
|
||||||
|
const lat = Number.parseFloat(mapLat);
|
||||||
|
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);
|
||||||
|
|
||||||
|
const geoSourceExtent = geoSource.getExtent();
|
||||||
|
if (geoSourceExtent) {
|
||||||
|
view.fit(geoSourceExtent, { padding: [20, 20, 20, 20] });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create GeoJSON layer:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async post(): Promise<void> {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
69
client/simple/src/js/router.ts
Normal file
69
client/simple/src/js/router.ts
Normal 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] }
|
||||||
|
);
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// 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
|
// synced with searx/webapp.py get_client_settings
|
||||||
type Settings = {
|
type Settings = {
|
||||||
advanced_search?: boolean;
|
plugins?: string[];
|
||||||
autocomplete?: string;
|
autocomplete?: string;
|
||||||
autocomplete_min?: number;
|
autocomplete_min?: number;
|
||||||
doi_resolver?: string;
|
doi_resolver?: string;
|
||||||
favicon_resolver?: string;
|
favicon_resolver?: string;
|
||||||
hotkeys?: KeyBindingLayout;
|
hotkeys?: KeyBindingLayout;
|
||||||
infinite_scroll?: boolean;
|
|
||||||
method?: "GET" | "POST";
|
method?: "GET" | "POST";
|
||||||
query_in_title?: boolean;
|
query_in_title?: boolean;
|
||||||
results_on_new_tab?: boolean;
|
results_on_new_tab?: boolean;
|
||||||
@@ -32,8 +31,6 @@ type ReadyOptions = {
|
|||||||
on?: (boolean | undefined)[];
|
on?: (boolean | undefined)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type AssertElement = (element?: HTMLElement | null) => asserts element is HTMLElement;
|
|
||||||
|
|
||||||
export type EndpointsKeys = keyof typeof Endpoints;
|
export type EndpointsKeys = keyof typeof Endpoints;
|
||||||
|
|
||||||
export const Endpoints = {
|
export const Endpoints = {
|
||||||
@@ -73,12 +70,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> => {
|
export const http = async (method: string, url: string | URL, options?: HTTPOptions): Promise<Response> => {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), options?.timeout ?? 30_000);
|
const timeoutId = setTimeout(() => controller.abort(), options?.timeout ?? 30_000);
|
||||||
@@ -131,10 +122,10 @@ export const ready = (callback: () => void, options?: ReadyOptions): void => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState !== "loading") {
|
if (document.readyState === "loading") {
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
listen("DOMContentLoaded", document, callback, { once: true });
|
listen("DOMContentLoaded", document, callback, { once: true });
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
34
client/simple/src/js/util/appendAnswerElement.ts
Normal file
34
client/simple/src/js/util/appendAnswerElement.ts
Normal 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);
|
||||||
|
};
|
||||||
8
client/simple/src/js/util/assertElement.ts
Normal file
8
client/simple/src/js/util/assertElement.ts
Normal 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");
|
||||||
|
}
|
||||||
|
};
|
||||||
21
client/simple/src/js/util/getElement.ts
Normal file
21
client/simple/src/js/util/getElement.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
iframe[src^="https://w.soundcloud.com"]
|
iframe[src^="https://w.soundcloud.com"] {
|
||||||
{
|
|
||||||
height: 120px;
|
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 real size is 92px, but 94px are needed to avoid an inner scrollbar of
|
||||||
// the embedded HTML.
|
// the embedded HTML.
|
||||||
height: 94px;
|
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
|
// 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
|
// 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
|
// request. The alternative text (<img alt='Mixcloud Logo'> then cause an
|
||||||
@@ -23,19 +20,16 @@ iframe[src^="https://www.mixcloud.com"]
|
|||||||
height: 250px;
|
height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe[src^="https://bandcamp.com/EmbeddedPlayer"]
|
iframe[src^="https://bandcamp.com/EmbeddedPlayer"] {
|
||||||
{
|
|
||||||
// show playlist
|
// show playlist
|
||||||
height: 350px;
|
height: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe[src^="https://bandcamp.com/EmbeddedPlayer/track"]
|
iframe[src^="https://bandcamp.com/EmbeddedPlayer/track"] {
|
||||||
{
|
|
||||||
// hide playlist
|
// hide playlist
|
||||||
height: 120px;
|
height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe[src^="https://genius.com/songs"]
|
iframe[src^="https://genius.com/songs"] {
|
||||||
{
|
|
||||||
height: 65px;
|
height: 65px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
background: url("../img/searxng.png") no-repeat;
|
background: url("./img/searxng.png") no-repeat;
|
||||||
min-height: 4rem;
|
min-height: 4rem;
|
||||||
margin: 4rem auto;
|
margin: 4rem auto;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
.stats_endpoint {
|
|
||||||
.github-issue-button {
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.issue-hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checked"] {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin: 1rem 1rem 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step_content {
|
|
||||||
margin: 1rem 1rem 1rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step1,
|
|
||||||
.step2 {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step1_delay {
|
|
||||||
transition: visibility 0s linear 4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#step1:checked ~ .step1,
|
|
||||||
#step2:checked ~ .step2 {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
word-break: break-word;
|
word-break: break-all;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--color-result-keyvalue-table);
|
background-color: var(--color-result-keyvalue-table);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
margin: 0.25rem 0 0 0;
|
margin: 0.25rem 0 0 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
word-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
line-height: 1.24;
|
line-height: 1.24;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,9 @@
|
|||||||
@import "mixins.less";
|
@import "mixins.less";
|
||||||
@import "toolkit.less";
|
@import "toolkit.less";
|
||||||
@import "autocomplete.less";
|
@import "autocomplete.less";
|
||||||
@import "detail.less";
|
|
||||||
@import "animations.less";
|
@import "animations.less";
|
||||||
@import "embedded.less";
|
@import "embedded.less";
|
||||||
@import "info.less";
|
@import "info.less";
|
||||||
@import "new_issue.less";
|
|
||||||
@import "stats.less";
|
@import "stats.less";
|
||||||
@import "result_templates.less";
|
@import "result_templates.less";
|
||||||
@import "weather.less";
|
@import "weather.less";
|
||||||
@@ -188,7 +186,7 @@ article[data-vim-selected].category-social {
|
|||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
word-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
margin: 0.4rem 0 0.4rem 0;
|
margin: 0.4rem 0 0.4rem 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
@@ -222,7 +220,7 @@ article[data-vim-selected].category-social {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
max-width: 54em;
|
max-width: 54em;
|
||||||
word-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
line-height: 1.24;
|
line-height: 1.24;
|
||||||
|
|
||||||
.highlight {
|
.highlight {
|
||||||
@@ -366,7 +364,7 @@ article[data-vim-selected].category-social {
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
margin: 0.25rem 0 0 0;
|
margin: 0.25rem 0 0 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
word-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
line-height: 1.24;
|
line-height: 1.24;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
@@ -571,13 +569,16 @@ article[data-vim-selected].category-social {
|
|||||||
|
|
||||||
#suggestions {
|
#suggestions {
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
padding-left: 0;
|
||||||
flex-flow: column;
|
margin: 0;
|
||||||
justify-content: flex-end;
|
list-style-position: inside;
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
color: var(--color-result-link-font);
|
||||||
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex: 1 1 50%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -585,8 +586,8 @@ article[data-vim-selected].category-social {
|
|||||||
#suggestions,
|
#suggestions,
|
||||||
#infoboxes {
|
#infoboxes {
|
||||||
input {
|
input {
|
||||||
padding: 0;
|
padding: 3px;
|
||||||
margin: 3px;
|
margin: 0;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -688,7 +689,7 @@ summary.title {
|
|||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
grid-area: sidebar;
|
grid-area: sidebar;
|
||||||
word-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
color: var(--color-sidebar-font);
|
color: var(--color-sidebar-font);
|
||||||
|
|
||||||
.infobox {
|
.infobox {
|
||||||
@@ -1029,10 +1030,6 @@ summary.title {
|
|||||||
/ 100%;
|
/ 100%;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
|
|
||||||
#sidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#urls {
|
#urls {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1167,3 +1164,4 @@ pre code {
|
|||||||
@import "result_types/code.less";
|
@import "result_types/code.less";
|
||||||
@import "result_types/paper.less";
|
@import "result_types/paper.less";
|
||||||
@import "result_types/file.less";
|
@import "result_types/file.less";
|
||||||
|
@import "result_types/image.less";
|
||||||
|
|||||||
@@ -610,7 +610,7 @@ td:hover .engine-tooltip,
|
|||||||
.stacked-bar-chart-base();
|
.stacked-bar-chart-base();
|
||||||
|
|
||||||
background: var(--color-base-font);
|
background: var(--color-base-font);
|
||||||
border: 1px solid rgba(var(--color-base-font-rgb), 0.9);
|
border: 1px solid rgb(var(--color-base-font-rgb), 0.9);
|
||||||
padding: 0.3rem 0;
|
padding: 0.3rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +618,7 @@ td:hover .engine-tooltip,
|
|||||||
.stacked-bar-chart-base();
|
.stacked-bar-chart-base();
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid rgba(var(--color-base-font-rgb), 0.3);
|
border: 1px solid rgb(var(--color-base-font-rgb), 0.3);
|
||||||
padding: 0.3rem 0;
|
padding: 0.3rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,7 +626,7 @@ td:hover .engine-tooltip,
|
|||||||
.stacked-bar-chart-base();
|
.stacked-bar-chart-base();
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-bottom: 1px dotted rgba(var(--color-base-font-rgb), 0.5);
|
border-bottom: 1px dotted rgb(var(--color-base-font-rgb), 0.5);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,7 +634,7 @@ td:hover .engine-tooltip,
|
|||||||
.stacked-bar-chart-base();
|
.stacked-bar-chart-base();
|
||||||
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border-left: 1px solid rgba(var(--color-base-font-rgb), 0.9);
|
border-left: 1px solid rgb(var(--color-base-font-rgb), 0.9);
|
||||||
padding: 0.4rem 0;
|
padding: 0.4rem 0;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ export type Src2Dest = {
|
|||||||
* Convert a list of SVG files to PNG.
|
* Convert a list of SVG files to PNG.
|
||||||
*
|
*
|
||||||
* @param items - Array of SVG files (src: SVG, dest:PNG) to convert.
|
* @param items - Array of SVG files (src: SVG, dest:PNG) to convert.
|
||||||
|
* @param width - (optional) width of the PNG pictures
|
||||||
|
* @param height - (optional) height of the PNG pictures.
|
||||||
*/
|
*/
|
||||||
export const svg2png = (items: Src2Dest[]): void => {
|
export const svg2png = (items: Src2Dest[], width?: number, height?: number): void => {
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
fs.mkdirSync(path.dirname(item.dest), { recursive: true });
|
fs.mkdirSync(path.dirname(item.dest), { recursive: true });
|
||||||
|
|
||||||
@@ -29,6 +31,9 @@ export const svg2png = (items: Src2Dest[]): void => {
|
|||||||
compressionLevel: 9,
|
compressionLevel: 9,
|
||||||
palette: true
|
palette: true
|
||||||
})
|
})
|
||||||
|
.resize(width, height, {
|
||||||
|
fit: "contain"
|
||||||
|
})
|
||||||
.toFile(item.dest)
|
.toFile(item.dest)
|
||||||
.then((info) => {
|
.then((info) => {
|
||||||
console.log(`[svg2png] created ${item.dest} -- bytes: ${info.size}, w:${info.width}px, h:${info.height}px`);
|
console.log(`[svg2png] created ${item.dest} -- bytes: ${info.size}, w:${info.width}px, h:${info.height}px`);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Custom vite plugins to build the web-client components of the simple theme.
|
* Custom vite plugins to build the web-client components of the simple theme.
|
||||||
*
|
*
|
||||||
* HINT:
|
* HINT:
|
||||||
* This is an inital implementation for the migration of the build process
|
* This is an initial implementation for the migration of the build process
|
||||||
* from grunt to vite. For fully support (vite: build & serve) more work is
|
* from grunt to vite. For fully support (vite: build & serve) more work is
|
||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
@@ -17,13 +17,15 @@ import { type Src2Dest, svg2png, svg2svg } from "./img.ts";
|
|||||||
* Vite plugin to convert a list of SVG files to PNG.
|
* Vite plugin to convert a list of SVG files to PNG.
|
||||||
*
|
*
|
||||||
* @param items - Array of SVG files (src: SVG, dest:PNG) to convert.
|
* @param items - Array of SVG files (src: SVG, dest:PNG) to convert.
|
||||||
|
* @param width - (optional) width of the PNG picture
|
||||||
|
* @param height - (optional) height of the PNG picture
|
||||||
*/
|
*/
|
||||||
export const plg_svg2png = (items: Src2Dest[]): Plugin => {
|
export const plg_svg2png = (items: Src2Dest[], width?: number, height?: number): Plugin => {
|
||||||
return {
|
return {
|
||||||
name: "searxng-simple-svg2png",
|
name: "searxng-simple-svg2png",
|
||||||
apply: "build",
|
apply: "build",
|
||||||
writeBundle: () => {
|
writeBundle: () => {
|
||||||
svg2png(items);
|
svg2png(items, width, height);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
|
|
||||||
"baseUrl": ".",
|
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
|
||||||
"types": ["vite/client"]
|
"types": ["vite/client"]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const svg2svg_favicon_opts: Config = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
base: "/static/themes/simple/",
|
base: "./",
|
||||||
publicDir: "static/",
|
publicDir: "static/",
|
||||||
|
|
||||||
build: {
|
build: {
|
||||||
@@ -46,39 +46,37 @@ export default {
|
|||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
rolldownOptions: {
|
rolldownOptions: {
|
||||||
input: {
|
input: {
|
||||||
// build CSS files
|
// entrypoint
|
||||||
"searxng-ltr.css": `${PATH.src}/less/style-ltr.less`,
|
core: `${PATH.src}/js/index.ts`,
|
||||||
"searxng-rtl.css": `${PATH.src}/less/style-rtl.less`,
|
|
||||||
"rss.css": `${PATH.src}/less/rss.less`,
|
|
||||||
|
|
||||||
// build script files
|
// stylesheets
|
||||||
"searxng.core": `${PATH.src}/js/core/index.ts`,
|
ltr: `${PATH.src}/less/style-ltr.less`,
|
||||||
|
rtl: `${PATH.src}/less/style-rtl.less`,
|
||||||
// ol pkg
|
rss: `${PATH.src}/less/rss.less`
|
||||||
ol: `${PATH.src}/js/pkg/ol.ts`,
|
|
||||||
"ol.css": `${PATH.modules}/ol/ol.css`
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// file naming conventions / pathnames are relative to outDir (PATH.dist)
|
// file naming conventions / pathnames are relative to outDir (PATH.dist)
|
||||||
output: {
|
output: {
|
||||||
entryFileNames: "js/[name].min.js",
|
entryFileNames: "sxng-[name].min.js",
|
||||||
chunkFileNames: "js/[name].min.js",
|
chunkFileNames: "chunk/[hash].min.js",
|
||||||
assetFileNames: ({ names }: PreRenderedAsset): string => {
|
assetFileNames: ({ names }: PreRenderedAsset): string => {
|
||||||
const [name] = names;
|
const [name] = names;
|
||||||
|
|
||||||
const extension = name?.split(".").pop();
|
switch (name?.split(".").pop()) {
|
||||||
switch (extension) {
|
|
||||||
case "css":
|
case "css":
|
||||||
return "css/[name].min[extname]";
|
return "sxng-[name].min[extname]";
|
||||||
case "js":
|
|
||||||
return "js/[name].min[extname]";
|
|
||||||
case "png":
|
|
||||||
case "svg":
|
|
||||||
return "img/[name][extname]";
|
|
||||||
default:
|
default:
|
||||||
console.warn("Unknown asset:", name);
|
return "sxng-[name][extname]";
|
||||||
return "[name][extname]";
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
sanitizeFileName: (name: string): string => {
|
||||||
|
return name
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[^a-zA-Z0-9.-]/g, "_")
|
||||||
|
.toLowerCase();
|
||||||
|
},
|
||||||
|
comments: {
|
||||||
|
legal: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,6 +132,28 @@ export default {
|
|||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
// SearXNG PWA Icons (static)
|
||||||
|
plg_svg2png(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
src: `${PATH.brand}/searxng-wordmark.svg`,
|
||||||
|
dest: `${PATH.dist}/img/512.png`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
512,
|
||||||
|
512
|
||||||
|
),
|
||||||
|
plg_svg2png(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
src: `${PATH.brand}/searxng-wordmark.svg`,
|
||||||
|
dest: `${PATH.dist}/img/192.png`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
192,
|
||||||
|
192
|
||||||
|
),
|
||||||
|
|
||||||
// -- svg
|
// -- svg
|
||||||
plg_svg2svg(
|
plg_svg2svg(
|
||||||
[
|
[
|
||||||
|
|||||||
15
container/.env.example
Normal file
15
container/.env.example
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Read the documentation before using the `docker-compose.yml` file:
|
||||||
|
# https://docs.searxng.org/admin/installation-docker.html
|
||||||
|
#
|
||||||
|
# Additional ENVs:
|
||||||
|
# https://docs.searxng.org/admin/settings/settings_general.html#settings-general
|
||||||
|
# https://docs.searxng.org/admin/settings/settings_server.html#settings-server
|
||||||
|
|
||||||
|
# Use a specific version tag. E.g. "latest" or "2026.3.25-541c6c3cb".
|
||||||
|
#SEARXNG_VERSION=latest
|
||||||
|
|
||||||
|
# Listen to a specific address.
|
||||||
|
#SEARXNG_HOST=[::]
|
||||||
|
|
||||||
|
# Listen to a specific port.
|
||||||
|
#SEARXNG_PORT=8080
|
||||||
@@ -21,8 +21,6 @@ RUN --mount=type=cache,id=uv,target=/root/.cache/uv set -eux -o pipefail; \
|
|||||||
|
|
||||||
COPY --exclude=./searx/version_frozen.py ./searx/ ./searx/
|
COPY --exclude=./searx/version_frozen.py ./searx/ ./searx/
|
||||||
|
|
||||||
ARG TIMESTAMP_SETTINGS="0"
|
|
||||||
|
|
||||||
RUN set -eux -o pipefail; \
|
RUN set -eux -o pipefail; \
|
||||||
python -m compileall -q -f -j 0 --invalidation-mode=unchecked-hash ./searx/; \
|
python -m compileall -q -f -j 0 --invalidation-mode=unchecked-hash ./searx/; \
|
||||||
find ./searx/static/ -type f \
|
find ./searx/static/ -type f \
|
||||||
@@ -30,5 +28,4 @@ RUN set -eux -o pipefail; \
|
|||||||
-exec gzip -9 -k {} + \
|
-exec gzip -9 -k {} + \
|
||||||
-exec brotli -9 -k {} + \
|
-exec brotli -9 -k {} + \
|
||||||
-exec gzip --test {}.gz + \
|
-exec gzip --test {}.gz + \
|
||||||
-exec brotli --test {}.br +; \
|
-exec brotli --test {}.br +
|
||||||
touch -c --date="@$TIMESTAMP_SETTINGS" ./searx/settings.yml
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ LABEL org.opencontainers.image.created="$CREATED" \
|
|||||||
org.opencontainers.image.url="https://searxng.org" \
|
org.opencontainers.image.url="https://searxng.org" \
|
||||||
org.opencontainers.image.version="$VERSION"
|
org.opencontainers.image.version="$VERSION"
|
||||||
|
|
||||||
ENV SEARXNG_VERSION="$VERSION" \
|
ENV __SEARXNG_VERSION="$VERSION" \
|
||||||
SEARXNG_SETTINGS_PATH="$CONFIG_PATH/settings.yml" \
|
__SEARXNG_SETTINGS_PATH="$__SEARXNG_CONFIG_PATH/settings.yml" \
|
||||||
GRANIAN_PROCESS_NAME="searxng" \
|
GRANIAN_PROCESS_NAME="searxng" \
|
||||||
GRANIAN_INTERFACE="wsgi" \
|
GRANIAN_INTERFACE="wsgi" \
|
||||||
GRANIAN_HOST="::" \
|
GRANIAN_HOST="::" \
|
||||||
@@ -36,8 +36,8 @@ ENV SEARXNG_VERSION="$VERSION" \
|
|||||||
GRANIAN_BLOCKING_THREADS_IDLE_TIMEOUT="5m"
|
GRANIAN_BLOCKING_THREADS_IDLE_TIMEOUT="5m"
|
||||||
|
|
||||||
# "*_PATH" ENVs are defined in base images
|
# "*_PATH" ENVs are defined in base images
|
||||||
VOLUME $CONFIG_PATH
|
VOLUME $__SEARXNG_CONFIG_PATH
|
||||||
VOLUME $DATA_PATH
|
VOLUME $__SEARXNG_DATA_PATH
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
|
|||||||
28
container/docker-compose.yml
Normal file
28
container/docker-compose.yml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Read the documentation before using the `docker-compose.yml` file:
|
||||||
|
# https://docs.searxng.org/admin/installation-docker.html
|
||||||
|
|
||||||
|
name: searxng
|
||||||
|
|
||||||
|
services:
|
||||||
|
core:
|
||||||
|
container_name: searxng-core
|
||||||
|
image: docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- ${SEARXNG_HOST:+${SEARXNG_HOST}:}${SEARXNG_PORT:-8080}:${SEARXNG_PORT:-8080}
|
||||||
|
env_file: ./.env
|
||||||
|
volumes:
|
||||||
|
- ./core-config/:/etc/searxng/:Z
|
||||||
|
- core-data:/var/cache/searxng/
|
||||||
|
|
||||||
|
valkey:
|
||||||
|
container_name: searxng-valkey
|
||||||
|
image: docker.io/valkey/valkey:9-alpine
|
||||||
|
command: valkey-server --save 30 1 --loglevel warning
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- valkey-data:/data/
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
core-data:
|
||||||
|
valkey-data:
|
||||||
@@ -77,56 +77,41 @@ volume_handler() {
|
|||||||
setup_ownership "$target" "directory"
|
setup_ownership "$target" "directory"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Handle configuration file updates
|
setup() {
|
||||||
config_handler() {
|
local template_settings="/usr/local/searxng/settings.template.yml"
|
||||||
local target="$1"
|
local target_settings="$__SEARXNG_CONFIG_PATH/settings.yml"
|
||||||
local template="$2"
|
|
||||||
local new_template_target="$target.new"
|
|
||||||
|
|
||||||
# Create/Update the configuration file
|
|
||||||
if [ -f "$target" ]; then
|
|
||||||
setup_ownership "$target" "file"
|
|
||||||
|
|
||||||
if [ "$template" -nt "$target" ]; then
|
|
||||||
cp -pfT "$template" "$new_template_target"
|
|
||||||
|
|
||||||
|
if [ ! -f "$target_settings" ]; then
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
...
|
...
|
||||||
... INFORMATION
|
... INFORMATION
|
||||||
... Update available for "$target"
|
... "$target_settings" does not exist, creating from template...
|
||||||
... It is recommended to update the configuration file to ensure proper functionality
|
|
||||||
...
|
|
||||||
... New version placed at "$new_template_target"
|
|
||||||
... Please review and merge changes
|
|
||||||
...
|
...
|
||||||
EOF
|
EOF
|
||||||
fi
|
cp -pfT "$template_settings" "$target_settings"
|
||||||
else
|
|
||||||
cat <<EOF
|
|
||||||
...
|
|
||||||
... INFORMATION
|
|
||||||
... "$target" does not exist, creating from template...
|
|
||||||
...
|
|
||||||
EOF
|
|
||||||
cp -pfT "$template" "$target"
|
|
||||||
|
|
||||||
sed -i "s/ultrasecretkey/$(head -c 24 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9')/g" "$target"
|
sed -i "s/ultrasecretkey/$(head -c 24 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9')/g" "$target_settings"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
check_file "$target"
|
check_file "$target_settings"
|
||||||
}
|
}
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
SearXNG $SEARXNG_VERSION
|
SearXNG $__SEARXNG_VERSION
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Check for volume mounts
|
# Check for volume mounts
|
||||||
volume_handler "$CONFIG_PATH"
|
volume_handler "$__SEARXNG_CONFIG_PATH"
|
||||||
volume_handler "$DATA_PATH"
|
volume_handler "$__SEARXNG_DATA_PATH"
|
||||||
|
|
||||||
# Check for files
|
setup
|
||||||
config_handler "$SEARXNG_SETTINGS_PATH" "/usr/local/searxng/searx/settings.yml"
|
|
||||||
|
|
||||||
|
# root only features
|
||||||
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
update-ca-certificates
|
update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ENVs aliases
|
||||||
|
export GRANIAN_PORT="${SEARXNG_PORT:-$GRANIAN_PORT}"
|
||||||
|
|
||||||
exec /usr/local/searxng/.venv/bin/granian searx.webapp:app
|
exec /usr/local/searxng/.venv/bin/granian searx.webapp:app
|
||||||
|
|||||||
8
container/settings.template.yml
Normal file
8
container/settings.template.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Read the documentation before extending the defaults:
|
||||||
|
# https://docs.searxng.org/admin/settings/
|
||||||
|
|
||||||
|
use_default_settings: true
|
||||||
|
|
||||||
|
server:
|
||||||
|
secret_key: "ultrasecretkey"
|
||||||
|
image_proxy: true
|
||||||
57
docs/_static/searxng.css
vendored
Normal file
57
docs/_static/searxng.css
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
.content {
|
||||||
|
width: 52em; /* instead of 46em */
|
||||||
|
}
|
||||||
|
|
||||||
|
p code.literal {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.sidebar {
|
||||||
|
border-color: lightsteelblue;
|
||||||
|
border-radius: 3pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.sidebar-title, .sidebar p {
|
||||||
|
margin: 6pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li,
|
||||||
|
.hlist li {
|
||||||
|
list-style-type: disclosure-closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sphinxsidebar .current > a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* admonitions with (rendered) reST markup examples (:class: rst-example)
|
||||||
|
*
|
||||||
|
* .. admonition:: title of the example
|
||||||
|
* :class: rst-example
|
||||||
|
* ....
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.rst-example {
|
||||||
|
background-color: inherit;
|
||||||
|
margin: 0;
|
||||||
|
border-top: none;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
border-bottom: none;
|
||||||
|
border-left: none;
|
||||||
|
border-radius: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.rst-example > p.admonition-title {
|
||||||
|
font-family: Sans Serif;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.8em;
|
||||||
|
display: block;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinx-tabs {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
151
docs/_themes/searxng/static/searxng.css
vendored
151
docs/_themes/searxng/static/searxng.css
vendored
@@ -1,151 +0,0 @@
|
|||||||
@import url("pocoo.css");
|
|
||||||
|
|
||||||
a, a.reference, a.footnote-reference {
|
|
||||||
color: #004b6b;
|
|
||||||
border-color: #004b6b;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #6d4100;
|
|
||||||
border-color: #6d4100;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.version-warning {
|
|
||||||
background-color: #004b6b;
|
|
||||||
}
|
|
||||||
|
|
||||||
aside.sidebar {
|
|
||||||
background-color: whitesmoke;
|
|
||||||
border-color: lightsteelblue;
|
|
||||||
border-radius: 3pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar p.caption {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sidebar-title, .sidebar p {
|
|
||||||
margin: 6pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li,
|
|
||||||
.hlist li {
|
|
||||||
list-style-type: disclosure-closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sphinxsidebar .current > a {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* admonitions
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.admonition, div.topic, nav.contents, div.toctree-wrapper {
|
|
||||||
background-color: #fafafa;
|
|
||||||
margin: 8px 0px;
|
|
||||||
padding: 1em;
|
|
||||||
border-radius: 3pt 0 0 3pt;
|
|
||||||
border-top: none;
|
|
||||||
border-right: none;
|
|
||||||
border-bottom: none;
|
|
||||||
border-left: 5pt solid #ccc;
|
|
||||||
list-style-type: disclosure-closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.toctree-wrapper p.caption {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 24px;
|
|
||||||
margin: 0 0 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title:after {
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admonition.hint { border-color: #416dc0b0; }
|
|
||||||
.admonition.note { border-color: #6c856cb0; }
|
|
||||||
.admonition.tip { border-color: #85c5c2b0; }
|
|
||||||
.admonition.attention { border-color: #ecec97b0; }
|
|
||||||
.admonition.caution { border-color: #a6c677b0; }
|
|
||||||
.admonition.danger { border-color: #d46262b0; }
|
|
||||||
.admonition.important { border-color: #dfa3a3b0; }
|
|
||||||
.admonition.error { border-color: red; }
|
|
||||||
.admonition.warning { border-color: darkred; }
|
|
||||||
|
|
||||||
.admonition.admonition-generic-admonition-title {
|
|
||||||
border-color: #416dc0b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* admonitions with (rendered) reST markup examples (:class: rst-example)
|
|
||||||
*
|
|
||||||
* .. admonition:: title of the example
|
|
||||||
* :class: rst-example
|
|
||||||
* ....
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.rst-example {
|
|
||||||
background-color: inherit;
|
|
||||||
margin: 0;
|
|
||||||
border-top: none;
|
|
||||||
border-right: 1px solid #ccc;
|
|
||||||
border-bottom: none;
|
|
||||||
border-left: none;
|
|
||||||
border-radius: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.rst-example > p.admonition-title {
|
|
||||||
font-family: Sans Serif;
|
|
||||||
font-style: italic;
|
|
||||||
font-size: 0.8em;
|
|
||||||
display: block;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* code block in figures
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.highlight pre {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table theme
|
|
||||||
*/
|
|
||||||
|
|
||||||
thead, tfoot {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
th:hover, td:hover {
|
|
||||||
background-color: #ffc;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead th, tfoot th, tfoot td, tbody th {
|
|
||||||
background-color: #fffaef;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:nth-child(odd) {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody tr:nth-child(even) {
|
|
||||||
background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
caption {
|
|
||||||
font-family: Sans Serif;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin: 0.5em 0 0.5em 0;
|
|
||||||
caption-side: top;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinx-tabs {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
7
docs/_themes/searxng/theme.conf
vendored
7
docs/_themes/searxng/theme.conf
vendored
@@ -1,7 +0,0 @@
|
|||||||
[theme]
|
|
||||||
inherit = pocoo
|
|
||||||
stylesheet = searxng.css
|
|
||||||
|
|
||||||
[options]
|
|
||||||
touch_icon =
|
|
||||||
globaltoc_maxdepth = 5
|
|
||||||
@@ -48,7 +48,7 @@ solve the CAPTCHA from `qwant.com <https://www.qwant.com/>`__.
|
|||||||
|
|
||||||
.. group-tab:: Firefox
|
.. group-tab:: Firefox
|
||||||
|
|
||||||
.. kernel-figure:: answer-captcha/ffox-setting-proxy-socks.png
|
.. kernel-figure:: /assets/answer-captcha/ffox-setting-proxy-socks.png
|
||||||
:alt: FFox proxy on SOCKS5, 127.0.0.1:8080
|
:alt: FFox proxy on SOCKS5, 127.0.0.1:8080
|
||||||
|
|
||||||
Firefox's network settings
|
Firefox's network settings
|
||||||
@@ -66,4 +66,3 @@ solve the CAPTCHA from `qwant.com <https://www.qwant.com/>`__.
|
|||||||
|
|
||||||
-N
|
-N
|
||||||
Do not execute a remote command. This is useful for just forwarding ports.
|
Do not execute a remote command. This is useful for just forwarding ports.
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
Buildhosts
|
Buildhosts
|
||||||
==========
|
==========
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
To get best results from build, it's recommend to install additional packages on
|
To get best results from build, it's recommend to install additional packages on
|
||||||
build hosts (see :ref:`searxng.sh`).
|
build hosts (see :ref:`searxng.sh`).
|
||||||
|
|
||||||
|
|||||||
@@ -61,11 +61,6 @@ section might give you some guidance.
|
|||||||
- `Apache Fedora`_
|
- `Apache Fedora`_
|
||||||
- `Apache directives`_
|
- `Apache directives`_
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
|
|
||||||
The Apache HTTP server
|
The Apache HTTP server
|
||||||
======================
|
======================
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ Installation container
|
|||||||
.. _Podman rootless containers: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
|
.. _Podman rootless containers: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
|
||||||
.. _DockerHub mirror: https://hub.docker.com/r/searxng/searxng
|
.. _DockerHub mirror: https://hub.docker.com/r/searxng/searxng
|
||||||
.. _GHCR mirror: https://ghcr.io/searxng/searxng
|
.. _GHCR mirror: https://ghcr.io/searxng/searxng
|
||||||
.. _Docker compose: https://github.com/searxng/searxng-docker
|
|
||||||
|
|
||||||
.. sidebar:: info
|
.. sidebar:: info
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ Installation container
|
|||||||
`Docker 101`_ before proceeding.
|
`Docker 101`_ before proceeding.
|
||||||
|
|
||||||
Container images are the basis for deployments in containerized environments,
|
Container images are the basis for deployments in containerized environments,
|
||||||
`Docker compose`_, Kubernetes and more.
|
Compose, Kubernetes and more.
|
||||||
|
|
||||||
.. _Container installation:
|
.. _Container installation:
|
||||||
|
|
||||||
@@ -55,10 +54,10 @@ In the case of Docker, you need to add the user running the container to the
|
|||||||
In the case of Podman, no additional steps are generally required, but there
|
In the case of Podman, no additional steps are generally required, but there
|
||||||
are some considerations when running `Podman rootless containers`_.
|
are some considerations when running `Podman rootless containers`_.
|
||||||
|
|
||||||
.. _Container pulling images:
|
.. _Container registries:
|
||||||
|
|
||||||
Pulling images
|
Registries
|
||||||
--------------
|
----------
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@@ -70,26 +69,115 @@ The official images are mirrored at:
|
|||||||
- `DockerHub mirror`_
|
- `DockerHub mirror`_
|
||||||
- `GHCR mirror`_ (GitHub Container Registry)
|
- `GHCR mirror`_ (GitHub Container Registry)
|
||||||
|
|
||||||
Pull the latest image:
|
.. _Container compose instancing:
|
||||||
|
|
||||||
|
Compose instancing
|
||||||
|
==================
|
||||||
|
|
||||||
|
This is the recommended way to deploy SearXNG in a containerized environment.
|
||||||
|
Compose templates allow you to define container configurations in a
|
||||||
|
declarative manner.
|
||||||
|
|
||||||
|
.. _Container compose instancing setup:
|
||||||
|
|
||||||
|
Setup
|
||||||
|
-----
|
||||||
|
|
||||||
|
1. Create the environment:
|
||||||
|
|
||||||
.. code:: sh
|
.. code:: sh
|
||||||
|
|
||||||
$ docker pull docker.io/searxng/searxng:latest
|
# Create the environment and configuration directories
|
||||||
|
$ mkdir -p ./searxng/core-config/
|
||||||
|
$ cd ./searxng/
|
||||||
|
|
||||||
\.\. or if you want to lock in to a specific version:
|
# Fetch the latest compose template
|
||||||
|
$ curl -fsSL \
|
||||||
|
-O https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
|
||||||
|
-O https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example
|
||||||
|
|
||||||
|
2. Copy the ``.env.example`` file and edit the values as needed:
|
||||||
|
|
||||||
.. code:: sh
|
.. code:: sh
|
||||||
|
|
||||||
$ docker pull docker.io/searxng/searxng:2025.8.1-3d96414
|
$ cp -i .env.example .env
|
||||||
|
|
||||||
.. _Container instancing:
|
# nano or your preferred text editor...
|
||||||
|
$ nano .env
|
||||||
|
|
||||||
Instancing
|
3. Start & stop the services:
|
||||||
==========
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose up -d
|
||||||
|
$ docker compose down
|
||||||
|
|
||||||
|
4. Setup your settings in ``core-config/settings.yml`` according to your preferences.
|
||||||
|
|
||||||
|
.. _Container compose instancing maintenance:
|
||||||
|
|
||||||
|
Management
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
|
||||||
|
Remember to review the new templates for any changes that may affect your
|
||||||
|
deployment, and update the ``.env`` file accordingly.
|
||||||
|
|
||||||
|
To update the templates to their latest versions:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose down
|
||||||
|
$ curl -fsSLO \
|
||||||
|
https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
|
||||||
|
https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example
|
||||||
|
$ docker compose up -d
|
||||||
|
|
||||||
|
To update the services to their latest versions:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose down
|
||||||
|
$ docker compose pull
|
||||||
|
$ docker compose up -d
|
||||||
|
|
||||||
|
List running services:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose ps
|
||||||
|
NAME IMAGE ... CREATED STATUS PORTS
|
||||||
|
searxng-core ... ... 3 minutes ago Up 3 minutes 0.0.0.0:8080->8080/tcp
|
||||||
|
searxng-valkey ... ... 3 minutes ago Up 3 minutes 6379/tcp
|
||||||
|
|
||||||
|
Print a service container logs:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose logs -f core
|
||||||
|
|
||||||
|
Access a service container shell (troubleshooting):
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose exec -it --user root core /bin/sh -l
|
||||||
|
/usr/local/searxng #
|
||||||
|
|
||||||
|
Stop and remove the services:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker compose down
|
||||||
|
|
||||||
|
.. _Container manual instancing:
|
||||||
|
|
||||||
|
Manual instancing
|
||||||
|
=================
|
||||||
|
|
||||||
This section is intended for advanced users who need custom deployments. We
|
This section is intended for advanced users who need custom deployments. We
|
||||||
recommend using `Docker compose`_, which provides a preconfigured environment
|
recommend using `Container compose instancing`_, which provides a preconfigured
|
||||||
with sensible defaults.
|
environment with sensible defaults.
|
||||||
|
|
||||||
Basic container instancing example:
|
Basic container instancing example:
|
||||||
|
|
||||||
@@ -121,12 +209,18 @@ List running containers:
|
|||||||
CONTAINER ID IMAGE ... CREATED PORTS NAMES
|
CONTAINER ID IMAGE ... CREATED PORTS NAMES
|
||||||
1af574997e63 ... ... 3 minutes ago 0.0.0.0:8888->8080/tcp searxng
|
1af574997e63 ... ... 3 minutes ago 0.0.0.0:8888->8080/tcp searxng
|
||||||
|
|
||||||
|
Print the container logs:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ docker container logs -f searxng
|
||||||
|
|
||||||
Access the container shell (troubleshooting):
|
Access the container shell (troubleshooting):
|
||||||
|
|
||||||
.. code:: sh
|
.. code:: sh
|
||||||
|
|
||||||
$ docker container exec -it --user root searxng /bin/sh -l
|
$ docker container exec -it --user root searxng /bin/sh -l
|
||||||
1af574997e63:/usr/local/searxng#
|
/usr/local/searxng #
|
||||||
|
|
||||||
Stop and remove the container:
|
Stop and remove the container:
|
||||||
|
|
||||||
@@ -153,18 +247,12 @@ Environment variables
|
|||||||
The following environment variables can be configured:
|
The following environment variables can be configured:
|
||||||
|
|
||||||
- ``$SEARXNG_*``: Controls the SearXNG configuration options, look out for
|
- ``$SEARXNG_*``: Controls the SearXNG configuration options, look out for
|
||||||
environment ``$SEARXNG_*`` in :ref:`settings server` and :ref:`settings
|
environment ``$SEARXNG_*`` in :ref:`settings server`, :ref:`settings
|
||||||
general`.
|
general` and the :origin:`container/.env.example` template.
|
||||||
- ``$GRANIAN_*``: Controls the :ref:`Granian server options <Granian configuration>`.
|
- ``$GRANIAN_*``: Controls the :ref:`Granian server options <Granian configuration>`.
|
||||||
- ``$FORCE_OWNERSHIP``: Ensures mounted volumes/files are owned by the
|
- ``$FORCE_OWNERSHIP``: Ensures mounted volumes/files are owned by the
|
||||||
``searxng:searxng`` user (default: ``true``)
|
``searxng:searxng`` user (default: ``true``)
|
||||||
|
|
||||||
Container internal paths (don't modify unless you know what you're doing):
|
|
||||||
|
|
||||||
- ``$CONFIG_PATH``: Path to the SearXNG configuration directory (default: ``/etc/searxng``)
|
|
||||||
- ``$SEARXNG_SETTINGS_PATH``: Path to the SearXNG settings file (default: ``$CONFIG_PATH/settings.yml``)
|
|
||||||
- ``$DATA_PATH``: Path to the SearXNG data directory (default: ``/var/cache/searxng``)
|
|
||||||
|
|
||||||
.. _Container custom certificates:
|
.. _Container custom certificates:
|
||||||
|
|
||||||
Custom certificates
|
Custom certificates
|
||||||
@@ -176,6 +264,8 @@ additional certificates as needed.
|
|||||||
They will be available on container (re)start or when running
|
They will be available on container (re)start or when running
|
||||||
``update-ca-certificates`` in the container shell.
|
``update-ca-certificates`` in the container shell.
|
||||||
|
|
||||||
|
This requires the container to be running with ``root`` privileges.
|
||||||
|
|
||||||
.. _Container custom images:
|
.. _Container custom images:
|
||||||
|
|
||||||
Custom images
|
Custom images
|
||||||
@@ -199,3 +289,61 @@ container images are not officially supported):
|
|||||||
localhost/searxng/searxng builder ... About a minute ago 524 MB
|
localhost/searxng/searxng builder ... About a minute ago 524 MB
|
||||||
ghcr.io/searxng/base searxng-builder ... 2 days ago 378 MB
|
ghcr.io/searxng/base searxng-builder ... 2 days ago 378 MB
|
||||||
ghcr.io/searxng/base searxng ... 2 days ago 42.2 MB
|
ghcr.io/searxng/base searxng ... 2 days ago 42.2 MB
|
||||||
|
|
||||||
|
Migrate from ``searxng-docker``
|
||||||
|
===============================
|
||||||
|
|
||||||
|
We expect the following source directory structure:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
.
|
||||||
|
└── searxng-docker
|
||||||
|
├── searxng
|
||||||
|
│ ├── favicons.toml
|
||||||
|
│ ├── limiter.toml
|
||||||
|
│ ├── settings.yml
|
||||||
|
│ └── ...
|
||||||
|
├── .env
|
||||||
|
├── Caddyfile
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── ...
|
||||||
|
|
||||||
|
Create a brand new environment outside ``searxng-docker`` directory, following
|
||||||
|
`Container compose instancing setup`_.
|
||||||
|
|
||||||
|
Once up and running, stop the services and move the configuration files from
|
||||||
|
the old mount to the new one:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
$ mv ./searxng-docker/searxng/* ./searxng/core-config/
|
||||||
|
|
||||||
|
If using Valkey features like bot protection (limiter), you will need to update
|
||||||
|
the URL hostname in :origin:`searx/settings.yml` file to ``valkey`` or
|
||||||
|
``searxng-valkey``.
|
||||||
|
|
||||||
|
If you have any environment variables in the old ``.env`` file, make
|
||||||
|
sure to add them to the new one.
|
||||||
|
|
||||||
|
Consider setting up a reverse proxy if exposing the instance to the public.
|
||||||
|
|
||||||
|
You should end with the following directory structure:
|
||||||
|
|
||||||
|
.. code:: sh
|
||||||
|
|
||||||
|
.
|
||||||
|
├── searxng
|
||||||
|
│ ├── core-config
|
||||||
|
│ │ ├── favicons.toml
|
||||||
|
│ │ ├── limiter.toml
|
||||||
|
│ │ ├── settings.yml
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── .env.example
|
||||||
|
│ ├── .env
|
||||||
|
│ └── docker-compose.yml
|
||||||
|
└── searxng-docker
|
||||||
|
└── ...
|
||||||
|
|
||||||
|
If everything is working on the new environment, you can remove the old
|
||||||
|
``searxng-docker`` directory and its contents.
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ section might give you some guidance.
|
|||||||
- `uWSGI support from nginx`_
|
- `uWSGI support from nginx`_
|
||||||
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
|
|
||||||
The nginx HTTP server
|
The nginx HTTP server
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,6 @@
|
|||||||
Step by step installation
|
Step by step installation
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
|
|
||||||
In this section we show the setup of a SearXNG instance that will be installed
|
In this section we show the setup of a SearXNG instance that will be installed
|
||||||
by the :ref:`installation scripts`.
|
by the :ref:`installation scripts`.
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ uWSGI
|
|||||||
- `systemd.unit`_
|
- `systemd.unit`_
|
||||||
- `uWSGI Emperor`_
|
- `uWSGI Emperor`_
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
|
|
||||||
.. _systemd.unit: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
|
.. _systemd.unit: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
|
||||||
.. _One service per app in systemd:
|
.. _One service per app in systemd:
|
||||||
https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
|
https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ Favicons
|
|||||||
|
|
||||||
Don't activate the favicons before reading the documentation.
|
Don't activate the favicons before reading the documentation.
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
Activating the favicons in SearXNG is very easy, but this **generates a
|
Activating the favicons in SearXNG is very easy, but this **generates a
|
||||||
significantly higher load** in the client/server communication and increases
|
significantly higher load** in the client/server communication and increases
|
||||||
resources needed on the server.
|
resources needed on the server.
|
||||||
@@ -250,4 +245,3 @@ into the *proxy*:
|
|||||||
.. _data URL:
|
.. _data URL:
|
||||||
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
|
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
|
||||||
.. _FQN: https://en.wikipedia.org/wiki/Fully_qualified_name
|
.. _FQN: https://en.wikipedia.org/wiki/Fully_qualified_name
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,5 @@ Limiter
|
|||||||
|
|
||||||
The limiter requires a :ref:`Valkey <settings valkey>` database.
|
The limiter requires a :ref:`Valkey <settings valkey>` database.
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. automodule:: searx.limiter
|
.. automodule:: searx.limiter
|
||||||
:members:
|
:members:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Settings
|
|||||||
settings_search
|
settings_search
|
||||||
settings_server
|
settings_server
|
||||||
settings_ui
|
settings_ui
|
||||||
|
settings_preferences
|
||||||
settings_redis
|
settings_redis
|
||||||
settings_valkey
|
settings_valkey
|
||||||
settings_outgoing
|
settings_outgoing
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ file.
|
|||||||
- :ref:`use_default_settings.yml`
|
- :ref:`use_default_settings.yml`
|
||||||
- :ref:`search API`
|
- :ref:`search API`
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. _settings location:
|
.. _settings location:
|
||||||
|
|
||||||
settings.yml location
|
settings.yml location
|
||||||
|
|||||||
@@ -124,14 +124,17 @@ engine is shown. Most of the options have a default value or even are optional.
|
|||||||
|
|
||||||
``api_key`` : optional
|
``api_key`` : optional
|
||||||
In a few cases, using an API needs the use of a secret key. How to obtain them
|
In a few cases, using an API needs the use of a secret key. How to obtain them
|
||||||
is described in the file.
|
is described in the file. Engines that require an API key are set to
|
||||||
|
``inactive: true`` by default. To enable such an engine, provide the API key
|
||||||
|
and set ``inactive: false``.
|
||||||
|
|
||||||
``disabled`` : optional
|
``disabled`` : optional
|
||||||
To disable by default the engine, but not deleting it. It will allow the user
|
To disable by default the engine, but not deleting it. It will allow the user
|
||||||
to manually activate it in the settings.
|
to manually activate it in the settings.
|
||||||
|
|
||||||
``inactive``: optional
|
``inactive``: optional
|
||||||
Remove the engine from the settings (*disabled & removed*).
|
Remove the engine from the settings (*disabled & removed*). This defaults to ``true`` for engines
|
||||||
|
that require an API key, please see the ``api_key`` section if you want to enable such an engine.
|
||||||
|
|
||||||
``language`` : optional
|
``language`` : optional
|
||||||
If you want to use another language for a specific engine, you can define it
|
If you want to use another language for a specific engine, you can define it
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ The built-in plugins are all located in the namespace `searx.plugins`.
|
|||||||
searx.plugins.calculator.SXNGPlugin:
|
searx.plugins.calculator.SXNGPlugin:
|
||||||
active: true
|
active: true
|
||||||
|
|
||||||
|
searx.plugins.infinite_scroll.SXNGPlugin:
|
||||||
|
active: false
|
||||||
|
|
||||||
searx.plugins.hash_plugin.SXNGPlugin:
|
searx.plugins.hash_plugin.SXNGPlugin:
|
||||||
active: true
|
active: true
|
||||||
|
|
||||||
@@ -99,11 +102,18 @@ The built-in plugins are all located in the namespace `searx.plugins`.
|
|||||||
external plugins
|
external plugins
|
||||||
================
|
================
|
||||||
|
|
||||||
|
SearXNG supports :ref:`external plugins <dev plugin>` / there is no need to
|
||||||
|
install one, SearXNG runs out of the box.
|
||||||
|
|
||||||
|
`Only show green hosted results`_:
|
||||||
|
SearXNG plugin to check if a domain is part of the Green WEB.
|
||||||
|
|
||||||
|
`SearXNG BM25 Reranker`_:
|
||||||
|
SearXNG plugin that reranks search results using BM25 text relevance scoring
|
||||||
|
to improve search quality.
|
||||||
|
|
||||||
.. _Only show green hosted results:
|
.. _Only show green hosted results:
|
||||||
https://github.com/return42/tgwf-searx-plugins/
|
https://github.com/return42/tgwf-searx-plugins/
|
||||||
|
|
||||||
SearXNG supports *external plugins* / there is no need to install one, SearXNG
|
.. _SearXNG BM25 Reranker:
|
||||||
runs out of the box.
|
https://github.com/Oaklight/searxng-bm25-reranker
|
||||||
|
|
||||||
- `Only show green hosted results`_
|
|
||||||
- ..
|
|
||||||
|
|||||||
8
docs/admin/settings/settings_preferences.rst
Normal file
8
docs/admin/settings/settings_preferences.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.. _settings preferences:
|
||||||
|
|
||||||
|
================
|
||||||
|
``preferences:``
|
||||||
|
================
|
||||||
|
|
||||||
|
.. autoclass:: searx._settings.SettingsPref
|
||||||
|
:members:
|
||||||
@@ -36,18 +36,19 @@
|
|||||||
|
|
||||||
- ``360search``
|
- ``360search``
|
||||||
- ``baidu``
|
- ``baidu``
|
||||||
|
- ``bing``
|
||||||
- ``brave``
|
- ``brave``
|
||||||
- ``dbpedia``
|
- ``dbpedia``
|
||||||
- ``duckduckgo``
|
- ``duckduckgo``
|
||||||
- ``google``
|
- ``google``
|
||||||
- ``mwmbl``
|
- ``mwmbl``
|
||||||
- ``naver``
|
- ``naver``
|
||||||
|
- ``privacywall``
|
||||||
- ``quark``
|
- ``quark``
|
||||||
- ``qwant``
|
- ``qwant``
|
||||||
- ``seznam``
|
- ``seznam``
|
||||||
- ``sogou``
|
- ``sogou``
|
||||||
- ``startpage``
|
- ``startpage``
|
||||||
- ``stract``
|
|
||||||
- ``swisscows``
|
- ``swisscows``
|
||||||
- ``wikipedia``
|
- ``wikipedia``
|
||||||
- ``yandex``
|
- ``yandex``
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
activated:
|
activated:
|
||||||
|
|
||||||
- :py:obj:`searx.botdetection.link_token` in the :ref:`limiter`
|
- :py:obj:`searx.botdetection.link_token` in the :ref:`limiter`
|
||||||
|
- :ref:`image_proxy`
|
||||||
|
|
||||||
.. _image_proxy:
|
.. _image_proxy:
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
ui:
|
ui:
|
||||||
default_locale: ""
|
default_locale: ""
|
||||||
query_in_title: false
|
query_in_title: false
|
||||||
infinite_scroll: false
|
|
||||||
center_alignment: false
|
center_alignment: false
|
||||||
cache_url: https://web.archive.org/web/
|
cache_url: https://web.archive.org/web/
|
||||||
default_theme: simple
|
default_theme: simple
|
||||||
@@ -32,9 +31,6 @@
|
|||||||
When true, the result page's titles contains the query it decreases the
|
When true, the result page's titles contains the query it decreases the
|
||||||
privacy, since the browser can records the page titles.
|
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``
|
``center_alignment`` : default ``false``
|
||||||
When enabled, the results are centered instead of being in the left (or RTL)
|
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*
|
side of the screen. This setting only affects the *desktop layout*
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ SearXNG maintenance
|
|||||||
- :ref:`toolboxing`
|
- :ref:`toolboxing`
|
||||||
- :ref:`uWSGI maintenance`
|
- :ref:`uWSGI maintenance`
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. _update searxng:
|
.. _update searxng:
|
||||||
|
|
||||||
How to update
|
How to update
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
1
docs/assets/sponsors/browserstack.svg
Normal file
1
docs/assets/sponsors/browserstack.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.2 KiB |
1
docs/assets/sponsors/docker.svg
Normal file
1
docs/assets/sponsors/docker.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.9 KiB |
1
docs/assets/sponsors/tuta.svg
Normal file
1
docs/assets/sponsors/tuta.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 1024 384"><path fill="#410002" d="M479.178 119.294c-.533-.016-.998.357-1.218 1.078l-24.438 78.364.005-.004c-8.59 27.537 4.516 46.485 34.268 46.485 4.336 0 10.006-.357 11.776-.797.885-.264 1.33-.71 1.594-1.506l5.134-17.177c.264-.973-.09-1.77-1.507-1.683-4.517.445-8.588.797-12.308.797-14.964 0-21.075-7.968-16.646-22.224l10.446-33.47h30.373c.797 0 1.506-.445 1.858-1.418l5.401-17.355c.264-.973-.264-1.681-1.417-1.681H492.66l3.895-12.662c.265-.885.089-1.418-.62-2.034l-15.761-14.255c-.332-.3-.676-.45-.996-.459zm173.64 0c-.532-.016-.996.357-1.218 1.078l-24.436 78.364.005-.004c-8.59 27.537 4.517 46.485 34.268 46.485 4.342 0 10.004-.357 11.778-.797.884-.264 1.324-.71 1.593-1.506l5.133-17.177c.26-.973 0-1.77-1.508-1.683-4.517.445-8.59.797-12.307.797-14.966 0-21.077-7.968-16.646-22.224l10.445-33.47H690.3c.795 0 1.504-.445 1.854-1.418l5.402-17.355c.265-.973-.26-1.681-1.414-1.681H666.3l3.896-12.662c.265-.885.087-1.418-.618-2.034l-15.765-14.255c-.332-.3-.676-.45-.996-.459zm-48.32 29.404c-.974 0-1.503.444-1.862 1.417L590.502 188.9c-7.525 23.998-19.478 37.721-31.965 37.721-12.487 0-17.797-9.83-13.105-24.883l16.028-51.178c.351-1.149-.088-1.857-1.328-1.857H539.94c-.97 0-1.505.444-1.86 1.417l-15.497 49.233.008-.005c-8.765 27.982 5.315 46.31 27.452 46.31 12.747 0 22.756-6.111 29.93-16.118l-.176 12.838c0 1.241.621 1.593 1.681 1.593h14.17c1.064 0 1.504-.445 1.859-1.418l28.512-91.997c.35-1.15-.09-1.858-1.33-1.858zm147.96.005c-43.653 0-60.654 37.719-60.654 62.157-.09 21.339 13.282 34.798 31.08 34.798v.004c11.868 0 21.693-5.314 29.133-16.117v12.836c0 1.061.62 1.596 1.594 1.596h14.166c.974 0 1.505-.446 1.86-1.42l28.777-92.086c.265-.973-.266-1.768-1.24-1.768zm-.616 20.54h17.265l-6.197 19.57c-7.35 23.289-18.684 37.896-32.585 37.896-10.094 0-15.585-6.907-15.585-18.15 0-17.976 13.722-39.315 37.102-39.315z"/><path fill="#850122" d="M226.561 106.964c-.558.007-1.043.428-1.043 1.095V251.59c0 1.594 2.04 1.594 2.48 0L261.38 143.3c.445-1.241.446-2.039-.62-3.1l-33.204-32.762c-.299-.332-.66-.478-.996-.474zm55.983 41.739c-1.241 0-1.594.444-2.039 1.417l-43.919 142.203c-.176.797.177 1.594 1.242 1.594h145.747c1.418 0 2.04-.62 2.48-1.858l44.098-141.499c.445-1.417-.18-1.857-1.417-1.857zm-40.022-58.62c-1.418 0-1.594 1.242-.797 2.04l35.065 35.24c.796.798 1.594 1.061 2.836 1.061h149.467c1.065 0 1.68-1.24.62-2.214l-34.63-34.885c-.796-.796-1.592-1.242-3.274-1.242z"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -119,8 +119,12 @@ ${fedora_build}
|
|||||||
pip install -U pip
|
pip install -U pip
|
||||||
pip install -U setuptools
|
pip install -U setuptools
|
||||||
pip install -U wheel
|
pip install -U wheel
|
||||||
|
|
||||||
|
# additional packages required for installation
|
||||||
pip install -U pyyaml
|
pip install -U pyyaml
|
||||||
pip install -U msgspec
|
pip install -U msgspec
|
||||||
|
pip install -U typing-extensions
|
||||||
|
pip install -U pybind11
|
||||||
|
|
||||||
# jump to SearXNG's working tree and install SearXNG into virtualenv
|
# jump to SearXNG's working tree and install SearXNG into virtualenv
|
||||||
(${SERVICE_USER})$ cd \"$SEARXNG_SRC\"
|
(${SERVICE_USER})$ cd \"$SEARXNG_SRC\"
|
||||||
@@ -165,7 +169,7 @@ ${fedora_build}
|
|||||||
$ sudo -H -u ${SERVICE_USER} -i
|
$ sudo -H -u ${SERVICE_USER} -i
|
||||||
(${SERVICE_USER})$ cd ${SEARXNG_SRC}
|
(${SERVICE_USER})$ cd ${SEARXNG_SRC}
|
||||||
(${SERVICE_USER})$ export SEARXNG_SETTINGS_PATH=\"${SEARXNG_SETTINGS_PATH}\"
|
(${SERVICE_USER})$ export SEARXNG_SETTINGS_PATH=\"${SEARXNG_SETTINGS_PATH}\"
|
||||||
(${SERVICE_USER})$ python searx/webapp.py
|
(${SERVICE_USER})$ python -m searx.webapp
|
||||||
|
|
||||||
# disable debug
|
# disable debug
|
||||||
$ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARXNG_SETTINGS_PATH\"
|
$ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARXNG_SETTINGS_PATH\"
|
||||||
|
|||||||
75
docs/conf.py
75
docs/conf.py
@@ -1,8 +1,7 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import sys, os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pallets_sphinx_themes import ProjectLink
|
|
||||||
|
|
||||||
from searx import get_setting
|
from searx import get_setting
|
||||||
from searx.version import VERSION_STRING, GIT_URL, GIT_BRANCH
|
from searx.version import VERSION_STRING, GIT_URL, GIT_BRANCH
|
||||||
@@ -98,7 +97,6 @@ extlinks['pull-searx'] = ('https://github.com/searx/searx/pull/%s', 'PR %s')
|
|||||||
extlinks['origin'] = (GIT_URL + '/blob/' + GIT_BRANCH + '/%s', 'git://%s')
|
extlinks['origin'] = (GIT_URL + '/blob/' + GIT_BRANCH + '/%s', 'git://%s')
|
||||||
extlinks['patch'] = (GIT_URL + '/commit/%s', '#%s')
|
extlinks['patch'] = (GIT_URL + '/commit/%s', '#%s')
|
||||||
extlinks['docs'] = (DOCS_URL + '/%s', 'docs: %s')
|
extlinks['docs'] = (DOCS_URL + '/%s', 'docs: %s')
|
||||||
extlinks['pypi'] = ('https://pypi.org/project/%s', 'PyPi: %s')
|
|
||||||
extlinks['man'] = ('https://manpages.debian.org/jump?q=%s', '%s')
|
extlinks['man'] = ('https://manpages.debian.org/jump?q=%s', '%s')
|
||||||
#extlinks['role'] = (
|
#extlinks['role'] = (
|
||||||
# 'https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-%s', '')
|
# 'https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-%s', '')
|
||||||
@@ -117,7 +115,6 @@ extensions = [
|
|||||||
'sphinx.ext.viewcode',
|
'sphinx.ext.viewcode',
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinx.ext.intersphinx",
|
"sphinx.ext.intersphinx",
|
||||||
"pallets_sphinx_themes",
|
|
||||||
"sphinx_issues", # https://github.com/sloria/sphinx-issues/blob/master/README.rst
|
"sphinx_issues", # https://github.com/sloria/sphinx-issues/blob/master/README.rst
|
||||||
"sphinx_jinja", # https://github.com/tardyp/sphinx-jinja
|
"sphinx_jinja", # https://github.com/tardyp/sphinx-jinja
|
||||||
"sphinxcontrib.programoutput", # https://github.com/NextThought/sphinxcontrib-programoutput
|
"sphinxcontrib.programoutput", # https://github.com/NextThought/sphinxcontrib-programoutput
|
||||||
@@ -158,15 +155,49 @@ issues_github_path = "searxng/searxng"
|
|||||||
|
|
||||||
# HTML -----------------------------------------------------------------
|
# HTML -----------------------------------------------------------------
|
||||||
|
|
||||||
|
html_theme = "furo"
|
||||||
|
html_theme_options = {
|
||||||
|
# link to project source at GitHub for editing and viewing docs code
|
||||||
|
"source_repository": "https://github.com/searxng/searxng/",
|
||||||
|
"source_branch": "master",
|
||||||
|
"source_directory": "docs/",
|
||||||
|
|
||||||
|
# Show link to repository & searx.space in footer
|
||||||
|
"footer_icons": [
|
||||||
|
{
|
||||||
|
"name": "GitHub",
|
||||||
|
"url": "https://github.com/searxng/searxng/",
|
||||||
|
"html": "💾",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "searx.space",
|
||||||
|
"url": "https://searx.space/",
|
||||||
|
"html": "🌐",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
html_sidebars = {
|
||||||
|
"**": [
|
||||||
|
"sidebar/scroll-start.html",
|
||||||
|
"sidebar/brand.html",
|
||||||
|
"sidebar/search.html",
|
||||||
|
"sidebar/navigation.html",
|
||||||
|
"sidebar/ethical-ads.html",
|
||||||
|
"sidebar/scroll-end.html",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
html_css_files = [
|
||||||
|
'searxng.css',
|
||||||
|
]
|
||||||
|
|
||||||
# https://searxng.github.io/searxng --> '/searxng/'
|
# https://searxng.github.io/searxng --> '/searxng/'
|
||||||
# https://docs.searxng.org --> '/'
|
# https://docs.searxng.org --> '/'
|
||||||
notfound_urls_prefix = '/'
|
notfound_urls_prefix = '/'
|
||||||
|
|
||||||
sys.path.append(os.path.abspath('_themes'))
|
|
||||||
sys.path.insert(0, os.path.abspath("../"))
|
|
||||||
html_theme_path = ['_themes']
|
|
||||||
html_theme = "searxng"
|
|
||||||
|
|
||||||
# sphinx.ext.imgmath setup
|
# sphinx.ext.imgmath setup
|
||||||
html_math_renderer = 'imgmath'
|
html_math_renderer = 'imgmath'
|
||||||
imgmath_image_format = 'svg'
|
imgmath_image_format = 'svg'
|
||||||
@@ -174,34 +205,10 @@ imgmath_font_size = 14
|
|||||||
# sphinx.ext.imgmath setup END
|
# sphinx.ext.imgmath setup END
|
||||||
|
|
||||||
html_show_sphinx = False
|
html_show_sphinx = False
|
||||||
html_theme_options = {"index_sidebar_logo": True}
|
|
||||||
html_context = {"project_links": [] }
|
|
||||||
html_context["project_links"].append(ProjectLink("Source", GIT_URL + '/tree/' + GIT_BRANCH))
|
|
||||||
|
|
||||||
if WIKI_URL:
|
|
||||||
html_context["project_links"].append(ProjectLink("Wiki", WIKI_URL))
|
|
||||||
if PUBLIC_INSTANCES:
|
|
||||||
html_context["project_links"].append(ProjectLink("Public instances", PUBLIC_INSTANCES))
|
|
||||||
if ISSUE_URL:
|
|
||||||
html_context["project_links"].append(ProjectLink("Issue Tracker", ISSUE_URL))
|
|
||||||
if PRIVACYPOLICY_URL:
|
|
||||||
html_context["project_links"].append(ProjectLink("Privacy Policy", PRIVACYPOLICY_URL))
|
|
||||||
if CONTACT_URL:
|
|
||||||
html_context["project_links"].append(ProjectLink("Contact", CONTACT_URL))
|
|
||||||
|
|
||||||
html_sidebars = {
|
|
||||||
"**": [
|
|
||||||
"globaltoc.html",
|
|
||||||
"project.html",
|
|
||||||
"relations.html",
|
|
||||||
"searchbox.html",
|
|
||||||
"sourcelink.html"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
|
|
||||||
html_logo = "../client/simple/src/brand/searxng-wordmark.svg"
|
html_logo = "../client/simple/src/brand/searxng-wordmark.svg"
|
||||||
html_title = "SearXNG Documentation ({})".format(VERSION_STRING)
|
html_title = "SearXNG Documentation ({})".format(VERSION_STRING)
|
||||||
html_show_sourcelink = True
|
html_show_sourcelink = True
|
||||||
|
html_copy_source = True
|
||||||
|
|
||||||
# LaTeX ----------------------------------------------------------------
|
# LaTeX ----------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
How to contribute
|
How to contribute
|
||||||
=================
|
=================
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
Prime directives: Privacy, Hackability
|
Prime directives: Privacy, Hackability
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
Demo Offline Engine
|
Demo Offline Engine
|
||||||
===================
|
===================
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. automodule:: searx.engines.demo_offline
|
.. automodule:: searx.engines.demo_offline
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
Demo Online Engine
|
Demo Online Engine
|
||||||
==================
|
==================
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. automodule:: searx.engines.demo_online
|
.. automodule:: searx.engines.demo_online
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
Engine Overview
|
Engine Overview
|
||||||
===============
|
===============
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 3
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. _metasearch-engine: https://en.wikipedia.org/wiki/Metasearch_engine
|
.. _metasearch-engine: https://en.wikipedia.org/wiki/Metasearch_engine
|
||||||
|
|
||||||
.. sidebar:: Further reading ..
|
.. sidebar:: Further reading ..
|
||||||
@@ -112,7 +107,7 @@ module:
|
|||||||
======================= =========== ===========================================
|
======================= =========== ===========================================
|
||||||
base_url string base-url, can be overwritten to use same
|
base_url string base-url, can be overwritten to use same
|
||||||
engine on other URL
|
engine on other URL
|
||||||
number_of_results int maximum number of results per request
|
page_size int maximum number of results per request
|
||||||
language string ISO code of language and country like en_US
|
language string ISO code of language and country like en_US
|
||||||
api_key string api-key if required by engine
|
api_key string api-key if required by engine
|
||||||
======================= =========== ===========================================
|
======================= =========== ===========================================
|
||||||
|
|||||||
@@ -4,12 +4,6 @@
|
|||||||
Engine Implementations
|
Engine Implementations
|
||||||
======================
|
======================
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:caption: Framework Components
|
:caption: Framework Components
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|||||||
@@ -4,10 +4,5 @@
|
|||||||
JSON Engine
|
JSON Engine
|
||||||
============
|
============
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. automodule:: searx.engines.json_engine
|
.. automodule:: searx.engines.json_engine
|
||||||
:members:
|
:members:
|
||||||
|
|||||||
@@ -4,10 +4,5 @@
|
|||||||
MediaWiki Engine
|
MediaWiki Engine
|
||||||
================
|
================
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:depth: 2
|
|
||||||
:local:
|
|
||||||
:backlinks: entry
|
|
||||||
|
|
||||||
.. automodule:: searx.engines.mediawiki
|
.. automodule:: searx.engines.mediawiki
|
||||||
:members:
|
:members:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user