Compare commits

...

401 Commits

Author SHA1 Message Date
Elian Doran
2e0f606a7a chore(release): prepare for v0.98.0 2025-08-17 22:46:16 +03:00
Elian Doran
87878dd6a7 Translations update from Hosted Weblate (#6679) 2025-08-17 22:20:39 +03:00
morteza rahvard
5296e073cc Translated using Weblate (Persian)
Currently translated at 0.7% (12 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fa/
2025-08-17 19:17:36 +00:00
acwr47
7bfb7d6f6e Translated using Weblate (Japanese)
Currently translated at 65.4% (1014 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-17 19:17:35 +00:00
Elian Doran
b5069cc7c2 chore(call_to_action): rephrase 2025-08-17 22:17:21 +03:00
Elian Doran
3b6791f51a chore(call_to_action): disable background effects for now 2025-08-17 21:23:22 +03:00
Elian Doran
0b0be77e02 chore(deps): update dependency @sveltejs/kit to v2.31.1 (#6676) 2025-08-17 08:23:45 +03:00
renovate[bot]
60db10559e chore(deps): update dependency @sveltejs/kit to v2.31.1 2025-08-17 02:31:17 +00:00
Elian Doran
76b066ba4a Translations update from Hosted Weblate (#6673) 2025-08-16 23:27:47 +03:00
ali mohammadi
a28db32369 Translated using Weblate (Persian)
Currently translated at 1.5% (6 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fa/
2025-08-16 20:02:07 +00:00
ali mohammadi
2523632391 Translated using Weblate (Persian)
Currently translated at 0.1% (3 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fa/
2025-08-16 20:02:05 +00:00
neketos851
53548c356a Translated using Weblate (Ukrainian)
Currently translated at 2.1% (8 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-16 20:02:03 +00:00
neketos851
565904ff5d Translated using Weblate (Ukrainian)
Currently translated at 3.1% (49 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-16 20:02:01 +00:00
acwr47
e0c5545f8c Translated using Weblate (Japanese)
Currently translated at 65.2% (1012 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-16 20:01:59 +00:00
Aristide Bauchart
bc21285289 Translated using Weblate (French)
Currently translated at 81.7% (309 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fr/
2025-08-16 20:01:57 +00:00
Elian Doran
318d504fad chore(deps): update dependency @types/node to v22.17.2 (#6665) 2025-08-16 08:55:02 +03:00
Elian Doran
fd5038148c Merge branch 'main' into renovate/node-22.x 2025-08-16 08:55:00 +03:00
renovate[bot]
693ca9291e chore(deps): update dependency @types/node to v22.17.2 2025-08-16 05:45:48 +00:00
Elian Doran
cfd8afc226 chore(deps): update dependency @sveltejs/kit to v2.31.0 (#6666) 2025-08-16 08:44:44 +03:00
Elian Doran
3e52ca7600 chore(deps): update dependency electron to v37.3.0 (#6667) 2025-08-16 08:44:09 +03:00
renovate[bot]
482522e802 chore(deps): update dependency electron to v37.3.0 2025-08-16 02:10:14 +00:00
renovate[bot]
8b5b6a01c6 chore(deps): update dependency @sveltejs/kit to v2.31.0 2025-08-16 02:09:37 +00:00
Elian Doran
e4403dd316 Translations update from Hosted Weblate (#6664) 2025-08-15 23:04:33 +03:00
ali mohammadi
3f267fe6c9 Added translation using Weblate (Persian) 2025-08-15 21:31:45 +02:00
ali mohammadi
3229471485 Added translation using Weblate (Persian) 2025-08-15 21:31:44 +02:00
neketos851
62bac1adf9 Translated using Weblate (Ukrainian)
Currently translated at 0.7% (3 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-15 21:31:43 +02:00
neketos851
82becfd52a Translated using Weblate (Ukrainian)
Currently translated at 0.5% (9 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-15 21:31:43 +02:00
Elian Doran
92f035545b Translations update from Hosted Weblate (#6663) 2025-08-15 21:49:49 +03:00
neketos851
74d8ea7dcb Added translation using Weblate (Ukrainian) 2025-08-15 20:45:02 +02:00
neketos851
ac3f087279 Added translation using Weblate (Ukrainian) 2025-08-15 20:45:02 +02:00
VortexP
1cc4eb98c1 Translated using Weblate (Finnish)
Currently translated at 1.5% (6 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fi/
2025-08-15 20:45:01 +02:00
VortexP
e99bdf8f24 Translated using Weblate (Finnish)
Currently translated at 6.0% (94 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fi/
2025-08-15 20:45:01 +02:00
acwr47
b4f521a141 Translated using Weblate (Japanese)
Currently translated at 60.8% (943 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-15 20:45:01 +02:00
VortexP
1e23bc09f1 Translated using Weblate (Finnish)
Currently translated at 1.3% (5 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fi/
2025-08-15 16:07:51 +00:00
VortexP
e3ec90405d Translated using Weblate (Finnish)
Currently translated at 1.6% (25 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fi/
2025-08-15 16:07:51 +00:00
acwr47
41c87794a4 Translated using Weblate (Japanese)
Currently translated at 60.7% (942 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-15 16:07:50 +00:00
VortexP
e62d2d4fda Added translation using Weblate (Finnish) 2025-08-15 16:07:50 +00:00
VortexP
93adaa0f52 Added translation using Weblate (Finnish) 2025-08-15 16:07:49 +00:00
Excal
263a5d2067 Translated using Weblate (Russian)
Currently translated at 7.9% (30 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ru/
2025-08-15 16:07:48 +00:00
acwr47
f0a5005794 Translated using Weblate (Japanese)
Currently translated at 60.3% (936 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-15 16:07:48 +00:00
Elian Doran
577457c8ab fix(docs): Update links to Trilium repo files in advanced config docs (#6662) 2025-08-15 19:07:39 +03:00
Jon Fuller
c0c450c444 fix(docs): Update links to Trilium repo files in advanced config docs 2025-08-15 08:39:40 -07:00
Elian Doran
1e1e0b0f51 Fix (Update): No update notification in the global menu (#6657) 2025-08-15 16:56:12 +03:00
Elian Doran
a19204a1d5 Translations update from Hosted Weblate (#6661) 2025-08-15 16:55:35 +03:00
Flowerlywind
1d139bfdfe Translated using Weblate (Vietnamese)
Currently translated at 2.1% (33 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/vi/
2025-08-15 14:02:12 +02:00
Excal
75072decec Translated using Weblate (Russian)
Currently translated at 1.8% (7 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ru/
2025-08-15 14:02:09 +02:00
Francis C
0cf2ad6901 Translated using Weblate (Japanese)
Currently translated at 96.8% (366 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-15 14:02:08 +02:00
acwr47
ccbd57a0c0 Translated using Weblate (Japanese)
Currently translated at 54.5% (845 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-15 14:02:07 +02:00
Francis C
92e6c8c445 Translated using Weblate (Japanese)
Currently translated at 54.5% (845 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-15 14:02:06 +02:00
Francis C
1e966f1d59 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hant/
2025-08-15 14:02:05 +02:00
Francis C
6872c2194e Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-15 14:02:03 +02:00
Bruno MARGUERIN
5b6a0216db Translated using Weblate (French)
Currently translated at 71.1% (269 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fr/
2025-08-15 14:02:01 +02:00
Elian Doran
e9a7194cd6 Translated using Weblate (Romanian)
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ro/
2025-08-15 14:01:59 +02:00
Bruno MARGUERIN
26898b9122 Translated using Weblate (French)
Currently translated at 82.3% (1277 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fr/
2025-08-15 14:01:58 +02:00
SiriusXT
ab1d8594ea Fix (Update): No update notification in the global menu 2025-08-15 16:04:59 +08:00
Elian Doran
1a15782686 fix(deps): update dependency i18next to v25.3.6 (#6652) 2025-08-15 09:05:12 +03:00
Elian Doran
3bd0aeef77 chore(deps): update svelte monorepo (#6654) 2025-08-15 09:04:48 +03:00
Elian Doran
b463baedd2 chore(deps): update dependency tsx to v4.20.4 (#6632) 2025-08-15 09:04:22 +03:00
Elian Doran
ae77c41dab chore(deps): update tailwindcss monorepo to v4.1.12 (#6651) 2025-08-15 09:03:58 +03:00
Elian Doran
807d909acd chore(deps): update dependency @anthropic-ai/sdk to v0.60.0 (#6653) 2025-08-15 09:03:36 +03:00
Elian Doran
fa4f5f526e chore(deps): update dependency turndown to v7.2.1 (#6650) 2025-08-15 09:03:13 +03:00
renovate[bot]
edff43cdb3 chore(deps): update dependency tsx to v4.20.4 2025-08-15 05:51:01 +00:00
renovate[bot]
46fe45528c chore(deps): update svelte monorepo 2025-08-15 02:17:16 +00:00
renovate[bot]
b4b53da6a4 chore(deps): update dependency @anthropic-ai/sdk to v0.60.0 2025-08-15 02:16:43 +00:00
renovate[bot]
41fd270080 fix(deps): update dependency i18next to v25.3.6 2025-08-15 02:16:06 +00:00
renovate[bot]
410bb3cdca chore(deps): update tailwindcss monorepo to v4.1.12 2025-08-15 02:15:22 +00:00
renovate[bot]
bc6fc24fbd chore(deps): update dependency turndown to v7.2.1 2025-08-15 02:14:46 +00:00
Jon Fuller
f8e20a1405 Update README.md (#6648) 2025-08-14 12:31:35 -07:00
MeIchthys
558ae1a2ea Update README.md
- Update links to point to new TriliumNext/Trilium repo
- Update a couple broken links
2025-08-14 14:59:08 -04:00
Elian Doran
1dfcf960d3 fix(client): missing calendar view language support 2025-08-14 15:20:08 +03:00
Elian Doran
9bdc51a3fb feat(i18n): add Japanese language 2025-08-14 14:51:57 +03:00
Elian Doran
dbf3bcfacf Merge remote-tracking branch 'weblate/main' 2025-08-14 14:34:30 +03:00
Elian Doran
3d5b269315 chore(docs): fix file 2025-08-14 14:23:37 +03:00
Elian Doran
48f97da9cc chore(forge/rpm): rename key properly 2025-08-14 14:10:33 +03:00
Elian Doran
9c954fbd81 Create CNAME 2025-08-14 13:53:25 +03:00
Elian Doran
c6bd41654f chore(forge/rpm): add public key 2025-08-14 13:40:23 +03:00
Francis C
d65a74bb23 Translated using Weblate (Japanese)
Currently translated at 96.8% (366 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-14 12:30:37 +02:00
acwr47
ff08bca042 Translated using Weblate (Japanese)
Currently translated at 41.6% (646 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-14 12:30:36 +02:00
Francis C
a5d3d2e3b4 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-14 12:30:35 +02:00
Bruno MARGUERIN
496a0667ee Translated using Weblate (French)
Currently translated at 71.1% (269 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fr/
2025-08-14 12:30:34 +02:00
Bruno MARGUERIN
9be688b667 Translated using Weblate (French)
Currently translated at 80.7% (1252 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fr/
2025-08-14 12:30:33 +02:00
Elian Doran
f3d9008c61 feat(forge): rpm signing (#6646) 2025-08-14 13:30:26 +03:00
Elian Doran
649a43c978 fix(forge): RPM signing not done on the right file 2025-08-14 12:45:18 +03:00
Elian Doran
50568704ca feat(forge): minor improvements to RPM signing 2025-08-14 12:40:19 +03:00
Elian Doran
b66b4dec83 feat(forge): proper rpm signing 2025-08-14 12:04:12 +03:00
Francis C
8d0e807435 Translated using Weblate (Japanese)
Currently translated at 96.8% (366 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-14 09:02:31 +00:00
acwr47
bf05ed7caf Translated using Weblate (Japanese)
Currently translated at 96.8% (366 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-14 09:02:29 +00:00
Elian Doran
b5080eff00 Translated using Weblate (Russian)
Currently translated at 55.6% (863 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-08-14 09:02:28 +00:00
Francis C
c474769dd6 Translated using Weblate (Japanese)
Currently translated at 35.4% (550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-14 09:02:27 +00:00
acwr47
a6ae01da0b Translated using Weblate (Japanese)
Currently translated at 35.4% (550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-14 09:02:25 +00:00
Francis C
2bf4c44dbf Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hant/
2025-08-14 09:02:24 +00:00
Francis C
5ca0fbba13 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-14 09:02:22 +00:00
Elian Doran
4cd84b2019 Translated using Weblate (Romanian)
Currently translated at 99.0% (1536 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ro/
2025-08-14 09:02:19 +00:00
Marcelo Popper Costa
c502a45cf5 Translated using Weblate (Portuguese (Brazil))
Currently translated at 22.3% (346 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-14 09:02:17 +00:00
Francis C
9e66914306 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-14 09:02:15 +00:00
Elian Doran
d33d27ee82 feat(forge): validate rpm signing 2025-08-14 11:45:59 +03:00
Elian Doran
e2b13573ae feat(forge): rpm signing 2025-08-14 10:43:38 +03:00
Elian Doran
ec74f5f1de feat(logs): provide an option to keep all logs (#6644) 2025-08-14 08:51:46 +03:00
Elian Doran
5dee56debc Add Traditional Chinese translation for README file & fix Docker Hub URL (#6645) 2025-08-14 08:43:08 +03:00
Francis C.
5623fc992d Update README-ZH_TW.md (tiny fix) 2025-08-14 12:04:45 +08:00
Francis C.
1d28bfc570 Update README-ZH_TW.md (tiny fix) 2025-08-14 11:25:53 +08:00
Francis C.
084327e973 Revise some words for Simplified Chinese translation 2025-08-14 11:20:32 +08:00
Francis C.
b2885efdc1 Update README-ZH_CN.md 2025-08-14 10:56:54 +08:00
Francis C.
b65a75f138 fix relative path for URLs 2025-08-14 10:27:41 +08:00
Francis C.
0ee7f50bb4 Move readme to docs folder 2025-08-14 10:15:03 +08:00
Francis C.
02ce21bc18 Add readme file translation for Traditional Chinese & fix Docker Hub URL 2025-08-14 10:12:14 +08:00
Romain DEP.
3ba487bb00 feat(logs): provide an option to keep all logs 2025-08-13 23:35:31 +02:00
Elian Doran
384a89b0e3 feat(etapi): also save note revision via etapi if needed too (#6602) 2025-08-13 19:45:56 +03:00
Elian Doran
e7fd9371b6 test(etapi): variable shadowing causing spurious error 2025-08-13 19:22:48 +03:00
Elian Doran
aa83429816 feat(logs): cleanup physical log files after 90 days by default (#6609) 2025-08-13 16:12:11 +03:00
Elian Doran
221ab02c24 docs(help): document backend logs retention 2025-08-13 16:10:11 +03:00
Elian Doran
0c4b751e8f Merge branch 'main' into feat/snapshot-etapi-notes-too 2025-08-13 15:17:27 +03:00
Elian Doran
43fd0924a1 feat(log): read from config.ini instead of options 2025-08-13 15:10:19 +03:00
Elian Doran
7a036fc777 fix(log): cyclic dependency to options breaking tests 2025-08-13 14:39:26 +03:00
Elian Doran
54efa6b38c Merge branch 'main' into feat/cleanup-logs 2025-08-13 13:26:52 +03:00
Elian Doran
6e37c9ee5a feat(search): improve search weights and operators (#6536) 2025-08-13 13:10:30 +03:00
Elian Doran
963f4586f3 docs: ✏️ Improve OIDC docs (#6628) 2025-08-13 13:03:37 +03:00
Elian Doran
4d0edebed3 Translations update from Hosted Weblate (#6627) 2025-08-13 12:03:28 +03:00
acwr47
cb39e8d0f8 Translated using Weblate (Japanese)
Currently translated at 72.4% (274 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:33:02 +00:00
Francis C
a336f472b8 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-13 08:33:01 +00:00
acwr47
0a097e72be Translated using Weblate (Japanese)
Currently translated at 63.7% (241 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:33:01 +00:00
acwr47
077f10af7b Translated using Weblate (Japanese)
Currently translated at 61.1% (231 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:33:00 +00:00
acwr47
9317658fc7 Translated using Weblate (Japanese)
Currently translated at 60.8% (230 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:32:59 +00:00
acwr47
21a13f2124 Translated using Weblate (Japanese)
Currently translated at 8.3% (130 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-13 08:32:59 +00:00
acwr47
db6658c05f Translated using Weblate (Japanese)
Currently translated at 55.0% (208 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:32:58 +00:00
acwr47
653af0bc06 Translated using Weblate (Japanese)
Currently translated at 20.6% (78 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:32:57 +00:00
acwr47
93c5281af7 Translated using Weblate (Japanese)
Currently translated at 20.3% (77 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-13 08:32:57 +00:00
Francis C
ce28fbc968 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-13 08:32:56 +00:00
Marcelo Popper Costa
eb41c45711 Translated using Weblate (Portuguese (Brazil))
Currently translated at 22.0% (342 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-13 08:32:56 +00:00
Francis C
17ab14e098 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1550 of 1550 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-13 08:32:55 +00:00
Hosted Weblate
a42f7b4ece Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/
2025-08-13 08:32:54 +00:00
Elian Doran
c7d69fa66b chore(deps): update dependency copy-webpack-plugin to v13.0.1 (#6630) 2025-08-13 11:32:44 +03:00
renovate[bot]
1da5c083ee chore(deps): update dependency copy-webpack-plugin to v13.0.1 2025-08-13 08:13:22 +00:00
Elian Doran
4fb911da40 chore(deps): update dependency vite to v7.1.2 (#6633) 2025-08-13 11:09:38 +03:00
Elian Doran
881417f860 Revert "fix(print): table captions not displayed properly (closes #6483)"
This reverts commit 3e0ef10b25.
2025-08-13 11:06:53 +03:00
renovate[bot]
9748b8bf94 chore(deps): update dependency vite to v7.1.2 2025-08-13 08:01:05 +00:00
Elian Doran
337326da2b fix(deps): update ckeditor monorepo to v46.0.1 (#6635) 2025-08-13 10:58:17 +03:00
Elian Doran
a088134d9b fix(deps): update dependency tsx to v4.20.4 (#6636) 2025-08-13 10:58:07 +03:00
renovate[bot]
e49100f3f4 fix(deps): update dependency tsx to v4.20.4 2025-08-13 07:30:57 +00:00
renovate[bot]
3c638e1574 fix(deps): update ckeditor monorepo to v46.0.1 2025-08-13 07:30:22 +00:00
Elian Doran
9131edf021 chore(deps): update dependency esbuild to v0.25.9 (#6631) 2025-08-13 10:24:19 +03:00
Elian Doran
52a1318475 chore(deps): update svelte monorepo (#6639) 2025-08-13 10:22:30 +03:00
renovate[bot]
5a7483d7c7 chore(deps): update svelte monorepo 2025-08-13 05:32:46 +00:00
renovate[bot]
41f2748829 chore(deps): update dependency esbuild to v0.25.9 2025-08-13 05:29:40 +00:00
Elian Doran
66bd5268ca chore(deps): update typescript-eslint monorepo to v8.39.1 (#6634) 2025-08-13 08:27:52 +03:00
Elian Doran
ebef134af7 chore(deps): update dependency typedoc-plugin-missing-exports to v4.1.0 (#6637) 2025-08-13 08:27:30 +03:00
Elian Doran
1173bf22ab chore(deps): update dependency webdriverio to v9.19.1 (#6638) 2025-08-13 08:26:34 +03:00
Elian Doran
e8f6828168 chore(deps): update dependency @types/tabulator-tables to v6.2.10 (#6629) 2025-08-13 08:26:10 +03:00
Elian Doran
02c9339f9c chore(deps): update actions/checkout action to v5 (#6640) 2025-08-13 08:25:51 +03:00
renovate[bot]
c72bf42684 chore(deps): update actions/checkout action to v5 2025-08-13 01:52:37 +00:00
renovate[bot]
f42eeb7ee8 chore(deps): update dependency webdriverio to v9.19.1 2025-08-13 01:51:53 +00:00
renovate[bot]
3d876121cc chore(deps): update dependency typedoc-plugin-missing-exports to v4.1.0 2025-08-13 01:51:12 +00:00
renovate[bot]
f9bcd7d90a chore(deps): update typescript-eslint monorepo to v8.39.1 2025-08-13 01:49:16 +00:00
renovate[bot]
b3af14fccb chore(deps): update dependency @types/tabulator-tables to v6.2.10 2025-08-13 01:10:44 +00:00
Jin
d224f33913 docs: ✏️ Improve OIDC docs 2025-08-12 22:03:36 +02:00
Elian Doran
3a5f33ba91 Merge remote-tracking branch 'weblate/main' 2025-08-12 22:54:06 +03:00
Elian Doran
e1ae8701b2 feat(client): rename themes 2025-08-12 22:39:03 +03:00
Hosted Weblate
aff5a4d0d5 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/
2025-08-12 19:18:38 +00:00
Hosted Weblate
17467a9c29 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/
2025-08-12 19:18:33 +00:00
acwr47
ebed661863 Translated using Weblate (Japanese)
Currently translated at 15.6% (59 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-12 19:07:14 +00:00
Elian Doran
c2e9f4764b Call to action (switch to Next theme, enable background effects) (#6625) 2025-08-12 22:07:04 +03:00
Elian Doran
7e5b87f00a feat(desktop): enable background effects by default for new users 2025-08-12 22:06:19 +03:00
Elian Doran
70182e863c test: enable background effects in integration DB 2025-08-12 21:41:46 +03:00
Elian Doran
f0d30c4e34 fix(canvas): links not working on desktop (fixes #6606) 2025-08-12 21:31:04 +03:00
Elian Doran
013e7a6aa4 refactor(canvas): use new rendering mechanism 2025-08-12 21:04:57 +03:00
Elian Doran
1b25b18d9e refactor(canvas): use tsx where appropriate 2025-08-12 20:59:45 +03:00
Elian Doran
72ff384187 refactor(call_to_action): clean up 2025-08-12 20:12:06 +03:00
Elian Doran
bac048f60f feat(call_to_action): allow dismissal 2025-08-12 19:37:32 +03:00
Elian Doran
d8d0a64134 Translations update from Hosted Weblate (#6624) 2025-08-12 19:15:08 +03:00
Elian Doran
b2db87db4e chore(call-to-action): add IDs for each call to action 2025-08-12 19:05:33 +03:00
Elian Doran
1baaee582e refactor(call-to-action): split into separate file & add translations 2025-08-12 18:42:55 +03:00
nejcmenard
9212b72351 Translated using Weblate (Slovenian)
Currently translated at 1.3% (5 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/sl/
2025-08-12 17:37:58 +02:00
nejcmenard
24af820477 Translated using Weblate (Slovenian)
Currently translated at 0.3% (5 of 1544 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/sl/
2025-08-12 17:37:58 +02:00
acwr47
6c4c2d22c6 Translated using Weblate (Japanese)
Currently translated at 15.3% (58 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-12 17:37:58 +02:00
acwr47
2e9b20be71 Translated using Weblate (Japanese)
Currently translated at 8.4% (130 of 1544 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-12 17:37:58 +02:00
Francis C
3216de5d89 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hant/
2025-08-12 17:37:57 +02:00
Francis C
4bf7cb8099 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 75.5% (1166 of 1544 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-12 17:37:57 +02:00
Elian Doran
4871dbd7ef feat(call_to_action): add actual functionality on action buttons 2025-08-12 17:48:44 +03:00
Elian Doran
e125809fe0 fix(call_to_action): error if no items 2025-08-12 17:42:18 +03:00
Elian Doran
27b80b573f feat(call_to_action): filter call to actions 2025-08-12 17:05:52 +03:00
Elian Doran
38d6ae87b6 feat(call-to-action): add support for multiple actions 2025-08-12 16:35:27 +03:00
Elian Doran
1a7cbc13e0 feat(call-to-action): basic dialog 2025-08-12 15:57:29 +03:00
Elian Doran
8e4691d4a4 fix(calendar): missing locale for Russian 2025-08-12 14:58:39 +03:00
Elian Doran
b371337ed2 Translations update from Hosted Weblate (#6623) 2025-08-12 10:55:28 +03:00
nejcmenard
4d4b76ce39 Added translation using Weblate (Slovenian) 2025-08-12 09:46:43 +02:00
nejcmenard
289d3e9882 Added translation using Weblate (Slovenian) 2025-08-12 09:46:42 +02:00
acwr47
a57eb8f27f Translated using Weblate (Japanese)
Currently translated at 1.4% (22 of 1544 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-12 09:46:42 +02:00
Elian Doran
27023f1fd5 only run nightly.yml on TriliumNext/Trilium (#6620) 2025-08-12 09:27:14 +03:00
Elian Doran
20a152993f feat(): Update codemirror-themes and add new Cobalt2 theme (#6621) 2025-08-12 09:26:14 +03:00
Elian Doran
ba7636db75 Translations update from Hosted Weblate (#6622) 2025-08-12 09:08:55 +03:00
Flowerlywind
e3e51a2e1f Translated using Weblate (Vietnamese)
Currently translated at 1.8% (29 of 1544 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/vi/
2025-08-12 07:46:18 +02:00
hulmgulm
93b601fe98 Translated using Weblate (German)
Currently translated at 80.4% (1242 of 1544 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-12 07:46:18 +02:00
hulmgulm
203ebb0e7a Update codemirror-themes and add new Cobalt2 theme 2025-08-12 07:09:14 +02:00
matt wilkie
041c2e5693 Merge branch 'main' into mhw-nightly 2025-08-11 21:11:17 -07:00
matt wilkie
258c0d511e only run nightly.yml on TriliumNext/Trilium
stops nightly CI/CD errors on forks without having to disable CI/CD entirely
2025-08-11 21:07:15 -07:00
Elian Doran
15705553c7 Adds a get/set to bNote to allow getting an Attribute by it's Id, or … (#6596) 2025-08-11 22:43:11 +03:00
Elian Doran
27f023e399 chore(deps): update dependency typedoc to v0.28.10 (#6611) 2025-08-11 22:31:27 +03:00
Elian Doran
0d2242171c fix(deps): update dependency i18next to v25.3.4 (#6612) 2025-08-11 22:30:56 +03:00
Elian Doran
0c62ecda65 Translations update from Hosted Weblate (#6619) 2025-08-11 22:30:32 +03:00
wild
7cd7fec93b Translated using Weblate (Serbian)
Currently translated at 27.8% (435 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/sr/
2025-08-11 19:28:12 +00:00
infaktor
cfab5e6217 Translated using Weblate (German)
Currently translated at 80.8% (1261 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-11 19:28:11 +00:00
Grant Zhu
0c313e8b8f Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1560 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-11 19:28:10 +00:00
Grip
61366061e6 Translated using Weblate (Italian)
Currently translated at 35.1% (133 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/it/
2025-08-11 19:28:09 +00:00
Grip
b2c0685c09 Translated using Weblate (Italian)
Currently translated at 13.9% (218 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/it/
2025-08-11 19:28:09 +00:00
Elian Doran
e3ee284e91 fix(deps): update fullcalendar monorepo to v6.1.19 (#6613) 2025-08-11 22:27:58 +03:00
Elian Doran
58901855af React modals (#6544) 2025-08-11 22:26:14 +03:00
Elian Doran
c7f8a49c47 docs(readme): wrong link to the latest release 2025-08-11 15:09:26 +03:00
renovate[bot]
10685fe183 fix(deps): update fullcalendar monorepo to v6.1.19 2025-08-11 02:03:23 +00:00
renovate[bot]
9e514fe95e fix(deps): update dependency i18next to v25.3.4 2025-08-11 02:02:51 +00:00
renovate[bot]
f28a319e26 chore(deps): update dependency typedoc to v0.28.10 2025-08-11 02:02:15 +00:00
perf3ct
decfb58142 feat(logs): cleanup physical log files after 90 days by default
asdf
2025-08-11 00:50:40 +00:00
perf3ct
415bbc3b0a feat(docs): also update documentation for search updates 2025-08-10 23:16:37 +00:00
perf3ct
5b669ca287 feat(search): also implement defensive checks for undefined notes 2025-08-10 21:59:20 +00:00
Elian Doran
1700217241 fix(react/dialogs): broken test in cheatsheet 2025-08-10 23:37:05 +03:00
Elian Doran
f00c0d5d73 chore(deps): update dependency @anthropic-ai/sdk to v0.59.0 (#6594) 2025-08-10 23:11:36 +03:00
Elian Doran
64ce0d2911 Translations update from Hosted Weblate (#6603) 2025-08-10 23:01:39 +03:00
Elian Doran
82dce7a0d3 fix(react/dialogs): restore jump to note text 2025-08-10 22:41:20 +03:00
Elian Doran
b94f67aa72 fix(react/dialogs): entering command palette 2025-08-10 21:53:21 +03:00
Elian Doran
1ff77a1464 fix(react/dialogs): jump to note not supporting spaces 2025-08-10 21:07:41 +03:00
Elian Doran
adb0e1e844 chore(react/dialogs): remove unnecessary close translation 2025-08-10 20:39:41 +03:00
Elian Doran
2043a06a48 chore(react/dialogs): remove empty translations 2025-08-10 20:32:08 +03:00
Elian Doran
738ebb66ac Merge remote-tracking branch 'origin/main' into react/modals 2025-08-10 20:28:51 +03:00
Elian Doran
abf1f6c041 fix(react/dialogs): revision list not full height 2025-08-10 20:24:20 +03:00
Flowerlywind
7db0e90506 Translated using Weblate (Vietnamese)
Currently translated at 0.5% (2 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/vi/
2025-08-10 19:07:49 +02:00
Flowerlywind
400f9cf911 Translated using Weblate (Vietnamese)
Currently translated at 1.0% (17 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/vi/
2025-08-10 19:07:48 +02:00
Elian Doran
2d3b99c959 fix(react/dialogs): restore focus after modal is dismissed 2025-08-10 20:06:05 +03:00
Elian Doran
fd1ea05c78 fix(react/dialogs): add link default text not working 2025-08-10 19:51:56 +03:00
Elian Doran
d7c4b8f530 fix(react/dialogs): undelete note not working 2025-08-10 19:19:48 +03:00
Elian Doran
238f358d6a fix(react/dialogs): bring back keyboard navigation for note list 2025-08-10 18:56:37 +03:00
Elian Doran
50afdca150 fix(react/dialogs): autocomplete not reacting to clear 2025-08-10 18:19:39 +03:00
Elian Doran
b5555d94f5 fix(react/dialogs): note autocomplete not restoring note 2025-08-10 18:01:36 +03:00
Elian Doran
3d81633214 fix(react/dialogs): add back selection in revisions list 2025-08-10 17:53:45 +03:00
Elian Doran
5db7997a17 fix(react/dialogs): revisions not refreshing after deleting an item 2025-08-10 17:48:17 +03:00
Elian Doran
71dd428919 fix(react/dialogs): conform dialog not reporting properly 2025-08-10 17:41:31 +03:00
Elian Doran
a20d66a6b5 fix(react/dialogs): some dialogs are not displayed on top 2025-08-10 17:37:48 +03:00
Elian Doran
3caefa5409 refactor(react): use memoization where appropriate 2025-08-10 17:19:39 +03:00
Elian Doran
a6e56be55a refactor(react): move effects outside conditional 2025-08-10 17:15:38 +03:00
Flowerlywind
6d582f09be Added translation using Weblate (Vietnamese) 2025-08-10 15:58:52 +02:00
Flowerlywind
40cd46cd09 Added translation using Weblate (Vietnamese) 2025-08-10 15:58:51 +02:00
renovate[bot]
3cc59149cf chore(deps): update dependency @anthropic-ai/sdk to v0.59.0 2025-08-10 12:44:22 +00:00
Elian Doran
e659266d62 refactor(react): remove use of any 2025-08-10 15:28:52 +03:00
Elian Doran
14e09f5ea0 refactor(react): normalize imports 2025-08-10 15:21:49 +03:00
Elian Doran
11f6462a31 fix(react/dialogs): events triggering even when modal is hidden 2025-08-10 15:11:43 +03:00
Elian Doran
48eebbe2fe refactor(react/dialogs): don't render modals unless they are actually shown 2025-08-10 14:55:41 +03:00
Elian Doran
f7093c035b refactor(react/dialogs): add documentation & enforce some properties 2025-08-10 14:49:58 +03:00
Elian Doran
b25e9cdee6 fix(react/dialogs): delete notes not properly reporting state 2025-08-10 14:46:40 +03:00
Elian Doran
5cd7e4707a fix(react/dialogs): dollar signs in help tooltips 2025-08-10 14:07:54 +03:00
Elian Doran
861374bb87 fix(react/dialogs): bulk actions not working in search notes 2025-08-10 14:06:14 +03:00
Elian Doran
d3519b3059 refactor(react/dialogs): solve client errors 2025-08-10 13:02:17 +03:00
Elian Doran
da1f18c60f refactor(react/dialogs): integrate proper closing of modal 2025-08-10 12:22:11 +03:00
Elian Doran
b7482f2a6a refactor(react/dialogs): use shown everywhere 2025-08-10 11:38:12 +03:00
Elian Doran
fd616cafca fix(deps): update eslint monorepo to v9.33.0 (#6595) 2025-08-10 09:22:13 +03:00
Elian Doran
b262a5181f fix(deps): update dependency mind-elixir to v5.0.5 (#6598) 2025-08-10 09:21:52 +03:00
Elian Doran
adb54a9054 fix(deps): update dependency eslint-linter-browserify to v9.33.0 (#6599) 2025-08-10 09:21:21 +03:00
Elian Doran
5eb05f5550 Translations update from Hosted Weblate (#6600) 2025-08-10 09:20:37 +03:00
perf3ct
2950c5eaa4 feat(etapi): also save note revision via etapi if needed too
asdf
2025-08-10 05:06:48 +00:00
infaktor
16fd67c070 Translated using Weblate (German)
Currently translated at 69.0% (261 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-10 05:38:12 +02:00
Marcelo Nolasco
9e3559f97c Translated using Weblate (Portuguese (Brazil))
Currently translated at 23.2% (363 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-10 05:38:12 +02:00
infaktor
83eea30ea0 Translated using Weblate (German)
Currently translated at 80.8% (1261 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-10 05:38:11 +02:00
renovate[bot]
6ceccf1c7a fix(deps): update dependency eslint-linter-browserify to v9.33.0 2025-08-10 01:33:44 +00:00
renovate[bot]
31e1c4c712 fix(deps): update dependency mind-elixir to v5.0.5 2025-08-10 01:32:47 +00:00
Elian Doran
fa97ec6c72 refactor(react/dialogs): integrate self-triggering modal in more dialogs 2025-08-10 00:32:26 +03:00
Elian Doran
cd5467bf5c refactor(react/bulk_actions): set up way to self-enable modal 2025-08-09 23:12:27 +03:00
Elian Doran
899f85f4e7 chore(react/bulk_actions): clean up 2025-08-09 20:37:45 +03:00
Elian Doran
7c79fbefa6 feat(react/bulk_actions): port execute script 2025-08-09 20:34:33 +03:00
Elian Doran
18c6fe7ebd feat(react/bulk_actions): port update relation target 2025-08-09 20:26:21 +03:00
Elian Doran
6f6643d758 feat(react/bulk_actions): port rename relation 2025-08-09 20:09:27 +03:00
Elian Doran
356adebbce feat(react/bulk_actions): improve delete relation 2025-08-09 20:01:48 +03:00
Elian Doran
5c8e4fd6fd feat(react/bulk_actions): improve note auto complete handling 2025-08-09 19:49:32 +03:00
Elian Doran
5be9bb47a7 feat(react/bulk_actions): port add relation 2025-08-09 19:41:49 +03:00
Elian Doran
60c5dc525b feat(react/bulk_actions): port rename note 2025-08-09 19:30:35 +03:00
Elian Doran
abfffcec07 feat(react/bulk_actions): clear autocomplete selection 2025-08-09 19:10:40 +03:00
Elian Doran
09b12052f0 feat(react/bulk_actions): port move note 2025-08-09 18:45:39 +03:00
Elian Doran
78bb0ab016 Added zen mode to mobile layout (#6584) 2025-08-09 18:00:03 +03:00
Papierkorb2292
4cd4c2f607 Apply suggestions from code review
Use `mobile` class on body to determine the style of `CloseZenButton` instead of an extra class

Co-authored-by: Elian Doran <contact@eliandoran.me>
2025-08-09 16:31:56 +02:00
Elian Doran
f95b5d6f14 feat(react/bulk_actions): port delete revisions 2025-08-09 16:55:51 +03:00
Elian Doran
4a53be1e33 feat(react/bulk_actions): port delete note 2025-08-09 16:47:05 +03:00
Elian Doran
cbbe845d7b fix: remove unnecessary idea directory (#6554) 2025-08-09 13:37:41 +03:00
Elian Doran
b2b52e92a4 Added Simple Update/Autoupdate Script (#6568) 2025-08-09 13:35:39 +03:00
renovate[bot]
15a97a4675 fix(deps): update eslint monorepo to v9.33.0 2025-08-09 10:31:41 +00:00
Elian Doran
a01f25ec12 chore(deps): update dependency @sveltejs/vite-plugin-svelte to v6.1.1 (#6590) 2025-08-09 13:31:16 +03:00
Elian Doran
2f175765ec chore(deps): update dependency lint-staged to v16.1.5 (#6592) 2025-08-09 13:31:00 +03:00
Elian Doran
6a7ae72b1b chore(deps): update dependency tmp to v0.2.5 (#6593) 2025-08-09 13:30:31 +03:00
Elian Doran
e396bb1641 chore(deps): update dependency @types/node to v22.17.1 (#6591) 2025-08-09 13:30:10 +03:00
Elian Doran
baedac4746 chore(deps): update dependency @stylistic/eslint-plugin to v5.2.3 (#6589) 2025-08-09 13:28:05 +03:00
Elian Doran
268ef626ca chore(deps): update dependency @eslint/compat to v1.3.2 (#6588) 2025-08-09 13:27:37 +03:00
Elian Doran
40c7ad4b46 fix(react/bulk_actions): delete button not working 2025-08-09 10:12:26 +03:00
Elian Doran
54f9ce87f9 feat(react/bulk_actions): port delete/rename label 2025-08-09 10:04:58 +03:00
Elian Doran
12b8a70e5c feat(react/bulk_actions): port update label value 2025-08-09 09:28:48 +03:00
Elian Doran
acf204d0e3 fix(react/bulk_actions): spaced update triggering too fast 2025-08-09 09:15:54 +03:00
Geekswordsman
ee19f9ccaa Adds a get/set to bNote to allow getting an Attribute by it's Id, or setting an Attribute's value by it's Id 2025-08-08 21:47:20 -04:00
renovate[bot]
34c0cf33b9 chore(deps): update dependency tmp to v0.2.5 2025-08-09 00:41:34 +00:00
renovate[bot]
34ec624e46 chore(deps): update dependency lint-staged to v16.1.5 2025-08-09 00:40:50 +00:00
renovate[bot]
056d3f9f36 chore(deps): update dependency @types/node to v22.17.1 2025-08-09 00:40:21 +00:00
renovate[bot]
040673af0b chore(deps): update dependency @sveltejs/vite-plugin-svelte to v6.1.1 2025-08-09 00:39:45 +00:00
renovate[bot]
48fb0c5e21 chore(deps): update dependency @stylistic/eslint-plugin to v5.2.3 2025-08-09 00:39:01 +00:00
renovate[bot]
4c1a55708f chore(deps): update dependency @eslint/compat to v1.3.2 2025-08-09 00:38:56 +00:00
Elian Doran
6e1951b356 feat(react/bulk_actions): port add_label 2025-08-08 23:23:07 +03:00
Elian Doran
3dd6b05d2e fix(react/dialog): react to adding new bulk actions 2025-08-08 22:01:30 +03:00
Elian Doran
05f1ae01f3 Merge remote-tracking branch 'origin/main' into react/modals 2025-08-08 21:37:13 +03:00
Elian Doran
3975041798 feat(react): set up hook for reacting to events 2025-08-08 20:08:06 +03:00
serossi
3a29d65777 Merge branch 'TriliumNext:main' into patch-1 2025-08-08 15:29:44 +02:00
Elian Doran
eeeecb3988 chore(deps): update dependency electron to v37.2.6 (#6573) 2025-08-08 13:18:24 +03:00
Elian Doran
28ababcbb9 chore(deps): update dependency vite to v7.1.1 (#6583) 2025-08-08 13:18:17 +03:00
Elian Doran
f382943af3 Translations update from Hosted Weblate (#6585) 2025-08-08 13:17:42 +03:00
renovate[bot]
fa38332a6c chore(deps): update dependency vite to v7.1.1 2025-08-08 09:46:23 +00:00
renovate[bot]
5a58fcde96 chore(deps): update dependency electron to v37.2.6 2025-08-08 09:45:02 +00:00
Doğukan Çağatay
62d048433b Translated using Weblate (Turkish)
Currently translated at 1.6% (26 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/tr/
2025-08-08 09:42:50 +00:00
Doğukan Çağatay
db4ba53449 Added translation using Weblate (Turkish) 2025-08-08 09:42:50 +00:00
Doğukan Çağatay
da20916767 Added translation using Weblate (Turkish) 2025-08-08 09:42:49 +00:00
Marcelo Nolasco
b1e12182ce Translated using Weblate (Portuguese (Brazil))
Currently translated at 22.3% (348 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-08 09:42:48 +00:00
Elian Doran
80b2061935 chore(deps): update dependency stylelint to v16.23.1 (#6582) 2025-08-08 12:42:41 +03:00
Elian Doran
8ce92f8c93 chore(deps): update dependency ollama to v0.5.17 (#6580) 2025-08-08 12:42:32 +03:00
Elian Doran
05cd8cb547 chore(deps): update svelte monorepo (#6575) 2025-08-08 12:42:23 +03:00
Elian Doran
6e7d0bc51b chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.3 (#6574) 2025-08-08 12:42:15 +03:00
renovate[bot]
b9aede23e6 chore(deps): update svelte monorepo 2025-08-08 09:42:12 +00:00
renovate[bot]
1d52988826 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.3 2025-08-08 09:41:29 +00:00
Elian Doran
ebe29f41f9 chore(deps): update dependency tmp to v0.2.4 [security] (#6572) 2025-08-08 12:40:01 +03:00
Elian Doran
598591a2da chore(deps): update electron-forge monorepo to v7.8.3 (#6564) 2025-08-08 12:39:06 +03:00
Elian Doran
32c2860b68 chore(deps): update dependency lint-staged to v16.1.4 (#6550) 2025-08-08 12:38:43 +03:00
Papierkorb2292
d975790e79 Added zen mode for mobile layout (useful on tablets) 2025-08-08 09:15:01 +02:00
renovate[bot]
3e1f74ae93 chore(deps): update electron-forge monorepo to v7.8.3 2025-08-08 06:51:40 +00:00
renovate[bot]
81a8908b98 chore(deps): update dependency stylelint to v16.23.1 2025-08-08 06:51:11 +00:00
renovate[bot]
892dfe2340 chore(deps): update dependency ollama to v0.5.17 2025-08-08 06:49:34 +00:00
renovate[bot]
fd175eb8a8 chore(deps): update dependency lint-staged to v16.1.4 2025-08-08 06:49:02 +00:00
renovate[bot]
c98f6d96d5 chore(deps): update dependency tmp to v0.2.4 [security] 2025-08-08 06:47:58 +00:00
serossi
4d28df7a89 Merge branch 'main' into patch-1 2025-08-07 23:31:59 +02:00
Elian Doran
bd6c690160 chore(react/dialog): improve recent changes 2025-08-07 22:31:51 +03:00
Elian Doran
c0d7278827 refactor(react/dialogs): deduplicate raw HTML component 2025-08-07 22:00:37 +03:00
Elian Doran
f9eb0a20f7 feat(react/dialogs): port bulk actions 2025-08-07 21:58:47 +03:00
Elian Doran
8d27a5aa39 feat(react/dialogs): port import 2025-08-07 19:20:35 +03:00
Elian Doran
90f9416524 feat(react/modals): port export dialog 2025-08-07 18:52:39 +03:00
Elian Doran
a593ce7c40 fix(react/dialog): delete note not working properly 2025-08-06 23:11:53 +03:00
Elian Doran
31fbf2cb57 fix(react/dialogs): port recent_changes 2025-08-06 23:11:31 +03:00
Elian Doran
c0d3027e65 fix(react/dialogs): unnecessary listeners on modal render 2025-08-06 20:54:29 +03:00
Elian Doran
bde270b73f fix(react/dialogs): some type errors 2025-08-06 20:29:19 +03:00
Elian Doran
edd18b53d0 refactor(react/dialogs): solve some type errors 2025-08-06 18:10:02 +03:00
Elian Doran
2ad4b26c9e chore(react/dialogs): reintroduce footer in note revisions 2025-08-06 18:01:26 +03:00
serossi
f39a5c55ba Update Packaged version for Linux.md
Updated the Script for Version check
2025-08-06 16:30:39 +02:00
Elian Doran
0af5feab79 refactor(react/dialogs): deduplicate data types 2025-08-06 17:11:01 +03:00
serossi
68dd54a100 Update Packaged version for Linux.md
Added Autoupdate Script for Server from Github releases
2025-08-06 16:07:27 +02:00
Elian Doran
7a0f148d28 chore(react/dialogs): add back content buttons to note revision 2025-08-06 17:01:32 +03:00
Elian Doran
958b1592f8 chore(react/dialogs): add back rendering in revisions 2025-08-06 16:53:58 +03:00
Elian Doran
7ac0828ae7 feat(react/dialogs): port note revisions 2025-08-06 16:16:30 +03:00
Elian Doran
f7e7b38551 chore(react/dialogs): add back badges for choose note type 2025-08-06 12:13:38 +03:00
Elian Doran
33e3112290 feat(react/dialog): port note_type_chooser 2025-08-06 12:08:17 +03:00
Elian Doran
2a40d6bb7e feat(react/dialogs): port upload attachments 2025-08-05 23:03:38 +03:00
Elian Doran
f196a78728 feat(react/checkbox): use bootstrap tooltip 2025-08-05 22:34:45 +03:00
Elian Doran
523c7ac273 chore(react/dialogs): improve display of description 2025-08-05 22:11:16 +03:00
Elian Doran
ce324586f8 fix(react/dialogs): missing autocomplete opts 2025-08-05 21:15:02 +03:00
Elian Doran
35bd210062 fix(react/dialogs): recent notes not triggered in autocomplete 2025-08-05 21:15:02 +03:00
Elian Doran
0cfe3351bb feat(react/dialogs): port include_note 2025-08-05 21:15:02 +03:00
Elian Doran
7202f47716 feat(react/dialogs): port cheatsheet 2025-08-05 21:15:02 +03:00
Elian Doran
bde4545afc feat(react/dialogs): port prompt 2025-08-05 21:15:02 +03:00
Elian Doran
b3c81ce5f2 feat(react/dialogs): port password_not_set 2025-08-05 21:15:02 +03:00
Elian Doran
02b0d1fb5e refactor(react/dialogs): separate alert component 2025-08-05 21:15:02 +03:00
Elian Doran
87d9ea06f3 feat(react/dialogs): port delete_notes 2025-08-05 21:15:02 +03:00
Adorian Doran
a4e6a964c9 feat(react/dialogs): fix broken class name 2025-08-05 20:00:39 +03:00
Elian Doran
79c5d479fc feat(react/dialogs): port incorrect_cpu_arch 2025-08-05 15:39:49 +03:00
Elian Doran
8f0a9f91c1 feat(react/dialogs): port the rest of confirm 2025-08-05 15:13:09 +03:00
Elian Doran
93fae9cc8c feat(react/dialogs): port confirm dialog partially 2025-08-05 14:25:21 +03:00
Elian Doran
134c869b07 feat(react/dialog): port protected session password 2025-08-04 23:22:45 +03:00
Elian Doran
beb0487513 feat(react): port move to 2025-08-04 22:37:31 +03:00
Elian Doran
aa9ffb8f6b feat(react/dialogs): port clone_to 2025-08-04 21:17:35 +03:00
Elian Doran
18eb704b81 feat(react/widgets): set up form group 2025-08-04 20:41:25 +03:00
Elian Doran
83fb62d4df fix(react/dialogs): listener leak in modal 2025-08-04 19:54:59 +03:00
Elian Doran
cb650b70cb fix(react/dialogs): autocomplete not displayed if list is empty 2025-08-04 19:27:53 +03:00
Elian Doran
d5e42318dd feat(dialogs): port jump to note partially 2025-08-04 18:54:56 +03:00
grantzhu
24ed474c8c fix: remove unnecessary idea directory, this will affect other developers using jetbrains ide from opening this project 2025-08-04 21:24:38 +08:00
Elian Doran
a9c25b4edd chore(react): bring back focus to add_link 2025-08-04 16:05:04 +03:00
Elian Doran
c89737ae7b feat(vscode): integrate i18n with react 2025-08-04 12:59:13 +03:00
Elian Doran
e619a6ef7c feat(react): port add_link 2025-08-04 12:58:42 +03:00
perf3ct
583ab8dc92 feat(quick_search): make sure that we rank exact matches higher when merging results with fuzzy search 2025-08-03 21:29:18 +00:00
perf3ct
db1619af31 feat(quick_search): change the results to use the warning accent color? 2025-08-03 21:22:41 +00:00
perf3ct
22740a6c8d feat(quick_search): add tests for updated fuzzy search progressive fuzzy search functionality 2025-08-03 21:02:56 +00:00
perf3ct
e9409577db feat(quick_search): only "fallback" to fuzzy search, if there aren't that many search results found from user's query 2025-08-03 20:43:16 +00:00
perf3ct
9cef8c8e70 feat(quick_search): try using another color so that matches stand out more? might change back 2025-08-03 20:42:52 +00:00
perf3ct
53bcec602d feat(quick_search): result titles are now aligned, inline with result text 2025-08-03 20:22:30 +00:00
Elian Doran
a62f12b427 feat(react): port info modal 2025-08-03 23:20:32 +03:00
perf3ct
e20816a7ce feat(quick_search): getting closer to how we want the quick search results to look with the spacing... 2025-08-03 19:49:43 +00:00
perf3ct
58535df676 feat(quick_search): within the quick search, allow for "infinite" scrolling of results 2025-08-03 19:49:43 +00:00
perf3ct
057040af06 feat(quick_search): limit the size of the Notes to search through, to 2MB 2025-08-03 19:49:43 +00:00
perf3ct
c603783a44 feat(quick_search): show the "matched" text in the search results, even if "edit distance" (misspelling) occurs 2025-08-03 19:49:43 +00:00
perf3ct
1928356ad5 feat(quick_search): edit distance searching in quick search works 2025-08-03 19:49:43 +00:00
Elian Doran
e53ad2c62a chore(react): reintroduce max width 2025-08-03 21:39:25 +03:00
Elian Doran
bca397e3e4 feat(react): port sort child notes 2025-08-03 21:18:18 +03:00
Elian Doran
164feaa3ec fix(react): button not working as submit 2025-08-03 20:01:54 +03:00
Elian Doran
4d09fabad8 feat(react): slightly faster about 2025-08-03 20:00:30 +03:00
Elian Doran
04913394c6 chore(react): clean up 2025-08-03 19:50:39 +03:00
Elian Doran
f8b563704f feat(react): add hlep page to branch prefix 2025-08-03 19:48:44 +03:00
Elian Doran
5d9bd0f6d3 feat(react): port branch prefix 2025-08-03 19:44:15 +03:00
Elian Doran
1229c26098 chore(react): add back Ctrl+Enter for markdown import 2025-08-03 19:06:21 +03:00
Elian Doran
77818d5453 feat(react): port markdown_import partially 2025-08-03 18:06:06 +03:00
perfectra1n
2d358342c5 feat(client): try to stylize the quick search even further in the client 2025-08-02 23:44:51 -07:00
perf3ct
6c79be881d feat(search): allow for search through very large notes 2025-08-03 01:44:55 +00:00
perf3ct
51a8937c64 feat(quick_search): also try to show the "matched" text in quick search results and not just note titles 2025-08-03 00:29:21 +00:00
perf3ct
c436455b32 feat(tests): implement tests for updated fuzzy search operators, and text_utils used in search 2025-08-03 00:16:47 +00:00
perf3ct
18f89b979d feat(search): normalize search text (fonts, etc.) 2025-08-02 23:59:45 +00:00
perf3ct
8094259c78 feat(search): implement edit_distances (misspelling tolerances) into fulltext search 2025-08-02 23:59:21 +00:00
perf3ct
b4f503b81e feat(search): implement additional operators (with bounds) for search comparison 2025-08-02 23:59:11 +00:00
perf3ct
4db04519bd feat(search): implement additional weights for search_results, normalize text as well 2025-08-02 23:56:23 +00:00
260 changed files with 22557 additions and 17077 deletions

View File

@@ -162,3 +162,25 @@ runs:
echo "Found ZIP: $zip_file"
echo "Note: ZIP files are not code signed, but their contents should be"
fi
- name: Sign the RPM
if: inputs.os == 'linux'
shell: ${{ inputs.shell }}
run: |
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import
# Import the key into RPM for verification
gpg --export -a > pubkey
rpm --import pubkey
rm pubkey
# Sign the RPM
rpm_file=$(find ./apps/desktop/upload -name "*.rpm" -print -quit)
rpmsign --define "_gpg_name Trilium Notes Signing Key <triliumnotes@outlook.com>" --addsign "$rpm_file"
rpm -Kv "$rpm_file"
# Validate code signing
if ! rpm -K "$rpm_file" | grep -q "digests signatures OK"; then
echo .rpm file not signed
exit 1
fi

View File

@@ -44,7 +44,7 @@ runs:
steps:
# Checkout branch to compare to [required]
- name: Checkout base branch
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{ inputs.branch }}
path: br-base

View File

@@ -57,7 +57,7 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # needed for https://github.com/marketplace/actions/nx-set-shas
@@ -48,7 +48,7 @@ jobs:
- check-affected
steps:
- name: Checkout the repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
@@ -68,7 +68,7 @@ jobs:
- test_dev
- check-affected
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -103,7 +103,7 @@ jobs:
- dockerfile: Dockerfile
steps:
- name: Checkout the repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Install dependencies

View File

@@ -32,7 +32,7 @@ jobs:
- dockerfile: Dockerfile
steps:
- name: Checkout the repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Set IMAGE_NAME to lowercase
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
@@ -141,7 +141,7 @@ jobs:
run: echo "TEST_TAG=${TEST_TAG,,}" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4

View File

@@ -27,6 +27,7 @@ permissions:
jobs:
nightly-electron:
if: github.repository == 'TriliumNext/Trilium'
name: Deploy nightly
strategy:
fail-fast: false
@@ -47,7 +48,7 @@ jobs:
forge_platform: win32
runs-on: ${{ matrix.os.image }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
@@ -75,6 +76,7 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
- name: Publish release
uses: softprops/action-gh-release@v2.3.2
@@ -96,6 +98,7 @@ jobs:
path: apps/desktop/upload
nightly-server:
if: github.repository == 'TriliumNext/Trilium'
name: Deploy server nightly
strategy:
fail-fast: false
@@ -108,7 +111,7 @@ jobs:
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Run the build
uses: ./.github/actions/build-server

View File

@@ -14,7 +14,7 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
filter: tree:0
fetch-depth: 0

View File

@@ -32,7 +32,7 @@ jobs:
forge_platform: win32
runs-on: ${{ matrix.os.image }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
@@ -58,6 +58,7 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
- name: Upload the artifact
uses: actions/upload-artifact@v4
@@ -78,7 +79,7 @@ jobs:
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Run the build
uses: ./.github/actions/build-server
@@ -101,7 +102,7 @@ jobs:
steps:
- run: mkdir upload
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
sparse-checkout: |
docs/Release Notes

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@ node_modules
# IDEs and editors
/.idea
.idea
.project
.classpath
.c9/

6
.idea/.gitignore generated vendored
View File

@@ -1,6 +0,0 @@
# Default ignored files
/workspace.xml
# Datasource local storage ignored files
/dataSources.local.xml
/dataSources/

View File

@@ -1,15 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</value>
</option>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

12
.idea/dataSources.xml generated
View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="document.db" uuid="2a4ac1e6-b828-4a2a-8e4a-3f59f10aff26">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/data/document.db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

4
.idea/encodings.xml generated
View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>

View File

@@ -1,11 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JSLintConfiguration">
<option devel="true" />
<option es6="true" />
<option maxerr="50" />
<option node="true" />
</component>
</project>

8
.idea/misc.xml generated
View File

@@ -1,8 +0,0 @@
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="openjdk-16" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/trilium.iml" filepath="$PROJECT_DIR$/trilium.iml" />
</modules>
</component>
</project>

7
.idea/sqldialects.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$" dialect="SQLite" />
<file url="PROJECT" dialect="SQLite" />
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -3,6 +3,7 @@
languageIds:
- javascript
- typescript
- typescriptreact
- html
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
@@ -25,9 +26,10 @@ scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
# The "$1" will be replaced by the keypath specified.
refactorTemplates:
- t("$1")
- {t("$1")}
- ${t("$1")}
- <%= t("$1") %>
# If set to true, only enables this custom framework (will disable all built-in frameworks)
monopoly: true
monopoly: true

View File

@@ -1,11 +1,11 @@
# Trilium Notes
![GitHub Sponsors](https://img.shields.io/github/sponsors/eliandoran) ![LiberaPay patrons](https://img.shields.io/liberapay/patrons/ElianDoran)
![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total)
![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/trilium)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/trilium/total)
[![RelativeCI](https://badges.relative-ci.com/badges/Di5q7dz9daNDZ9UXi0Bp?branch=develop)](https://app.relative-ci.com/projects/Di5q7dz9daNDZ9UXi0Bp) [![Translation status](https://hosted.weblate.org/widget/trilium/svg-badge.svg)](https://hosted.weblate.org/engage/trilium/)
[English](./README.md) | [Chinese](./docs/README-ZH_CN.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
[English](./README.md) | [Chinese (Simplified)](./docs/README-ZH_CN.md) | [Chinese (Traditional)](./docs/README-ZH_TW.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
Trilium Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
@@ -46,15 +46,15 @@ See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for q
- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
## ⚠️ Why TriliumNext?
## Why TriliumNext?
[The original Trilium project is in maintenance mode](https://github.com/zadam/trilium/issues/4620).
The original Trilium developer ([Zadam](https://github.com/zadam)) has graciously given the Trilium repository to the community project which resides at https://github.com/TriliumNext
### Migrating from Trilium?
### ⬆️Migrating from Zadam/Trilium?
There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Notes instance. Simply [install TriliumNext/Notes](#-installation) as usual and it will use your existing database.
There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Trilium instance. Simply [install TriliumNext/Trilium](#-installation) as usual and it will use your existing database.
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Notes/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext have their sync versions incremented.
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Trilium/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext/Trilium have their sync versions incremented which prevents direct migration.
## 📖 Documentation
@@ -75,14 +75,14 @@ Feel free to join our official conversations. We would love to hear what feature
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions.)
- The `General` Matrix room is also bridged to [XMPP](xmpp:discuss@trilium.thisgreat.party?join)
- [Github Discussions](https://github.com/TriliumNext/Notes/discussions) (For asynchronous discussions.)
- [Github Issues](https://github.com/TriliumNext/Notes/issues) (For bug reports and feature requests.)
- [Github Discussions](https://github.com/TriliumNext/Trilium/discussions) (For asynchronous discussions.)
- [Github Issues](https://github.com/TriliumNext/Trilium/issues) (For bug reports and feature requests.)
## 🏗 Installation
### Windows / MacOS
Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable.
### Linux
@@ -90,7 +90,7 @@ If your distribution is listed in the table below, use your distribution's packa
[![Packaging status](https://repology.org/badge/vertical-allrepos/triliumnext.svg)](https://repology.org/project/triliumnext/versions)
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Trilium/releases/latest), unzip the package and run the `trilium` executable.
TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
@@ -104,13 +104,15 @@ Currently only the latest versions of Chrome & Firefox are supported (and tested
To use TriliumNext on a mobile device, you can use a mobile web browser to access the mobile interface of a server installation (see below).
If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid). Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid).
See issue https://github.com/TriliumNext/Trilium/issues/4962 for more information on mobile app support.
See issue https://github.com/TriliumNext/Notes/issues/72 for more information on mobile app support.
If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid).
Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid).
Note: It is best to disable automatic updates on your server installation (see below) when using TriliumDroid since the sync version must match between Trilium and TriliumDroid.
### Server
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/notes)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/trilium)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
## 💻 Contribute
@@ -152,11 +154,11 @@ pnpm install
pnpm nx --project=desktop electron-forge:make -- --arch=x64 --platform=win32
```
For more details, see the [development docs](https://github.com/TriliumNext/Notes/blob/develop/docs/Developer%20Guide/Developer%20Guide/Building%20and%20deployment/Running%20a%20development%20build.md).
For more details, see the [development docs](https://github.com/TriliumNext/Trilium/tree/main/docs/Developer%20Guide/Developer%20Guide).
### Developer Documentation
Please view the [documentation guide](./docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) for details. If you have more questions, feel free to reach out via the links described in the "Discuss with us" section above.
Please view the [documentation guide](https://github.com/TriliumNext/Trilium/blob/main/docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) for details. If you have more questions, feel free to reach out via the links described in the "Discuss with us" section above.
## 👏 Shoutouts
@@ -168,7 +170,7 @@ Please view the [documentation guide](./docs/Developer%20Guide/Developer%20Guide
## 🤝 Support
Support for the TriliumNext organization will be possible in the near future. For now, you can:
- Support continued development on TriliumNext by supporting our developers: [eliandoran](https://github.com/sponsors/eliandoran) (See the [repository insights]([developers]([url](https://github.com/TriliumNext/Notes/graphs/contributors))) for a full list)
- Support continued development on TriliumNext by supporting our developers: [eliandoran](https://github.com/sponsors/eliandoran) (See the [repository insights]([developers]([url](https://github.com/TriliumNext/trilium/graphs/contributors))) for a full list)
- Show a token of gratitude to the original Trilium developer ([zadam](https://github.com/sponsors/zadam)) via [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).

View File

@@ -36,12 +36,12 @@
},
"devDependencies": {
"@playwright/test": "1.54.2",
"@stylistic/eslint-plugin": "5.2.2",
"@stylistic/eslint-plugin": "5.2.3",
"@types/express": "5.0.3",
"@types/node": "22.17.0",
"@types/node": "22.17.2",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.32.0",
"eslint": "9.33.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25",
"jsdoc": "4.0.4",
@@ -49,8 +49,8 @@
"rcedit": "4.0.1",
"rimraf": "6.0.1",
"tslib": "2.8.1",
"typedoc": "0.28.9",
"typedoc-plugin-missing-exports": "4.0.0"
"typedoc": "0.28.10",
"typedoc-plugin-missing-exports": "4.1.0"
},
"optionalDependencies": {
"appdmg": "0.6.6"

View File

@@ -1,6 +1,6 @@
{
"name": "@triliumnext/client",
"version": "0.97.2",
"version": "0.98.0",
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
"private": true,
"license": "AGPL-3.0-only",
@@ -10,14 +10,14 @@
"url": "https://github.com/TriliumNext/Notes"
},
"dependencies": {
"@eslint/js": "9.32.0",
"@eslint/js": "9.33.0",
"@excalidraw/excalidraw": "0.18.0",
"@fullcalendar/core": "6.1.18",
"@fullcalendar/daygrid": "6.1.18",
"@fullcalendar/interaction": "6.1.18",
"@fullcalendar/list": "6.1.18",
"@fullcalendar/multimonth": "6.1.18",
"@fullcalendar/timegrid": "6.1.18",
"@fullcalendar/core": "6.1.19",
"@fullcalendar/daygrid": "6.1.19",
"@fullcalendar/interaction": "6.1.19",
"@fullcalendar/list": "6.1.19",
"@fullcalendar/multimonth": "6.1.19",
"@fullcalendar/timegrid": "6.1.19",
"@maplibre/maplibre-gl-leaflet": "0.1.3",
"@mermaid-js/layout-elk": "0.1.8",
"@mind-elixir/node-menu": "5.0.0",
@@ -36,7 +36,7 @@
"draggabilly": "3.0.0",
"force-graph": "1.50.1",
"globals": "16.3.0",
"i18next": "25.3.2",
"i18next": "25.3.6",
"i18next-http-backend": "3.0.2",
"jquery": "3.7.1",
"jquery.fancytree": "2.38.5",
@@ -48,7 +48,7 @@
"mark.js": "8.11.1",
"marked": "16.1.2",
"mermaid": "11.9.0",
"mind-elixir": "5.0.4",
"mind-elixir": "5.0.5",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.27.0",
@@ -65,8 +65,8 @@
"@types/leaflet": "1.9.20",
"@types/leaflet-gpx": "1.3.7",
"@types/mark.js": "8.11.12",
"@types/tabulator-tables": "6.2.9",
"copy-webpack-plugin": "13.0.0",
"@types/tabulator-tables": "6.2.10",
"copy-webpack-plugin": "13.0.1",
"happy-dom": "18.0.1",
"script-loader": "0.7.2",
"vite-plugin-static-copy": "3.1.1"

View File

@@ -30,6 +30,7 @@ import type CodeMirror from "@triliumnext/codemirror";
import { StartupChecks } from "./startup_checks.js";
import type { CreateNoteOpts } from "../services/note_create.js";
import { ColumnComponent } from "tabulator-tables";
import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx";
interface Layout {
getRootWidget: (appContext: AppContext) => RootWidget;
@@ -92,7 +93,9 @@ export type CommandMappings = {
closeTocCommand: CommandData;
closeHlt: CommandData;
showLaunchBarSubtree: CommandData;
showRevisions: CommandData;
showRevisions: CommandData & {
noteId?: string | null;
};
showLlmChat: CommandData;
createAiChat: CommandData;
showOptions: CommandData & {
@@ -368,6 +371,9 @@ export type CommandMappings = {
};
refreshTouchBar: CommandData;
reloadTextEditor: CommandData;
chooseNoteType: CommandData & {
callback: ChooseNoteTypeCallback
}
};
type EventMappings = {

View File

@@ -30,6 +30,7 @@ import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolb
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
import NoteDetailWidget from "../widgets/note_detail.js";
import NoteListWidget from "../widgets/note_list.js";
import { CallToActionDialog } from "../widgets/dialogs/call_to_action.jsx";
export function applyModals(rootContainer: RootContainer) {
rootContainer
@@ -66,4 +67,5 @@ export function applyModals(rootContainer: RootContainer) {
.child(new PromotedAttributesWidget())
.child(new NoteDetailWidget())
.child(new NoteListWidget(true)))
.child(new CallToActionDialog());
}

View File

@@ -26,6 +26,7 @@ import TabRowWidget from "../widgets/tab_row.js";
import RefreshButton from "../widgets/floating_buttons/refresh_button.js";
import MobileEditorToolbar from "../widgets/ribbon_widgets/mobile_editor_toolbar.js";
import { applyModals } from "./layout_commons.js";
import CloseZenButton from "../widgets/close_zen_button.js";
const MOBILE_CSS = `
<style>
@@ -174,7 +175,8 @@ export default class MobileLayout {
.id("mobile-bottom-bar")
.child(new TabRowWidget().css("height", "40px"))
.child(new FlexContainer("row").class("horizontal").css("height", "53px").child(new LauncherContainer(true)).child(new GlobalMenuWidget(true)).id("launcher-pane"))
);
)
.child(new CloseZenButton());
applyModals(rootContainer);
return rootContainer;
}

View File

@@ -38,8 +38,8 @@ export interface Suggestion {
commandShortcut?: string;
}
interface Options {
container?: HTMLElement;
export interface Options {
container?: HTMLElement | null;
fastSearch?: boolean;
allowCreatingNotes?: boolean;
allowJumpToSearchNotes?: boolean;
@@ -82,12 +82,12 @@ async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void
// Check if we're in command mode
if (options.isCommandPalette && term.startsWith(">")) {
const commandQuery = term.substring(1).trim();
// Get commands (all if no query, filtered if query provided)
const commands = commandQuery.length === 0
const commands = commandQuery.length === 0
? commandRegistry.getAllCommands()
: commandRegistry.searchCommands(commandQuery);
// Convert commands to suggestions
const commandSuggestions: Suggestion[] = commands.map(cmd => ({
action: "command",
@@ -99,7 +99,7 @@ async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void
commandShortcut: cmd.shortcut,
icon: cmd.icon
}));
cb(commandSuggestions);
return;
}
@@ -452,6 +452,21 @@ function init() {
};
}
/**
* Convenience function which triggers the display of recent notes in the autocomplete input and focuses it.
*
* @param inputElement - The input element to trigger recent notes on.
*/
export function triggerRecentNotes(inputElement: HTMLInputElement | null | undefined) {
if (!inputElement) {
return;
}
const $el = $(inputElement);
showRecentNotes($el);
$el.trigger("focus").trigger("select");
}
export default {
autocompleteSourceForCKEditor,
initNoteAutocomplete,

View File

@@ -109,8 +109,6 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
async function chooseNoteType() {
return new Promise<ChooseNoteTypeResponse>((res) => {
// TODO: Remove ignore after callback for chooseNoteType is defined in app_context.ts
//@ts-ignore
appContext.triggerCommand("chooseNoteType", { callback: res });
});
}

View File

@@ -320,8 +320,3 @@ h6 {
page-break-after: avoid;
break-after: avoid;
}
figure.table {
/* Workaround for https://github.com/ckeditor/ckeditor5/issues/18903. Remove once official fix is released */
display: table !important;
}

View File

@@ -642,6 +642,10 @@ table.promoted-attributes-in-tooltip th {
z-index: calc(var(--ck-z-panel) - 1) !important;
}
.tooltip.tooltip-top {
z-index: 32767 !important;
}
.tooltip-trigger {
background: transparent;
pointer-events: none;
@@ -1886,6 +1890,7 @@ body.zen #launcher-container,
body.zen #launcher-pane,
body.zen #left-pane,
body.zen #right-pane,
body.zen #mobile-sidebar-wrapper,
body.zen .tab-row-container,
body.zen .tab-row-widget,
body.zen .ribbon-container:not(:has(.classic-toolbar-widget.visible)),
@@ -1893,7 +1898,8 @@ body.zen .ribbon-container:has(.classic-toolbar-widget.visible) .ribbon-top-row,
body.zen .ribbon-container .ribbon-body:not(:has(.classic-toolbar-widget.visible)),
body.zen .note-icon-widget,
body.zen .title-row .button-widget,
body.zen .floating-buttons-children > *:not(.bx-edit-alt) {
body.zen .floating-buttons-children > *:not(.bx-edit-alt),
body.zen .action-button {
display: none !important;
}
@@ -1935,6 +1941,10 @@ body.zen .note-title-widget input {
background: transparent !important;
}
body.zen #detail-container {
width: 100%;
}
/* Content renderer */
footer.file-footer,
@@ -2245,6 +2255,13 @@ footer.webview-footer button {
padding: 1px 10px 1px 10px;
}
/* Search result highlighting */
.search-result-title b,
.search-result-content b {
font-weight: 900;
color: var(--admonition-warning-accent-color);
}
/* Customized icons */
.bx-tn-toc::before {

View File

@@ -233,16 +233,16 @@ div.tn-tool-dialog {
/* Item title link */
.recent-changes-content ul li .note-title a {
.recent-changes-content ul li a {
color: currentColor;
}
.recent-changes-content ul li .note-title a:hover {
.recent-changes-content ul li a:hover {
text-decoration: underline;
}
/* Item title for deleted notes */
.recent-changes-content ul li.deleted-note .note-title > .note-title {
.recent-changes-content ul li.deleted-note .note-title {
text-decoration: line-through;
}

View File

@@ -1,31 +1,23 @@
{
"about": {
"title": "Sobre Trilium Notes",
"close": "Tanca",
"homepage": "Pàgina principal:"
},
"add_link": {
"note": "Nota",
"close": "Tanca"
"note": "Nota"
},
"branch_prefix": {
"close": "Tanca",
"prefix": "Prefix: ",
"save": "Desa"
},
"bulk_actions": {
"close": "Tanca",
"labels": "Etiquetes",
"relations": "Relacions",
"notes": "Notes",
"other": "Altres"
},
"clone_to": {
"close": "Tanca"
},
"confirm": {
"confirmation": "Confirmació",
"close": "Tanca",
"cancel": "Cancel·la",
"ok": "OK"
},
@@ -39,53 +31,34 @@
"export": "Exporta"
},
"help": {
"close": "Tanca",
"troubleshooting": "Solució de problemes",
"other": "Altres"
},
"import": {
"close": "Tanca",
"options": "Opcions",
"import": "Importa"
},
"include_note": {
"close": "Tanca",
"label_note": "Nota"
},
"info": {
"closeButton": "Tanca",
"okButton": "OK"
},
"jump_to_note": {
"close": "Tanca"
},
"markdown_import": {
"close": "Tanca"
},
"move_to": {
"close": "Tanca"
},
"note_type_chooser": {
"close": "Tanca",
"templates": "Plantilles:"
},
"password_not_set": {
"close": "Tanca"
},
"prompt": {
"title": "Sol·licitud",
"close": "Tanca",
"defaultTitle": "Sol·licitud"
},
"protected_session_password": {
"close_label": "Tanca"
},
"recent_changes": {
"close": "Tanca",
"undelete_link": "recuperar"
},
"revisions": {
"close": "Tanca",
"restore_button": "Restaura",
"delete_button": "Suprimeix",
"download_button": "Descarrega",
@@ -93,14 +66,12 @@
"preview": "Vista prèvia:"
},
"sort_child_notes": {
"close": "Tanca",
"title": "títol",
"ascending": "ascendent",
"descending": "descendent",
"folders": "Carpetes"
},
"upload_attachments": {
"close": "Tanca",
"options": "Opcions",
"upload": "Puja"
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
{
"about": {
"title": "Über Trilium Notes",
"close": "Schließen",
"homepage": "Startseite:",
"app_version": "App-Version:",
"db_version": "DB-Version:",
@@ -28,25 +27,22 @@
"add_link": {
"add_link": "Link hinzufügen",
"help_on_links": "Hilfe zu Links",
"close": "Schließen",
"note": "Notiz",
"search_note": "Suche nach einer Notiz anhand ihres Namens",
"link_title_mirrors": "Der Linktitel spiegelt den aktuellen Titel der Notiz wider",
"link_title_arbitrary": "Der Linktitel kann beliebig geändert werden",
"link_title": "Linktitel",
"button_add_link": "Link hinzufügen <kbd>Eingabetaste</kbd>"
"button_add_link": "Link hinzufügen"
},
"branch_prefix": {
"edit_branch_prefix": "Zweigpräfix bearbeiten",
"help_on_tree_prefix": "Hilfe zum Baumpräfix",
"close": "Schließen",
"prefix": "Präfix: ",
"save": "Speichern",
"branch_prefix_saved": "Zweigpräfix wurde gespeichert."
},
"bulk_actions": {
"bulk_actions": "Massenaktionen",
"close": "Schließen",
"affected_notes": "Betroffene Notizen",
"include_descendants": "Unternotizen der ausgewählten Notizen einbeziehen",
"available_actions": "Verfügbare Aktionen",
@@ -61,20 +57,18 @@
},
"clone_to": {
"clone_notes_to": "Notizen klonen nach...",
"close": "Schließen",
"help_on_links": "Hilfe zu Links",
"notes_to_clone": "Notizen zum Klonen",
"target_parent_note": "Ziel-Übergeordnetenotiz",
"search_for_note_by_its_name": "Suche nach einer Notiz anhand ihres Namens",
"cloned_note_prefix_title": "Die geklonte Notiz wird im Notizbaum mit dem angegebenen Präfix angezeigt",
"prefix_optional": "Präfix (optional)",
"clone_to_selected_note": "Auf ausgewählte Notiz klonen <kbd>Eingabe</kbd>",
"clone_to_selected_note": "Auf ausgewählte Notiz klonen",
"no_path_to_clone_to": "Kein Pfad zum Klonen.",
"note_cloned": "Die Notiz \"{{clonedTitle}}\" wurde in \"{{targetTitle}}\" hinein geklont"
},
"confirm": {
"confirmation": "Bestätigung",
"close": "Schließen",
"cancel": "Abbrechen",
"ok": "OK",
"are_you_sure_remove_note": "Bist du sicher, dass du \"{{title}}\" von der Beziehungskarte entfernen möchten? ",
@@ -87,9 +81,9 @@
"delete_all_clones_description": "auch alle Klone löschen (kann bei letzte Änderungen rückgängig gemacht werden)",
"erase_notes_description": "Beim normalen (vorläufigen) Löschen werden die Notizen nur als gelöscht markiert und sie können innerhalb eines bestimmten Zeitraums (im Dialogfeld „Letzte Änderungen“) wiederhergestellt werden. Wenn du diese Option aktivierst, werden die Notizen sofort gelöscht und es ist nicht möglich, die Notizen wiederherzustellen.",
"erase_notes_warning": "Notizen dauerhaft löschen (kann nicht rückgängig gemacht werden), einschließlich aller Klone. Dadurch wird ein Neuladen der Anwendung erzwungen.",
"notes_to_be_deleted": "Folgende Notizen werden gelöscht (<span class=\"deleted-notes-count\"></span>)",
"notes_to_be_deleted": "Folgende Notizen werden gelöscht ({{notesCount}})",
"no_note_to_delete": "Es werden keine Notizen gelöscht (nur Klone).",
"broken_relations_to_be_deleted": "Folgende Beziehungen werden gelöst und gelöscht (<span class=\"broke-relations-count\"></span>)",
"broken_relations_to_be_deleted": "Folgende Beziehungen werden gelöst und gelöscht ({{ relationCount}})",
"cancel": "Abbrechen",
"ok": "OK",
"deleted_relation_text": "Notiz {{- note}} (soll gelöscht werden) wird von Beziehung {{- relation}} ausgehend von {{- source}} referenziert."
@@ -113,20 +107,18 @@
"format_pdf": "PDF - für Ausdrucke oder Teilen."
},
"help": {
"fullDocumentation": "Hilfe (gesamte Dokumentation ist <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a> verfügbar)",
"close": "Schließen",
"noteNavigation": "Notiz Navigation",
"goUpDown": "<kbd>Pfeil Hoch</kbd>, <kbd>Pfeil Runter</kbd> - In der Liste der Notizen nach oben/unten gehen",
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - Knoten reduzieren/erweitern",
"goUpDown": "In der Liste der Notizen nach oben/unten gehen",
"collapseExpand": "Knoten reduzieren/erweitern",
"notSet": "nicht eingestellt",
"goBackForwards": "in der Historie zurück/vorwärts gehen",
"showJumpToNoteDialog": "zeige <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Springe zu\" dialog</a>",
"scrollToActiveNote": "Scrolle zur aktiven Notiz",
"jumpToParentNote": "<kbd>Backspace</kbd> - Zur übergeordneten Notiz springen",
"jumpToParentNote": "Zur übergeordneten Notiz springen",
"collapseWholeTree": "Reduziere den gesamten Notizbaum",
"collapseSubTree": "Teilbaum einklappen",
"tabShortcuts": "Tab-Tastenkürzel",
"newTabNoteLink": "<kbd>Strg+Klick</kbd> - (oder mittlerer Mausklick) auf den Notizlink öffnet die Notiz in einem neuen Tab",
"newTabNoteLink": "auf den Notizlink öffnet die Notiz in einem neuen Tab",
"onlyInDesktop": "Nur im Desktop (Electron Build)",
"openEmptyTab": "Leeren Tab öffnen",
"closeActiveTab": "Aktiven Tab schließen",
@@ -141,14 +133,14 @@
"moveNoteUpHierarchy": "Verschiebe die Notiz in der Hierarchie nach oben",
"multiSelectNote": "Mehrfachauswahl von Notizen oben/unten",
"selectAllNotes": "Wähle alle Notizen in der aktuellen Ebene aus",
"selectNote": "<kbd>Umschalt+Klick</kbd> - Notiz auswählen",
"selectNote": "Notiz auswählen",
"copyNotes": "Kopiere aktive Notiz (oder aktuelle Auswahl) in den Zwischenspeicher (wird genutzt für <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">Klonen</a>)",
"cutNotes": "Aktuelle Notiz (oder aktuelle Auswahl) in die Zwischenablage ausschneiden (wird zum Verschieben von Notizen verwendet)",
"pasteNotes": "Notiz(en) als Unternotiz in die aktive Notiz einfügen (entweder verschieben oder klonen, je nachdem, ob sie kopiert oder in die Zwischenablag e ausgeschnitten wurde)",
"deleteNotes": "Notiz / Unterbaum löschen",
"editingNotes": "Notizen bearbeiten",
"editNoteTitle": "Im Baumbereich wird vom Baumbereich zum Notiztitel gewechselt. Beim Druck auf Eingabe im Notiztitel, wechselt der Fokus zum Texteditor. <kbd>Strg+.</kbd> wechselt vom Editor zurück zum Baumbereich.",
"createEditLink": "<kbd>Strg+K</kbd> - Externen Link erstellen/bearbeiten",
"createEditLink": "Externen Link erstellen/bearbeiten",
"createInternalLink": "Internen Link erstellen",
"followLink": "Folge dem Link unter dem Cursor",
"insertDateTime": "Gebe das aktuelle Datum und die aktuelle Uhrzeit an der Einfügemarke ein",
@@ -164,11 +156,12 @@
"showSQLConsole": "SQL-Konsole anzeigen",
"other": "Andere",
"quickSearch": "Fokus auf schnelle Sucheingabe",
"inPageSearch": "Auf-der-Seite-Suche"
"inPageSearch": "Auf-der-Seite-Suche",
"newTabWithActivationNoteLink": "auf einen Notiz-Link öffnet und aktiviert die Notiz in einem neuen Tab",
"title": "Spickzettel"
},
"import": {
"importIntoNote": "In Notiz importieren",
"close": "Schließen",
"chooseImportFile": "Wähle Importdatei aus",
"importDescription": "Der Inhalt der ausgewählten Datei(en) wird als untergeordnete Notiz(en) importiert",
"options": "Optionen",
@@ -195,14 +188,13 @@
},
"include_note": {
"dialog_title": "Notiz beifügen",
"close": "Schließen",
"label_note": "Notiz",
"placeholder_search": "Suche nach einer Notiz anhand ihres Namens",
"box_size_prompt": "Kartongröße des beigelegten Zettels:",
"box_size_small": "klein (~ 10 Zeilen)",
"box_size_medium": "mittel (~ 30 Zeilen)",
"box_size_full": "vollständig (Feld zeigt vollständigen Text)",
"button_include": "Notiz beifügen <kbd>Eingabetaste</kbd>"
"button_include": "Notiz beifügen"
},
"info": {
"modalTitle": "Infonachricht",
@@ -210,42 +202,35 @@
"okButton": "OK"
},
"jump_to_note": {
"close": "Schließen",
"search_button": "Suche im Volltext: <kbd>Strg+Eingabetaste</kbd>"
"search_button": "Suche im Volltext"
},
"markdown_import": {
"dialog_title": "Markdown-Import",
"close": "Schließen",
"modal_body_text": "Aufgrund der Browser-Sandbox ist es nicht möglich, die Zwischenablage direkt aus JavaScript zu lesen. Bitte füge den zu importierenden Markdown in den Textbereich unten ein und klicke auf die Schaltfläche „Importieren“.",
"import_button": "Importieren Strg+Eingabe",
"import_button": "Importieren",
"import_success": "Markdown-Inhalt wurde in das Dokument importiert."
},
"move_to": {
"dialog_title": "Notizen verschieben nach ...",
"close": "Schließen",
"notes_to_move": "Notizen zum Verschieben",
"target_parent_note": "Ziel-Elternnotiz",
"search_placeholder": "Suche nach einer Notiz anhand ihres Namens",
"move_button": "Zur ausgewählten Notiz wechseln <kbd>Eingabetaste</kbd>",
"move_button": "Zur ausgewählten Notiz wechseln",
"error_no_path": "Kein Weg, auf den man sich bewegen kann.",
"move_success_message": "Ausgewählte Notizen wurden verschoben"
},
"note_type_chooser": {
"modal_title": "Wähle den Notiztyp aus",
"close": "Schließen",
"modal_body": "Wähle den Notiztyp / die Vorlage der neuen Notiz:",
"templates": "Vorlagen:"
"templates": "Vorlagen"
},
"password_not_set": {
"title": "Das Passwort ist nicht festgelegt",
"close": "Schließen",
"body1": "Geschützte Notizen werden mit einem Benutzerpasswort verschlüsselt, es wurde jedoch noch kein Passwort festgelegt.",
"body2": "Um Notizen verschlüsseln zu können, klicke <a class=\"open-password-options-button\" href=\"javascript:\">hier</a> um das Optionsmenu zu öffnen und ein Passwort zu setzen."
"body1": "Geschützte Notizen werden mit einem Benutzerpasswort verschlüsselt, es wurde jedoch noch kein Passwort festgelegt."
},
"prompt": {
"title": "Prompt",
"close": "Schließen",
"ok": "OK <kbd>Eingabe</kbd>",
"ok": "OK",
"defaultTitle": "Prompt"
},
"protected_session_password": {
@@ -253,12 +238,11 @@
"help_title": "Hilfe zu geschützten Notizen",
"close_label": "Schließen",
"form_label": "Um mit der angeforderten Aktion fortzufahren, musst du eine geschützte Sitzung starten, indem du ein Passwort eingibst:",
"start_button": "Geschützte Sitzung starten <kbd>enter</kbd>"
"start_button": "Geschützte Sitzung starten"
},
"recent_changes": {
"title": "Aktuelle Änderungen",
"erase_notes_button": "Jetzt gelöschte Notizen löschen",
"close": "Schließen",
"deleted_notes_message": "Gelöschte Notizen wurden gelöscht.",
"no_changes_message": "Noch keine Änderungen...",
"undelete_link": "Wiederherstellen",
@@ -269,7 +253,6 @@
"delete_all_revisions": "Lösche alle Revisionen dieser Notiz",
"delete_all_button": "Alle Revisionen löschen",
"help_title": "Hilfe zu Notizrevisionen",
"close": "Schließen",
"revision_last_edited": "Diese Revision wurde zuletzt am {{date}} bearbeitet",
"confirm_delete_all": "Möchtest du alle Revisionen dieser Notiz löschen?",
"no_revisions": "Für diese Notiz gibt es noch keine Revisionen...",
@@ -289,7 +272,6 @@
},
"sort_child_notes": {
"sort_children_by": "Unternotizen sortieren nach...",
"close": "Schließen",
"sorting_criteria": "Sortierkriterien",
"title": "Titel",
"date_created": "Erstellungsdatum",
@@ -303,13 +285,12 @@
"sort_with_respect_to_different_character_sorting": "Sortierung im Hinblick auf unterschiedliche Sortier- und Sortierregeln für Zeichen in verschiedenen Sprachen oder Regionen.",
"natural_sort_language": "Natürliche Sortiersprache",
"the_language_code_for_natural_sort": "Der Sprachcode für die natürliche Sortierung, z. B. \"de-DE\" für Deutsch.",
"sort": "Sortieren <kbd>Eingabetaste</kbd>"
"sort": "Sortieren"
},
"upload_attachments": {
"upload_attachments_to_note": "Lade Anhänge zur Notiz hoch",
"close": "Schließen",
"choose_files": "Wähle Dateien aus",
"files_will_be_uploaded": "Dateien werden als Anhänge in hochgeladen",
"files_will_be_uploaded": "Dateien werden als Anhänge in hochgeladen {{noteTitle}}",
"options": "Optionen",
"shrink_images": "Bilder verkleinern",
"upload": "Hochladen",
@@ -1098,12 +1079,9 @@
"title": "Thema",
"theme_label": "Thema",
"override_theme_fonts_label": "Theme-Schriftarten überschreiben",
"auto_theme": "Auto",
"light_theme": "Hell",
"dark_theme": "Dunkel",
"triliumnext": "TriliumNext Beta (Systemfarbschema folgend)",
"triliumnext-light": "TriliumNext Beta (Hell)",
"triliumnext-dark": "TriliumNext Beta (Dunkel)",
"triliumnext": "Trilium (Systemfarbschema folgend)",
"triliumnext-light": "Trilium (Hell)",
"triliumnext-dark": "Trilium (Dunkel)",
"layout": "Layout",
"layout-vertical-title": "Vertikal",
"layout-horizontal-title": "Horizontal",
@@ -1650,5 +1628,8 @@
},
"time_selector": {
"invalid_input": "Die eingegebene Zeit ist keine valide Zahl."
},
"modal": {
"close": "Schließen"
}
}

View File

@@ -1,7 +1,6 @@
{
"about": {
"title": "Πληροφορίες για το Trilium Notes",
"close": "Κλείσιμο",
"homepage": "Αρχική Σελίδα:",
"app_version": "Έκδοση εφαρμογής:",
"db_version": "Έκδοση βάσης δεδομένων:",

View File

@@ -1,7 +1,6 @@
{
"about": {
"title": "About Trilium Notes",
"close": "Close",
"homepage": "Homepage:",
"app_version": "App version:",
"db_version": "DB version:",
@@ -28,25 +27,22 @@
"add_link": {
"add_link": "Add link",
"help_on_links": "Help on links",
"close": "Close",
"note": "Note",
"search_note": "search for note by its name",
"link_title_mirrors": "link title mirrors the note's current title",
"link_title_arbitrary": "link title can be changed arbitrarily",
"link_title": "Link title",
"button_add_link": "Add link <kbd>enter</kbd>"
"button_add_link": "Add link"
},
"branch_prefix": {
"edit_branch_prefix": "Edit branch prefix",
"help_on_tree_prefix": "Help on Tree prefix",
"close": "Close",
"prefix": "Prefix: ",
"save": "Save",
"branch_prefix_saved": "Branch prefix has been saved."
},
"bulk_actions": {
"bulk_actions": "Bulk actions",
"close": "Close",
"affected_notes": "Affected notes",
"include_descendants": "Include descendants of the selected notes",
"available_actions": "Available actions",
@@ -61,20 +57,18 @@
},
"clone_to": {
"clone_notes_to": "Clone notes to...",
"close": "Close",
"help_on_links": "Help on links",
"notes_to_clone": "Notes to clone",
"target_parent_note": "Target parent note",
"search_for_note_by_its_name": "search for note by its name",
"cloned_note_prefix_title": "Cloned note will be shown in note tree with given prefix",
"prefix_optional": "Prefix (optional)",
"clone_to_selected_note": "Clone to selected note <kbd>enter</kbd>",
"clone_to_selected_note": "Clone to selected note",
"no_path_to_clone_to": "No path to clone to.",
"note_cloned": "Note \"{{clonedTitle}}\" has been cloned into \"{{targetTitle}}\""
},
"confirm": {
"confirmation": "Confirmation",
"close": "Close",
"cancel": "Cancel",
"ok": "OK",
"are_you_sure_remove_note": "Are you sure you want to remove the note \"{{title}}\" from relation map? ",
@@ -87,9 +81,9 @@
"delete_all_clones_description": "Delete also all clones (can be undone in recent changes)",
"erase_notes_description": "Normal (soft) deletion only marks the notes as deleted and they can be undeleted (in recent changes dialog) within a period of time. Checking this option will erase the notes immediately and it won't be possible to undelete the notes.",
"erase_notes_warning": "Erase notes permanently (can't be undone), including all clones. This will force application reload.",
"notes_to_be_deleted": "Following notes will be deleted ({{- noteCount}})",
"notes_to_be_deleted": "Following notes will be deleted ({{notesCount}})",
"no_note_to_delete": "No note will be deleted (only clones).",
"broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{- relationCount}})",
"broken_relations_to_be_deleted": "Following relations will be broken and deleted ({{ relationCount}})",
"cancel": "Cancel",
"ok": "OK",
"deleted_relation_text": "Note {{- note}} (to be deleted) is referenced by relation {{- relation}} originating from {{- source}}."
@@ -113,21 +107,20 @@
"format_pdf": "PDF - for printing or sharing purposes."
},
"help": {
"fullDocumentation": "Help (full documentation is available <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
"close": "Close",
"title": "Cheatsheet",
"noteNavigation": "Note navigation",
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> - go up/down in the list of notes",
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - collapse/expand node",
"goUpDown": "go up/down in the list of notes",
"collapseExpand": "collapse/expand node",
"notSet": "not set",
"goBackForwards": "go back / forwards in the history",
"showJumpToNoteDialog": "show <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Jump to\" dialog</a>",
"scrollToActiveNote": "scroll to active note",
"jumpToParentNote": "<kbd>Backspace</kbd> - jump to parent note",
"jumpToParentNote": "jump to parent note",
"collapseWholeTree": "collapse whole note tree",
"collapseSubTree": "collapse sub-tree",
"tabShortcuts": "Tab shortcuts",
"newTabNoteLink": "<kbd>Ctrl+click</kbd> - (or <kbd>middle mouse click</kbd>) on note link opens note in a new tab",
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+click</kbd> - (or <kbd>Shift+middle mouse click</kbd>) on note link opens and activates the note in a new tab",
"newTabNoteLink": "on note link opens note in a new tab",
"newTabWithActivationNoteLink": "on note link opens and activates the note in a new tab",
"onlyInDesktop": "Only in desktop (Electron build)",
"openEmptyTab": "open empty tab",
"closeActiveTab": "close active tab",
@@ -142,14 +135,14 @@
"moveNoteUpHierarchy": "move note up in the hierarchy",
"multiSelectNote": "multi-select note above/below",
"selectAllNotes": "select all notes in the current level",
"selectNote": "<kbd>Shift+click</kbd> - select note",
"selectNote": "select note",
"copyNotes": "copy active note (or current selection) into clipboard (used for <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">cloning</a>)",
"cutNotes": "cut current note (or current selection) into clipboard (used for moving notes)",
"pasteNotes": "paste note(s) as sub-note into active note (which is either move or clone depending on whether it was copied or cut into clipboard)",
"deleteNotes": "delete note / sub-tree",
"editingNotes": "Editing notes",
"editNoteTitle": "in tree pane will switch from tree pane into note title. Enter from note title will switch focus to text editor. <kbd>Ctrl+.</kbd> will switch back from editor to tree pane.",
"createEditLink": "<kbd>Ctrl+K</kbd> - create / edit external link",
"createEditLink": "create / edit external link",
"createInternalLink": "create internal link",
"followLink": "follow link under cursor",
"insertDateTime": "insert current date and time at caret position",
@@ -169,7 +162,6 @@
},
"import": {
"importIntoNote": "Import into note",
"close": "Close",
"chooseImportFile": "Choose import file",
"importDescription": "Content of the selected file(s) will be imported as child note(s) into",
"options": "Options",
@@ -196,14 +188,13 @@
},
"include_note": {
"dialog_title": "Include note",
"close": "Close",
"label_note": "Note",
"placeholder_search": "search for note by its name",
"box_size_prompt": "Box size of the included note:",
"box_size_small": "small (~ 10 lines)",
"box_size_medium": "medium (~ 30 lines)",
"box_size_full": "full (box shows complete text)",
"button_include": "Include note <kbd>enter</kbd>"
"button_include": "Include note"
},
"info": {
"modalTitle": "Info message",
@@ -212,23 +203,20 @@
},
"jump_to_note": {
"search_placeholder": "Search for note by its name or type > for commands...",
"close": "Close",
"search_button": "Search in full text <kbd>Ctrl+Enter</kbd>"
"search_button": "Search in full text"
},
"markdown_import": {
"dialog_title": "Markdown import",
"close": "Close",
"modal_body_text": "Because of browser sandbox it's not possible to directly read clipboard from JavaScript. Please paste the Markdown to import to textarea below and click on Import button",
"import_button": "Import Ctrl+Enter",
"import_button": "Import",
"import_success": "Markdown content has been imported into the document."
},
"move_to": {
"dialog_title": "Move notes to ...",
"close": "Close",
"notes_to_move": "Notes to move",
"target_parent_note": "Target parent note",
"search_placeholder": "search for note by its name",
"move_button": "Move to selected note <kbd>enter</kbd>",
"move_button": "Move to selected note",
"error_no_path": "No path to move to.",
"move_success_message": "Selected notes have been moved into "
},
@@ -236,20 +224,19 @@
"change_path_prompt": "Change where to create the new note:",
"search_placeholder": "search path by name (default if empty)",
"modal_title": "Choose note type",
"close": "Close",
"modal_body": "Choose note type / template of the new note:",
"templates": "Templates:"
"templates": "Templates",
"builtin_templates": "Built-in Templates"
},
"password_not_set": {
"title": "Password is not set",
"close": "Close",
"body1": "Protected notes are encrypted using a user password, but password has not been set yet.",
"body2": "To be able to protect notes, click <a class=\"open-password-options-button\" href=\"javascript:\">here</a> to open the Options dialog and set your password."
"body2": "To be able to protect notes, click the button below to open the Options dialog and set your password.",
"go_to_password_options": "Go to Password options"
},
"prompt": {
"title": "Prompt",
"close": "Close",
"ok": "OK <kbd>enter</kbd>",
"ok": "OK",
"defaultTitle": "Prompt"
},
"protected_session_password": {
@@ -257,12 +244,11 @@
"help_title": "Help on Protected notes",
"close_label": "Close",
"form_label": "To proceed with requested action you need to start protected session by entering password:",
"start_button": "Start protected session <kbd>enter</kbd>"
"start_button": "Start protected session"
},
"recent_changes": {
"title": "Recent changes",
"erase_notes_button": "Erase deleted notes now",
"close": "Close",
"deleted_notes_message": "Deleted notes have been erased.",
"no_changes_message": "No changes yet...",
"undelete_link": "undelete",
@@ -273,7 +259,6 @@
"delete_all_revisions": "Delete all revisions of this note",
"delete_all_button": "Delete all revisions",
"help_title": "Help on Note Revisions",
"close": "Close",
"revision_last_edited": "This revision was last edited on {{date}}",
"confirm_delete_all": "Do you want to delete all revisions of this note?",
"no_revisions": "No revisions for this note yet...",
@@ -295,7 +280,6 @@
},
"sort_child_notes": {
"sort_children_by": "Sort children by...",
"close": "Close",
"sorting_criteria": "Sorting criteria",
"title": "title",
"date_created": "date created",
@@ -309,13 +293,12 @@
"sort_with_respect_to_different_character_sorting": "sort with respect to different character sorting and collation rules in different languages or regions.",
"natural_sort_language": "Natural sort language",
"the_language_code_for_natural_sort": "The language code for natural sort, e.g. \"zh-CN\" for Chinese.",
"sort": "Sort <kbd>enter</kbd>"
"sort": "Sort"
},
"upload_attachments": {
"upload_attachments_to_note": "Upload attachments to note",
"close": "Close",
"choose_files": "Choose files",
"files_will_be_uploaded": "Files will be uploaded as attachments into",
"files_will_be_uploaded": "Files will be uploaded as attachments into {{noteTitle}}",
"options": "Options",
"shrink_images": "Shrink images",
"upload": "Upload",
@@ -1118,12 +1101,12 @@
"title": "Application Theme",
"theme_label": "Theme",
"override_theme_fonts_label": "Override theme fonts",
"auto_theme": "Auto",
"light_theme": "Light",
"dark_theme": "Dark",
"triliumnext": "TriliumNext Beta (Follow system color scheme)",
"triliumnext-light": "TriliumNext Beta (Light)",
"triliumnext-dark": "TriliumNext Beta (Dark)",
"auto_theme": "Legacy (Follow system color scheme)",
"light_theme": "Legacy (Light)",
"dark_theme": "Legacy (Dark)",
"triliumnext": "Trilium (Follow system color scheme)",
"triliumnext-light": "Trilium (Light)",
"triliumnext-dark": "Trilium (Dark)",
"layout": "Layout",
"layout-vertical-title": "Vertical",
"layout-horizontal-title": "Horizontal",
@@ -2007,6 +1990,15 @@
"open_externally": "Open externally"
},
"modal": {
"close": "Close"
"close": "Close",
"help_title": "Display more information about this screen"
},
"call_to_action": {
"next_theme_title": "Try the new Trilium theme",
"next_theme_message": "You are currently using the legacy theme, would you like to try the new theme?",
"next_theme_button": "Try the new theme",
"background_effects_title": "Background effects are now stable",
"background_effects_message": "On Windows devices, background effects are now fully stable. The background effects adds a touch of color to the user interface by blurring the background behind it. This technique is also used in other applications such as Windows Explorer.",
"background_effects_button": "Enable background effects"
}
}

View File

@@ -1,7 +1,6 @@
{
"about": {
"title": "Acerca de Trilium Notes",
"close": "Cerrar",
"homepage": "Página principal:",
"app_version": "Versión de la aplicación:",
"db_version": "Versión de base de datos:",
@@ -28,25 +27,22 @@
"add_link": {
"add_link": "Agregar enlace",
"help_on_links": "Ayuda sobre enlaces",
"close": "Cerrar",
"note": "Nota",
"search_note": "buscar nota por su nombre",
"link_title_mirrors": "el título del enlace replica el título actual de la nota",
"link_title_arbitrary": "el título del enlace se puede cambiar arbitrariamente",
"link_title": "Título del enlace",
"button_add_link": "Agregar enlace <kbd>Enter</kbd>"
"button_add_link": "Agregar enlace"
},
"branch_prefix": {
"edit_branch_prefix": "Editar prefijo de rama",
"help_on_tree_prefix": "Ayuda sobre el prefijo del árbol",
"close": "Cerrar",
"prefix": "Prefijo: ",
"save": "Guardar",
"branch_prefix_saved": "Se ha guardado el prefijo de rama."
},
"bulk_actions": {
"bulk_actions": "Acciones en bloque",
"close": "Cerrar",
"affected_notes": "Notas afectadas",
"include_descendants": "Incluir descendientes de las notas seleccionadas",
"available_actions": "Acciones disponibles",
@@ -61,20 +57,18 @@
},
"clone_to": {
"clone_notes_to": "Clonar notas a...",
"close": "Cerrar",
"help_on_links": "Ayuda sobre enlaces",
"notes_to_clone": "Notas a clonar",
"target_parent_note": "Nota padre de destino",
"search_for_note_by_its_name": "buscar nota por su nombre",
"cloned_note_prefix_title": "La nota clonada se mostrará en el árbol de notas con el prefijo dado",
"prefix_optional": "Prefijo (opcional)",
"clone_to_selected_note": "Clonar a nota seleccionada <kbd>enter</kbd>",
"clone_to_selected_note": "Clonar a nota seleccionada",
"no_path_to_clone_to": "No hay ruta para clonar.",
"note_cloned": "La nota \"{{clonedTitle}}\" a sido clonada en \"{{targetTitle}}\""
},
"confirm": {
"confirmation": "Confirmación",
"close": "Cerrar",
"cancel": "Cancelar",
"ok": "Aceptar",
"are_you_sure_remove_note": "¿Está seguro que desea eliminar la nota \"{{title}}\" del mapa de relaciones? ",
@@ -87,9 +81,9 @@
"delete_all_clones_description": "Eliminar también todos los clones (se puede deshacer en cambios recientes)",
"erase_notes_description": "La eliminación normal (suave) solo marca las notas como eliminadas y se pueden recuperar (en el cuadro de diálogo de cambios recientes) dentro de un periodo de tiempo. Al marcar esta opción se borrarán las notas inmediatamente y no será posible recuperarlas.",
"erase_notes_warning": "Eliminar notas permanentemente (no se puede deshacer), incluidos todos los clones. Esto forzará la recarga de la aplicación.",
"notes_to_be_deleted": "Las siguientes notas serán eliminadas ({{- noteCount}})",
"notes_to_be_deleted": "Las siguientes notas serán eliminadas ({{notesCount}})",
"no_note_to_delete": "No se eliminará ninguna nota (solo clones).",
"broken_relations_to_be_deleted": "Las siguientes relaciones se romperán y serán eliminadas ({{- relationCount}})",
"broken_relations_to_be_deleted": "Las siguientes relaciones se romperán y serán eliminadas ({{ relationCount}})",
"cancel": "Cancelar",
"ok": "Aceptar",
"deleted_relation_text": "Nota {{- note}} (para ser eliminada) está referenciado por la relación {{- relation}} que se origina en {{- source}}."
@@ -113,21 +107,19 @@
"format_pdf": "PDF - para propósitos de impresión o compartición."
},
"help": {
"fullDocumentation": "Ayuda (la documentación completa está disponible <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
"close": "Cerrar",
"noteNavigation": "Navegación de notas",
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> - subir/bajar en la lista de notas",
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - colapsar/expandir nodo",
"goUpDown": "subir/bajar en la lista de notas",
"collapseExpand": "colapsar/expandir nodo",
"notSet": "no establecido",
"goBackForwards": "retroceder / avanzar en la historia",
"showJumpToNoteDialog": "mostrar <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Saltar a\" diálogo</a>",
"scrollToActiveNote": "desplazarse hasta la nota activa",
"jumpToParentNote": "<kbd>Backspace</kbd> - saltar a la nota padre",
"jumpToParentNote": "saltar a la nota padre",
"collapseWholeTree": "colapsar todo el árbol de notas",
"collapseSubTree": "colapsar subárbol",
"tabShortcuts": "Atajos de pestañas",
"newTabNoteLink": "<kbd>CTRL+clic</kbd> - (o <kbd>clic central del mouse</kbd>) en el enlace de la nota abre la nota en una nueva pestaña",
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+clic</kbd> - (o <kbd>Shift+clic de rueda de ratón</kbd>) en el enlace de la nota abre y activa la nota en una nueva pestaña",
"newTabNoteLink": "en el enlace de la nota abre la nota en una nueva pestaña",
"newTabWithActivationNoteLink": "en el enlace de la nota abre y activa la nota en una nueva pestaña",
"onlyInDesktop": "Solo en escritorio (compilación con Electron)",
"openEmptyTab": "abrir pestaña vacía",
"closeActiveTab": "cerrar pestaña activa",
@@ -142,14 +134,14 @@
"moveNoteUpHierarchy": "mover nota hacia arriba en la jerarquía",
"multiSelectNote": "selección múltiple de nota hacia arriba/abajo",
"selectAllNotes": "seleccionar todas las notas en el nivel actual",
"selectNote": "<kbd>Shift+click</kbd> - seleccionar nota",
"selectNote": "seleccionar nota",
"copyNotes": "copiar nota activa (o selección actual) al portapapeles (usado para <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">clonar</a>)",
"cutNotes": "cortar la nota actual (o la selección actual) en el portapapeles (usado para mover notas)",
"pasteNotes": "pegar notas como subnotas en la nota activa (que se puede mover o clonar dependiendo de si se copió o cortó en el portapapeles)",
"deleteNotes": "eliminar nota/subárbol",
"editingNotes": "Editando notas",
"editNoteTitle": "en el panel de árbol cambiará del panel de árbol al título de la nota. Ingresar desde el título de la nota cambiará el foco al editor de texto. <kbd>Ctrl+.</kbd> cambiará de nuevo del editor al panel de árbol.",
"createEditLink": "<kbd>Ctrl+K</kbd> - crear/editar enlace externo",
"createEditLink": "crear/editar enlace externo",
"createInternalLink": "crear enlace interno",
"followLink": "siga el enlace debajo del cursor",
"insertDateTime": "insertar la fecha y hora actuales en la posición del cursor",
@@ -169,7 +161,6 @@
},
"import": {
"importIntoNote": "Importar a nota",
"close": "Cerrar",
"chooseImportFile": "Elija el archivo de importación",
"importDescription": "El contenido de los archivos seleccionados se importará como notas secundarias en",
"options": "Opciones",
@@ -196,14 +187,13 @@
},
"include_note": {
"dialog_title": "Incluir nota",
"close": "Cerrar",
"label_note": "Nota",
"placeholder_search": "buscar nota por su nombre",
"box_size_prompt": "Tamaño de caja de la nota incluida:",
"box_size_small": "pequeño (~ 10 líneas)",
"box_size_medium": "medio (~ 30 líneas)",
"box_size_full": "completo (el cuadro muestra el texto completo)",
"button_include": "Incluir nota <kbd>Enter</kbd>"
"button_include": "Incluir nota"
},
"info": {
"modalTitle": "Mensaje informativo",
@@ -211,24 +201,21 @@
"okButton": "Aceptar"
},
"jump_to_note": {
"close": "Cerrar",
"search_button": "Buscar en texto completo <kbd>Ctrl+Enter</kbd>",
"search_button": "Buscar en texto completo",
"search_placeholder": "Busque nota por su nombre o escriba > para comandos..."
},
"markdown_import": {
"dialog_title": "Importación de Markdown",
"close": "Cerrar",
"modal_body_text": "Debido al entorno limitado del navegador, no es posible leer directamente el portapapeles desde JavaScript. Por favor, pegue el código Markdown para importar en el área de texto a continuación y haga clic en el botón Importar",
"import_button": "Importar Ctrl+Enter",
"import_button": "Importar",
"import_success": "El contenido de Markdown se ha importado al documento."
},
"move_to": {
"dialog_title": "Mover notas a...",
"close": "Cerrar",
"notes_to_move": "Notas a mover",
"target_parent_note": "Nota padre de destino",
"search_placeholder": "buscar nota por su nombre",
"move_button": "Mover a la nota seleccionada <kbd>enter</kbd>",
"move_button": "Mover a la nota seleccionada",
"error_no_path": "No hay ruta a donde mover.",
"move_success_message": "Las notas seleccionadas se han movido a "
},
@@ -236,20 +223,16 @@
"change_path_prompt": "Cambiar donde se creará la nueva nota:",
"search_placeholder": "ruta de búsqueda por nombre (por defecto si está vacío)",
"modal_title": "Elija el tipo de nota",
"close": "Cerrar",
"modal_body": "Elija el tipo de nota/plantilla de la nueva nota:",
"templates": "Plantillas:"
"templates": "Plantillas"
},
"password_not_set": {
"title": "La contraseña no está establecida",
"close": "Cerrar",
"body1": "Las notas protegidas se cifran mediante una contraseña de usuario, pero la contraseña aún no se ha establecido.",
"body2": "Para poder proteger notas, dé clic <a class=\"open-password-options-button\" href=\"javascript:\">aquí</a> para abrir el diálogo de Opciones y establecer tu contraseña."
"body1": "Las notas protegidas se cifran mediante una contraseña de usuario, pero la contraseña aún no se ha establecido."
},
"prompt": {
"title": "Aviso",
"close": "Cerrar",
"ok": "Aceptar <kbd>enter</kbd>",
"ok": "Aceptar",
"defaultTitle": "Aviso"
},
"protected_session_password": {
@@ -257,12 +240,11 @@
"help_title": "Ayuda sobre notas protegidas",
"close_label": "Cerrar",
"form_label": "Para continuar con la acción solicitada, debe iniciar en la sesión protegida ingresando la contraseña:",
"start_button": "Iniciar sesión protegida <kbd>entrar</kbd>"
"start_button": "Iniciar sesión protegida"
},
"recent_changes": {
"title": "Cambios recientes",
"erase_notes_button": "Borrar notas eliminadas ahora",
"close": "Cerrar",
"deleted_notes_message": "Las notas eliminadas han sido borradas.",
"no_changes_message": "Aún no hay cambios...",
"undelete_link": "recuperar",
@@ -273,7 +255,6 @@
"delete_all_revisions": "Eliminar todas las revisiones de esta nota",
"delete_all_button": "Eliminar todas las revisiones",
"help_title": "Ayuda sobre revisiones de notas",
"close": "Cerrar",
"revision_last_edited": "Esta revisión se editó por última vez en {{date}}",
"confirm_delete_all": "¿Quiere eliminar todas las revisiones de esta nota?",
"no_revisions": "Aún no hay revisiones para esta nota...",
@@ -295,7 +276,6 @@
},
"sort_child_notes": {
"sort_children_by": "Ordenar hijos por...",
"close": "Cerrar",
"sorting_criteria": "Criterios de ordenamiento",
"title": "título",
"date_created": "fecha de creación",
@@ -309,13 +289,12 @@
"sort_with_respect_to_different_character_sorting": "ordenar con respecto a diferentes reglas de ordenamiento y clasificación de caracteres en diferentes idiomas o regiones.",
"natural_sort_language": "Idioma de clasificación natural",
"the_language_code_for_natural_sort": "El código del idioma para el ordenamiento natural, ej. \"zh-CN\" para Chino.",
"sort": "Ordenar <kbd>Enter</kbd>"
"sort": "Ordenar"
},
"upload_attachments": {
"upload_attachments_to_note": "Cargar archivos adjuntos a nota",
"close": "Cerrar",
"choose_files": "Elija los archivos",
"files_will_be_uploaded": "Los archivos se cargarán como archivos adjuntos en",
"files_will_be_uploaded": "Los archivos se cargarán como archivos adjuntos en {{noteTitle}}",
"options": "Opciones",
"shrink_images": "Reducir imágenes",
"upload": "Subir",
@@ -1118,12 +1097,9 @@
"title": "Tema",
"theme_label": "Tema",
"override_theme_fonts_label": "Sobreescribir fuentes de tema",
"auto_theme": "Automático",
"light_theme": "Claro",
"dark_theme": "Oscuro",
"triliumnext": "TriliumNext Beta (Sigue el esquema de color del sistema)",
"triliumnext-light": "TriliumNext Beta (Claro)",
"triliumnext-dark": "TriliumNext Beta (Oscuro)",
"triliumnext": "Trilium (Sigue el esquema de color del sistema)",
"triliumnext-light": "Trilium (Claro)",
"triliumnext-dark": "Trilium (Oscuro)",
"layout": "Disposición",
"layout-vertical-title": "Vertical",
"layout-horizontal-title": "Horizontal",

View File

@@ -0,0 +1,22 @@
{
"about": {
"title": "درباره Trilium Notes",
"homepage": "صفحه اصلی:",
"app_version": "نسخه برنامه:",
"db_version": "نسخه پایگاه داده:",
"sync_version": "نسخه منطبق:",
"build_date": "تاریخ ساخت:",
"build_revision": "نسخه بازنگری شده:",
"data_directory": "دایرکتوری داده:"
},
"toast": {
"critical-error": {
"title": "خطای بحرانی",
"message": "خطای بحرانی رخ داده که مانع از اجرای برنامه می شود\n\n {{message}}\n\nبه احتمال زیاد ناشی از خطای غیرمنتظره در اجرای ناموفق یک اسکریپت است. برنامه را در مد ایمن اجرا کنید و خطا را بررسی نمایید."
}
},
"add_link": {
"add_link": "افزودن لینک",
"note": "یادداشت"
}
}

View File

@@ -0,0 +1,144 @@
{
"about": {
"title": "Lisätietoja Trilium Notes:ista",
"homepage": "Kotisivu:",
"app_version": "Sovelluksen versio:",
"db_version": "Tietokannan versio:",
"build_date": "Koontipäivämäärä:",
"data_directory": "Datakansio:",
"sync_version": "Synkronoinnin versio:",
"build_revision": "Sovelluksen versio:"
},
"toast": {
"critical-error": {
"title": "Kriittinen virhe"
},
"widget-error": {
"title": "Widgetin luonti epäonnistui"
}
},
"add_link": {
"add_link": "Lisää linkki",
"link_title": "Linkin otsikko",
"button_add_link": "Lisää linkki",
"note": "Muistio",
"search_note": "etsi muistiota sen nimellä"
},
"branch_prefix": {
"prefix": "Etuliite: ",
"save": "Tallenna"
},
"bulk_actions": {
"bulk_actions": "Massatoiminnot",
"available_actions": "Saatavilla olevat toiminnot",
"chosen_actions": "Valitut toiminnot",
"execute_bulk_actions": "Toteuta massatoiminnot",
"bulk_actions_executed": "Massatoiminnot on toteutettu onnistuneesti.",
"none_yet": "Ei vielä... lisää toiminto klikkaamalla jotiain yllä saatavilla olevaa yltä.",
"labels": "Merkit",
"relations": "Suhteet",
"notes": "Muistiot",
"other": "Muut",
"affected_notes": "Vaikuttaa muistioihin"
},
"clone_to": {
"clone_notes_to": "Kopioi muistiot...",
"help_on_links": "Apua linkkeihin",
"notes_to_clone": "Kopioitavat muistiot",
"target_parent_note": "Kohteen päämuistio",
"search_for_note_by_its_name": "ensi muistiota sen nimellä",
"cloned_note_prefix_title": "Kopioitu muistia näytetään puussa annetulla etuliitteellä",
"prefix_optional": "Etuliite (valinnainen)",
"clone_to_selected_note": "Kopioi valittuun muistioon",
"note_cloned": "Muistio \"{{clonedTitle}}\" on kopioitu \"{{targetTitle}}\""
},
"confirm": {
"confirmation": "Vahvistus",
"cancel": "Peruuta",
"ok": "OK",
"also_delete_note": "Poista myös muistio"
},
"delete_notes": {
"delete_notes_preview": "Poista muistion esikatselu",
"close": "Sulje",
"notes_to_be_deleted": "Seuraavat muistiot tullaan poistamaan ({{notesCount}})",
"no_note_to_delete": "Muistioita ei poisteta (vain kopiot).",
"cancel": "Peruuta",
"ok": "OK"
},
"export": {
"export_note_title": "Vie muistio",
"close": "Sulje",
"format_html": "HTML - suositeltu, sillä se säilyttää kaikki formatoinnit",
"format_markdown": "Markdown - tämä säilyttää suurimman osan formatoinneista.",
"opml_version_1": "OPML v1.0 - pelkkä teksti",
"opml_version_2": "OPML v2.0 - sallii myös HTML:n",
"export": "Vie",
"choose_export_type": "Valitse ensin viennin tyyppi",
"export_status": "Viennin tila",
"export_in_progress": "Vienti käynnissä: {{progressCount}}",
"export_finished_successfully": "Vienti valmistui onnistuneesti.",
"format_pdf": "PDF - tulostukseen ja jakamiseen."
},
"help": {
"title": "Lunttilappu",
"noteNavigation": "Muistion navigointi",
"goUpDown": "mene ylös/alas muistioiden listassa",
"collapseExpand": "pienennä/suurenna solmu",
"notSet": "ei asetettu",
"goBackForwards": "mene taaksepäin/eteenpäin historiassa",
"jumpToParentNote": "Hyppää ylempään muistioon",
"collapseWholeTree": "pienennä koko muistio puu",
"onlyInDesktop": "Vain työpöytänäkymässä (Electron build)",
"openEmptyTab": "Avaa tyhjä välilehti",
"closeActiveTab": "sulje aktiivinen välilehti",
"activateNextTab": "aktivoi seuraava välilehti",
"activatePreviousTab": "aktivoi edellinen välilehti",
"creatingNotes": "Luo muistiota",
"movingCloningNotes": "Siirrä / kopioi muistioita",
"moveNoteUpHierarchy": "siirrä muistio ylöspäin listassa",
"selectNote": "valitse muistio",
"editingNotes": "Muokkaa solmua",
"createEditLink": "luo / muokkaa ulkoista linkkiä",
"createInternalLink": "luo sisäinen linkki",
"insertDateTime": "lisää nykyinen päivämäärä ja aika hiiren kohdalle",
"troubleshooting": "Vianmääritys",
"reloadFrontend": "lataa Trilium:in käyttöliittymä",
"showDevTools": "näytä kehittäjätyökalut",
"showSQLConsole": "näytä SQL konsoli",
"other": "Muut"
},
"import": {
"importIntoNote": "Tuo muistioon",
"chooseImportFile": "Valitse tuonnin tiedosto",
"options": "Valinnat",
"safeImport": "Turvallinen tuonti",
"shrinkImages": "Kutista kuvat",
"replaceUnderscoresWithSpaces": "Korvaa alaviivat väleillä tuotujen muistioiden tiedostonimissä",
"import": "Tuo",
"failed": "Tuonti epäonnistui: {{message}}.",
"html_import_tags": {
"title": "HTML Tuonnin Tunnisteet",
"placeholder": "Lisää HTML tunnisteet, yksi per rivi"
},
"import-status": "Tuonnin tila",
"in-progress": "Tuonti vaiheessa: {{progress}}",
"successful": "Tuonti valmistui onnistuneesti."
},
"include_note": {
"dialog_title": "Sisällytä muistio",
"label_note": "Muistio",
"placeholder_search": "etsi muistiota sen nimellä",
"box_size_small": "pieni (~ 10 riviä)",
"box_size_medium": "keskisuuri (~ 30 riviä)",
"button_include": "Sisällytä muistio"
},
"info": {
"modalTitle": "Info viesti",
"closeButton": "Sulje",
"okButton": "OK"
},
"jump_to_note": {
"search_button": "Etsi koko tekstistä"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
{
"about": {
"close": "Chiudi",
"app_version": "Versione dell'app:",
"db_version": "Versione DB:",
"sync_version": "Versione Sync:",
@@ -27,7 +26,6 @@
},
"add_link": {
"add_link": "Aggiungi un collegamento",
"close": "Chiudi",
"note": "Nota",
"search_note": "cerca una nota per nome",
"link_title_mirrors": "il titolo del collegamento rispecchia il titolo della nota corrente",
@@ -39,14 +37,12 @@
"branch_prefix": {
"edit_branch_prefix": "Modifica il prefisso del ramo",
"help_on_tree_prefix": "Aiuto sui prefissi dell'Albero",
"close": "Chiudi",
"prefix": "Prefisso: ",
"save": "Salva",
"branch_prefix_saved": "Il prefisso del ramo è stato salvato."
},
"bulk_actions": {
"bulk_actions": "Azioni massive",
"close": "Chiudi",
"affected_notes": "Note influenzate",
"include_descendants": "Includi i discendenti della nota selezionata",
"available_actions": "Azioni disponibili",
@@ -61,7 +57,6 @@
},
"clone_to": {
"clone_notes_to": "Clona note in...",
"close": "Chiudi",
"help_on_links": "Aiuto sui collegamenti",
"notes_to_clone": "Note da clonare",
"target_parent_note": "Nodo padre obiettivo",
@@ -73,7 +68,6 @@
"note_cloned": "La nota \"{{clonedTitle}}\" è stata clonata in \"{{targetTitle}}\""
},
"confirm": {
"close": "Chiudi",
"cancel": "Annulla",
"ok": "OK",
"confirmation": "Conferma",
@@ -106,47 +100,19 @@
"choose_export_type": "Scegli prima il tipo di esportazione, per favore",
"export_in_progress": "Esportazione in corso: {{progressCount}}",
"export_finished_successfully": "Esportazione terminata con successo.",
"format_pdf": "PDF- allo scopo di stampa o esportazione."
},
"help": {
"close": "Chiudi",
"fullDocumentation": "Aiuto (la documentazione completa è disponibile <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)"
},
"import": {
"close": "Chiudi"
},
"include_note": {
"close": "Chiudi"
},
"jump_to_note": {
"close": "Chiudi"
},
"markdown_import": {
"close": "Chiudi"
},
"move_to": {
"close": "Chiudi"
},
"note_type_chooser": {
"close": "Chiudi"
"format_pdf": "PDF- allo scopo di stampa o esportazione.",
"export_type_subtree": "Questa nota e tutti i suoi discendenti",
"format_html": "HTML - raccomandato in quanto mantiene tutti i formati",
"format_html_zip": "HTML in archivio ZIP - questo è raccomandato in quanto conserva tutta la formattazione.",
"format_markdown": "MArkdown - questo conserva la maggior parte della formattazione."
},
"password_not_set": {
"close": "Chiudi",
"body1": "Le note protette sono crittografate utilizzando una password utente, ma la password non è stata ancora impostata.",
"body2": "Per proteggere le note, fare clic su <a class=\"open-password-options-button\" href=\"javascript:\">qui</a> per aprire la finestra di dialogo Opzioni e impostare la password."
},
"protected_session_password": {
"close_label": "Chiudi"
},
"prompt": {
"close": "Chiudi"
},
"recent_changes": {
"close": "Chiudi"
},
"revisions": {
"close": "Chiudi"
},
"abstract_bulk_action": {
"remove_this_search_action": "Rimuovi questa azione di ricerca"
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,340 +1,414 @@
{
"code_block": {
"theme_none": "Sem destaque de sintaxe",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas escuros"
"code_block": {
"theme_none": "Sem destaque de sintaxe",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas escuros"
},
"about": {
"title": "Sobre o Trilium Notes",
"homepage": "Página inicial:",
"app_version": "Versão do App:",
"db_version": "Versão do db:",
"sync_version": "Versão de sincronização:",
"build_date": "Data de compilação:",
"build_revision": "Revisão da compilação:",
"data_directory": "Diretório de dados:"
},
"toast": {
"critical-error": {
"title": "Erro crítico",
"message": "Ocorreu um erro crítico que impede a inicialização do aplicativo cliente:\n\n{{message}}\n\nIsso provavelmente foi causado por um script que falhou de maneira inesperada. Tente iniciar o aplicativo no modo seguro e resolva o problema."
},
"about": {
"title": "Sobre o Trilium Notes",
"close": "Fechar",
"homepage": "Página inicial:",
"app_version": "Versão do App:",
"db_version": "Versão do db:",
"sync_version": "Versão de sincronização:",
"build_date": "Data de compilação:",
"build_revision": "Revisão da compilação:",
"data_directory": "Diretório de dados:"
"widget-error": {
"title": "Falha ao inicializar um widget",
"message-custom": "O widget personalizado da nota com ID \"{{id}}\", intitulada \"{{title}}\", não pôde ser inicializado devido a:\n\n{{message}}",
"message-unknown": "Widget desconhecido não pôde ser inicializado devido a:\n\n{{message}}"
},
"toast": {
"critical-error": {
"title": "Erro crítico",
"message": "Ocorreu um erro crítico que impede a inicialização do aplicativo cliente:\n\n{{message}}\n\nIsso provavelmente foi causado por um script que falhou de maneira inesperada. Tente iniciar o aplicativo no modo seguro e resolva o problema."
},
"widget-error": {
"title": "Falha ao inicializar um widget",
"message-custom": "O widget personalizado da nota com ID \"{{id}}\", intitulada \"{{title}}\", não pôde ser inicializado devido a:\n\n{{message}}",
"message-unknown": "Widget desconhecido não pôde ser inicializado devido a:\n\n{{message}}"
},
"bundle-error": {
"title": "Falha para carregar o script customizado",
"message": "O script da nota com ID \"{{id}}\", intitulada \"{{title}}\", não pôde ser executado devido a:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Adicionar link",
"help_on_links": "Ajuda sobre links",
"close": "Fechar",
"note": "Nota",
"search_note": "pesquisar nota pelo nome",
"link_title_mirrors": "o título do link reflete o título atual da nota",
"link_title_arbitrary": "o título do link pode ser alterado livremente",
"link_title": "Titulo do link",
"button_add_link": "Adicionar link <kbd>enter</kbd>"
},
"branch_prefix": {
"close": "Fechar",
"prefix": "Prefixo: ",
"save": "Salvar",
"edit_branch_prefix": "Editar Prefixo do Branch",
"help_on_tree_prefix": "Ajuda sobre o prefixo da árvore de notas",
"branch_prefix_saved": "O prefixo de ramificação foi salvo."
},
"bulk_actions": {
"bulk_actions": "Ações em massa",
"close": "Fechar",
"affected_notes": "Notas Afetadas",
"include_descendants": "Incluir notas filhas das notas selecionadas",
"available_actions": "Ações disponíveis",
"chosen_actions": "Ações selecionadas",
"execute_bulk_actions": "Executar ações em massa",
"bulk_actions_executed": "As ações em massa foram concluídas com sucesso.",
"none_yet": "Nenhuma até agora... adicione uma ação clicando em uma das disponíveis acima.",
"labels": "Etiquetas",
"relations": "Relações",
"notes": "Notas",
"other": "Outros"
},
"clone_to": {
"clone_notes_to": "Clonar notas para...",
"close": "Fechar",
"help_on_links": "Ajuda sobre links",
"notes_to_clone": "Notas para clonar",
"search_for_note_by_its_name": "pesquisar nota pelo nome",
"cloned_note_prefix_title": "A nota clonada aparecerá na árvore de notas com o prefixo fornecido",
"prefix_optional": "Prefixo (opcional)",
"no_path_to_clone_to": "Nenhum caminho para clonar.",
"target_parent_note": "Nota pai-alvo",
"clone_to_selected_note": "Clonar para a nota selecionada <kbd>enter</kbd>",
"note_cloned": "A nota \"{{clonedTitle}}\" foi clonada para \"{{targetTitle}}\""
},
"ai_llm": {
"n_notes_queued_0": "{{ count }} nota enfileirada para indexação",
"n_notes_queued_1": "{{ count }} notas enfileiradas para indexação",
"n_notes_queued_2": "{{ count }} notas enfileiradas para indexação",
"notes_indexed_0": "{{ count }} nota indexada",
"notes_indexed_1": "{{ count }} notas indexadas",
"notes_indexed_2": "{{ count }} notas indexadas"
},
"confirm": {
"confirmation": "Confirmação",
"close": "Fechar",
"cancel": "Cancelar",
"ok": "OK",
"are_you_sure_remove_note": "Tem certeza de que deseja remover a nota '{{title}}' do mapa de relações? ",
"if_you_dont_check": "Se você não marcar isso, a nota será removida apenas do mapa de relações.",
"also_delete_note": "Também excluir a nota"
},
"delete_notes": {
"delete_notes_preview": "Excluir pré-visualização de notas",
"close": "Fechar",
"delete_all_clones_description": "Excluir também todos os clones (pode ser desfeito em alterações recentes)",
"erase_notes_description": "A exclusão normal (suave) apenas marca as notas como excluídas, permitindo que sejam recuperadas (no diálogo de alterações recentes) dentro de um período de tempo. Se esta opção for marcada, as notas serão apagadas imediatamente e não será possível restaurá-las.",
"erase_notes_warning": "Apagar notas permanentemente (não pode ser desfeito), incluindo todos os clones. Isso forçará o recarregamento do aplicativo.",
"notes_to_be_deleted": "As seguintes notas serão excluídas ({{- noteCount}})",
"no_note_to_delete": "Nenhuma nota será excluída (apenas os clones).",
"broken_relations_to_be_deleted": "As seguintes relações serão quebradas e excluídas ({{- relationCount}})",
"cancel": "Cancelar",
"ok": "OK",
"deleted_relation_text": "A nota {{- note}} (a ser excluída) está referenciada pela relação {{- relation}} originada de {{- source}}."
},
"export": {
"export_note_title": "Exportar nota",
"close": "Fechar",
"export_type_subtree": "Esta nota e todos os seus descendentes",
"format_html": "HTML recomendado, pois mantém toda a formatação",
"format_html_zip": "HTML em arquivo ZIP recomendado, pois isso preserva toda a formatação.",
"format_markdown": "Markdown isso preserva a maior parte da formatação.",
"format_opml": "OPML - formato de intercâmbio de outliners apenas para texto. Formatação, imagens e arquivos não estão incluídos.",
"opml_version_1": "OPML v1.0 apenas texto simples",
"opml_version_2": "OPML v2.0 permite também HTML",
"export_type_single": "Apenas esta nota, sem seus descendentes",
"export": "Exportar",
"choose_export_type": "Por favor, escolha primeiro o tipo de exportação",
"export_status": "Status da exportação",
"export_in_progress": "Exportação em andamento: {{progressCount}}",
"export_finished_successfully": "Exportação concluída com sucesso.",
"format_pdf": "PDF para impressão ou compartilhamento."
},
"help": {
"fullDocumentation": "Ajuda (a documentação completa está disponível <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
"close": "Fechar",
"noteNavigation": "Navegação de notas",
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> subir/descer na lista de notas",
"collapseExpand": "<kbd>ESQUERDA</kbd>, <kbd>DIREITA</kbd> recolher/expandir nó",
"notSet": "não definido",
"goBackForwards": "voltar / avançar no histórico",
"showJumpToNoteDialog": "mostrar diálogo <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Ir para\"</a>",
"scrollToActiveNote": "rolar até a nota atual",
"jumpToParentNote": "<kbd>Backspace</kbd> ir para a nota pai",
"collapseWholeTree": "recolher toda a árvore de notas",
"collapseSubTree": "recolher subárvore",
"tabShortcuts": "Atalhos de abas",
"newTabNoteLink": "<kbd>Ctrl+clique</kbd> (ou <kbd>clique com o botão do meio do mouse</kbd>) em um link de nota abre a nota em uma nova aba",
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+clique</kbd> (ou <kbd>Shift+clique com o botão do meio do mouse</kbd>) em um link de nota abre e ativa a nota em uma nova aba",
"onlyInDesktop": "Apenas na versão para desktop (compilação Electron)",
"openEmptyTab": "abrir aba vazia",
"closeActiveTab": "fechar aba ativa",
"activateNextTab": "ativar próxima aba",
"activatePreviousTab": "ativar aba anterior",
"creatingNotes": "Criando notas",
"createNoteAfter": "criar nova nota após a nota atual",
"createNoteInto": "criar nova subnota dentro da nota atual",
"editBranchPrefix": "editar <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/tree-concepts.html#prefix\">prefixo</a> do clone da nota ativa",
"movingCloningNotes": "Movendo / clonando notas",
"moveNoteUpDown": "mover nota para cima/baixo na lista de notas",
"moveNoteUpHierarchy": "mover nota para cima na hierarquia",
"multiSelectNote": "selecionar múltiplas notas acima/abaixo",
"selectAllNotes": "selecionar todas as notas no nível atual",
"selectNote": "<kbd>Shift+clique</kbd> - selecionar nota",
"copyNotes": "copiar nota ativa (ou seleção atual) para a área de transferência (usado para <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">clonar</a>)",
"cutNotes": "recortar nota atual (ou seleção atual) para a área de transferência (usado para mover notas)",
"pasteNotes": "colar nota(s) como subnota dentro da nota ativa (o que pode ser mover ou clonar dependendo se foi copiado ou recortado para a área de transferência)",
"deleteNotes": "excluir nota / subárvore",
"editingNotes": "Edição de notas",
"editNoteTitle": "no painel de árvore, a navegação mudará do painel de árvore para o título da nota. Pressionar Enter no título da nota mudará o foco para o editor de texto. <kbd>Ctrl+.</kbd> mudará o foco de volta do editor para o painel de árvore.",
"createEditLink": "<kbd>Ctrl+K</kbd> - criar / editar link externo",
"createInternalLink": "criar link interno",
"followLink": "seguir link sob o cursor",
"insertDateTime": "inserir data e hora atual na posição do cursor",
"jumpToTreePane": "ir para a árvore de notas e rolar até a nota ativa",
"markdownAutoformat": "Autoformatação estilo Markdown",
"headings": "<code>##</code>, <code>###</code>, <code>####</code> etc. seguidos de espaço para títulos",
"bulletList": "<code>*</code> ou <code>-</code> seguidos de espaço para lista com marcadores",
"numberedList": "<code>1.</code> ou <code>1)</code> seguidos de espaço para lista numerada",
"blockQuote": "comece uma linha com <code>></code> seguido de espaço para citação em bloco",
"troubleshooting": "Solução de problemas",
"reloadFrontend": "recarregar o frontend do Trilium",
"showDevTools": "mostrar ferramentas de desenvolvedor",
"showSQLConsole": "mostrar console SQL",
"other": "Outros",
"quickSearch": "focar no campo de pesquisa rápida",
"inPageSearch": "pesquisa na página"
},
"import": {
"importIntoNote": "Importar para a nota",
"close": "Fechar",
"chooseImportFile": "Escolher arquivo para importar",
"importDescription": "O conteúdo do(s) arquivo(s) selecionado(s) será importado como nota(s) filha(s) em",
"options": "Opções",
"safeImportTooltip": "Arquivos de exportação Trilium<code> .zip</code> podem conter scripts executáveis que podem apresentar comportamentos prejudiciais. A importação segura desativará a execução automática de todos os scripts importados. Desmarque “Importação segura” apenas se o arquivo de importação contiver scripts executáveis e você confiar totalmente no conteúdo do arquivo importado.",
"safeImport": "Importação segura",
"explodeArchivesTooltip": "Se esta opção estiver marcada, o Trilium irá ler arquivos <code>.zip</code>, <code>.enex</code> e <code>.opml</code> e criar notas a partir dos arquivos contidos nesses arquivos compactados. Se estiver desmarcada, o Trilium irá anexar os próprios arquivos compactados à nota.",
"explodeArchives": "Ler conteúdos de arquivos <code>.zip</code>, <code>.enex</code> e <code>.opml</code>.",
"shrinkImagesTooltip": "<p>Se você marcar esta opção, o Trilium tentará reduzir o tamanho das imagens importadas por meio de escala e otimização, o que pode afetar a qualidade visual das imagens. Se desmarcada, as imagens serão importadas sem alterações.</p><p>Isso não se aplica a importações de arquivos <code>.zip</code> com metadados, pois presume-se que esses arquivos já estejam otimizados.</p>",
"shrinkImages": "Reduzir imagens",
"textImportedAsText": "Importar arquivos HTML, Markdown e TXT como notas de texto caso não esteja claro pelos metadados",
"codeImportedAsCode": "Importar arquivos de código reconhecidos (por exemplo, <code>.json</code>) como notas de código caso não esteja claro pelos metadados",
"replaceUnderscoresWithSpaces": "Substituir sublinhados por espaços nos nomes das notas importadas",
"import": "Importar",
"failed": "Falha na importação: {{message}}.",
"html_import_tags": {
"title": "Tags de importação HTML",
"description": "Configurar quais tags HTML devem ser preservadas ao importar notas. As tags que não estiverem nesta lista serão removidas durante a importação. Algumas tags (como 'script') são sempre removidas por motivos de segurança.",
"placeholder": "Digite as tags HTML, uma por linha",
"reset_button": "Redefinir para lista padrão"
},
"import-status": "Status da importação",
"in-progress": "Importação em andamento: {{progress}}",
"successful": "Importação concluída com sucesso."
},
"include_note": {
"dialog_title": "Incluir nota",
"close": "Fechar",
"label_note": "Nota",
"placeholder_search": "pesquisar nota pelo nome",
"box_size_prompt": "Dimensão da caixa da nota incluída:",
"box_size_small": "pequeno (~ 10 linhas)",
"box_size_medium": "médio (~ 30 linhas)",
"box_size_full": "completo (a caixa exibe o texto completo)",
"button_include": "Incluir nota <kbd>enter</kbd>"
},
"info": {
"modalTitle": "Mensagem informativa",
"closeButton": "Fechar",
"okButton": "OK"
},
"jump_to_note": {
"search_placeholder": "Pesquise uma nota pelo nome ou digite > para comandos...",
"close": "Fechar",
"search_button": "Pesquisar em texto completo <kbd>Ctrl+Enter</kbd>"
},
"markdown_import": {
"dialog_title": "Importar Markdown",
"close": "Fechar",
"modal_body_text": "Por motivos de segurança (sandbox do navegador), o JavaScript não pode acessar diretamente a área de transferência. Por favor, cole o conteúdo Markdown na área de texto abaixo e clique em Importar",
"import_button": "Importar Ctrl+Enter",
"import_success": "O conteúdo Markdown foi importado para o documento."
},
"move_to": {
"dialog_title": "Mover notas para...",
"close": "Fechar",
"notes_to_move": "Notas para mover",
"target_parent_note": "Nota pai-alvo",
"search_placeholder": "pesquisar nota pelo nome",
"move_button": "Mover para a nota selecionada <kbd>enter</kbd>",
"error_no_path": "Nenhum caminho para mover.",
"move_success_message": "As notas selecionadas foram movidas para "
},
"note_type_chooser": {
"change_path_prompt": "Alterar onde criar a nova nota:",
"search_placeholder": "buscar caminho pelo nome (valor padrão se não for preenchido)",
"modal_title": "Escolher tipo de nota",
"close": "Fechar",
"modal_body": "Escolha o tipo/modelo da nova nota:",
"templates": "Modelos:"
},
"password_not_set": {
"title": "A senha não está definida",
"close": "Fechar",
"body1": "Notas protegidas são criptografadas usando uma senha do usuário, mas a senha ainda não foi definida.",
"body2": "Para poder proteger notas, clique <a class=\"open-password-options-button\" href=\"javascript:\">aqui</a> para abrir a caixa de diálogo de Opções e definir sua senha."
},
"prompt": {
"title": "Prompt",
"close": "Fechar",
"ok": "OK <kbd>enter</kbd>",
"defaultTitle": "Prompt"
},
"protected_session_password": {
"modal_title": "Sessão Protegida",
"help_title": "Ajuda sobre notas protegidas",
"close_label": "Fechar",
"form_label": "Para prosseguir com a ação solicitada, você precisa iniciar uma sessão protegida digitando a senha:",
"start_button": "Iniciar sessão protegida <kbd>enter</kbd>"
},
"recent_changes": {
"title": "Alterações recentes",
"erase_notes_button": "Remover permanentemente as notas excluídas agora",
"close": "Fechar",
"deleted_notes_message": "As notas excluídas foram removidas permanentemente.",
"no_changes_message": "Nenhuma alteração ainda...",
"undelete_link": "Restaurar",
"confirm_undelete": "Você deseja restaurar esta nota e suas subnotas?"
},
"revisions": {
"note_revisions": "Versões da nota",
"delete_all_revisions": "Excluir todas as versões desta nota",
"delete_all_button": "Excluir todas as versões",
"help_title": "Ajuda sobre as versões da nota",
"close": "Fechar",
"revision_last_edited": "Esta versão foi editada pela última vez em {{date}}",
"confirm_delete_all": "Você quer excluir todas as versões desta nota?",
"no_revisions": "Ainda não há versões para esta nota...",
"restore_button": "Recuperar",
"confirm_restore": "Deseja restaurar esta versão? Isso irá substituir o título e o conteúdo atuais da nota por esta versão.",
"delete_button": "Excluir",
"confirm_delete": "Deseja excluir esta versão?",
"revisions_deleted": "As versões da nota foram removidas.",
"revision_restored": "A versão da nota foi restaurada.",
"revision_deleted": "A versão da nota foi excluída.",
"snapshot_interval": "Intervalo de captura das versões da nota: {{seconds}}s.",
"maximum_revisions": "Limite de capturas das versões da nota: {{number}}.",
"settings": "Configurações de versões da nota",
"download_button": "Download",
"mime": "MIME: ",
"file_size": "Tamanho do arquivo:",
"preview": "Visualizar:",
"preview_not_available": "A visualização não está disponível para este tipo de nota."
},
"sort_child_notes": {
"sort_children_by": "Ordenar notas filhas por...",
"close": "Fechar",
"sorting_criteria": "Critérios de ordenação",
"title": "título",
"date_created": "data de criação",
"date_modified": "data de modificação",
"sorting_direction": "Direção de ordenação",
"ascending": "crescente",
"descending": "decrescente",
"folders": "Pastas",
"sort_folders_at_top": "colocar pastas no topo",
"natural_sort": "Ordenação Natural",
"sort_with_respect_to_different_character_sorting": "classificar de acordo com diferentes regras de ordenação de caracteres e colação em diferentes idiomas ou regiões.",
"natural_sort_language": "Linguagem da ordenação natural",
"the_language_code_for_natural_sort": "O código do idioma para ordenação natural, por exemplo, \"zh-CN\" para chinês.",
"sort": "Ordenar <kbd>enter</kbd>"
},
"upload_attachments": {
"upload_attachments_to_note": "Enviar anexos para a nota",
"close": "Fechar",
"choose_files": "Escolher arquivos",
"files_will_be_uploaded": "Os arquivos serão enviados como anexos para",
"options": "Opções",
"shrink_images": "Reduzir imagens",
"upload": "Enviar",
"tooltip": "Se você marcar esta opção, o Trilium tentará reduzir as imagens enviadas redimensionando e otimizando, o que pode afetar a qualidade visual percebida. Se desmarcada, as imagens serão enviadas sem alterações."
},
"attribute_detail": {
"attr_detail_title": "Título Detalhado do Atributo"
"bundle-error": {
"title": "Falha para carregar o script customizado",
"message": "O script da nota com ID \"{{id}}\", intitulada \"{{title}}\", não pôde ser executado devido a:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Adicionar link",
"help_on_links": "Ajuda sobre links",
"note": "Nota",
"search_note": "pesquisar nota pelo nome",
"link_title_mirrors": "o título do link reflete o título atual da nota",
"link_title_arbitrary": "o título do link pode ser alterado livremente",
"link_title": "Titulo do link",
"button_add_link": "Adicionar link"
},
"branch_prefix": {
"prefix": "Prefixo: ",
"save": "Salvar",
"edit_branch_prefix": "Editar Prefixo do Branch",
"help_on_tree_prefix": "Ajuda sobre o prefixo da árvore de notas",
"branch_prefix_saved": "O prefixo de ramificação foi salvo."
},
"bulk_actions": {
"bulk_actions": "Ações em massa",
"affected_notes": "Notas Afetadas",
"include_descendants": "Incluir notas filhas das notas selecionadas",
"available_actions": "Ações disponíveis",
"chosen_actions": "Ações selecionadas",
"execute_bulk_actions": "Executar ações em massa",
"bulk_actions_executed": "As ações em massa foram concluídas com sucesso.",
"none_yet": "Nenhuma até agora... adicione uma ação clicando em uma das disponíveis acima.",
"labels": "Etiquetas",
"relations": "Relações",
"notes": "Notas",
"other": "Outros"
},
"clone_to": {
"clone_notes_to": "Clonar notas para...",
"help_on_links": "Ajuda sobre links",
"notes_to_clone": "Notas para clonar",
"search_for_note_by_its_name": "pesquisar nota pelo nome",
"cloned_note_prefix_title": "A nota clonada aparecerá na árvore de notas com o prefixo fornecido",
"prefix_optional": "Prefixo (opcional)",
"no_path_to_clone_to": "Nenhum caminho para clonar.",
"target_parent_note": "Nota pai-alvo",
"clone_to_selected_note": "Clonar para a nota selecionada",
"note_cloned": "A nota \"{{clonedTitle}}\" foi clonada para \"{{targetTitle}}\""
},
"ai_llm": {
"n_notes_queued_0": "{{ count }} nota enfileirada para indexação",
"n_notes_queued_1": "{{ count }} notas enfileiradas para indexação",
"n_notes_queued_2": "{{ count }} notas enfileiradas para indexação",
"notes_indexed_0": "{{ count }} nota indexada",
"notes_indexed_1": "{{ count }} notas indexadas",
"notes_indexed_2": "{{ count }} notas indexadas"
},
"confirm": {
"confirmation": "Confirmação",
"cancel": "Cancelar",
"ok": "OK",
"are_you_sure_remove_note": "Tem certeza de que deseja remover a nota '{{title}}' do mapa de relações? ",
"if_you_dont_check": "Se você não marcar isso, a nota será removida apenas do mapa de relações.",
"also_delete_note": "Também excluir a nota"
},
"delete_notes": {
"delete_notes_preview": "Excluir pré-visualização de notas",
"close": "Fechar",
"delete_all_clones_description": "Excluir também todos os clones (pode ser desfeito em alterações recentes)",
"erase_notes_description": "A exclusão normal (suave) apenas marca as notas como excluídas, permitindo que sejam recuperadas (no diálogo de alterações recentes) dentro de um período de tempo. Se esta opção for marcada, as notas serão apagadas imediatamente e não será possível restaurá-las.",
"erase_notes_warning": "Apagar notas permanentemente (não pode ser desfeito), incluindo todos os clones. Isso forçará o recarregamento do aplicativo.",
"notes_to_be_deleted": "As seguintes notas serão excluídas ({{notesCount}})",
"no_note_to_delete": "Nenhuma nota será excluída (apenas os clones).",
"broken_relations_to_be_deleted": "As seguintes relações serão quebradas e excluídas ({{ relationCount}})",
"cancel": "Cancelar",
"ok": "OK",
"deleted_relation_text": "A nota {{- note}} (a ser excluída) está referenciada pela relação {{- relation}} originada de {{- source}}."
},
"export": {
"export_note_title": "Exportar nota",
"close": "Fechar",
"export_type_subtree": "Esta nota e todos os seus descendentes",
"format_html": "HTML recomendado, pois mantém toda a formatação",
"format_html_zip": "HTML em arquivo ZIP recomendado, pois isso preserva toda a formatação.",
"format_markdown": "Markdown isso preserva a maior parte da formatação.",
"format_opml": "OPML - formato de intercâmbio de outliners apenas para texto. Formatação, imagens e arquivos não estão incluídos.",
"opml_version_1": "OPML v1.0 apenas texto simples",
"opml_version_2": "OPML v2.0 permite também HTML",
"export_type_single": "Apenas esta nota, sem seus descendentes",
"export": "Exportar",
"choose_export_type": "Por favor, escolha primeiro o tipo de exportação",
"export_status": "Status da exportação",
"export_in_progress": "Exportação em andamento: {{progressCount}}",
"export_finished_successfully": "Exportação concluída com sucesso.",
"format_pdf": "PDF para impressão ou compartilhamento."
},
"help": {
"noteNavigation": "Navegação de notas",
"goUpDown": "subir/descer na lista de notas",
"collapseExpand": "recolher/expandir nó",
"notSet": "não definido",
"goBackForwards": "voltar / avançar no histórico",
"showJumpToNoteDialog": "mostrar diálogo <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Ir para\"</a>",
"scrollToActiveNote": "rolar até a nota atual",
"jumpToParentNote": "ir para a nota pai",
"collapseWholeTree": "recolher toda a árvore de notas",
"collapseSubTree": "recolher subárvore",
"tabShortcuts": "Atalhos de abas",
"newTabNoteLink": "em um link de nota abre a nota em uma nova aba",
"newTabWithActivationNoteLink": "em um link de nota abre e ativa a nota em uma nova aba",
"onlyInDesktop": "Apenas na versão para desktop (compilação Electron)",
"openEmptyTab": "abrir aba vazia",
"closeActiveTab": "fechar aba ativa",
"activateNextTab": "ativar próxima aba",
"activatePreviousTab": "ativar aba anterior",
"creatingNotes": "Criando notas",
"createNoteAfter": "criar nova nota após a nota atual",
"createNoteInto": "criar nova subnota dentro da nota atual",
"editBranchPrefix": "editar <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/tree-concepts.html#prefix\">prefixo</a> do clone da nota ativa",
"movingCloningNotes": "Movendo / clonando notas",
"moveNoteUpDown": "mover nota para cima/baixo na lista de notas",
"moveNoteUpHierarchy": "mover nota para cima na hierarquia",
"multiSelectNote": "selecionar múltiplas notas acima/abaixo",
"selectAllNotes": "selecionar todas as notas no nível atual",
"selectNote": "selecionar nota",
"copyNotes": "copiar nota ativa (ou seleção atual) para a área de transferência (usado para <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">clonar</a>)",
"cutNotes": "recortar nota atual (ou seleção atual) para a área de transferência (usado para mover notas)",
"pasteNotes": "colar nota(s) como subnota dentro da nota ativa (o que pode ser mover ou clonar dependendo se foi copiado ou recortado para a área de transferência)",
"deleteNotes": "excluir nota / subárvore",
"editingNotes": "Edição de notas",
"editNoteTitle": "no painel de árvore, a navegação mudará do painel de árvore para o título da nota. Pressionar Enter no título da nota mudará o foco para o editor de texto. <kbd>Ctrl+.</kbd> mudará o foco de volta do editor para o painel de árvore.",
"createEditLink": "criar / editar link externo",
"createInternalLink": "criar link interno",
"followLink": "seguir link sob o cursor",
"insertDateTime": "inserir data e hora atual na posição do cursor",
"jumpToTreePane": "ir para a árvore de notas e rolar até a nota ativa",
"markdownAutoformat": "Autoformatação estilo Markdown",
"headings": "<code>##</code>, <code>###</code>, <code>####</code> etc. seguidos de espaço para títulos",
"bulletList": "<code>*</code> ou <code>-</code> seguidos de espaço para lista com marcadores",
"numberedList": "<code>1.</code> ou <code>1)</code> seguidos de espaço para lista numerada",
"blockQuote": "comece uma linha com <code>></code> seguido de espaço para citação em bloco",
"troubleshooting": "Solução de problemas",
"reloadFrontend": "recarregar o frontend do Trilium",
"showDevTools": "mostrar ferramentas de desenvolvedor",
"showSQLConsole": "mostrar console SQL",
"other": "Outros",
"quickSearch": "focar no campo de pesquisa rápida",
"inPageSearch": "pesquisa na página",
"title": "Folha de Dicas"
},
"import": {
"importIntoNote": "Importar para a nota",
"chooseImportFile": "Escolher arquivo para importar",
"importDescription": "O conteúdo do(s) arquivo(s) selecionado(s) será importado como nota(s) filha(s) em",
"options": "Opções",
"safeImportTooltip": "Arquivos de exportação Trilium<code> .zip</code> podem conter scripts executáveis que podem apresentar comportamentos prejudiciais. A importação segura desativará a execução automática de todos os scripts importados. Desmarque “Importação segura” apenas se o arquivo de importação contiver scripts executáveis e você confiar totalmente no conteúdo do arquivo importado.",
"safeImport": "Importação segura",
"explodeArchivesTooltip": "Se esta opção estiver marcada, o Trilium irá ler arquivos <code>.zip</code>, <code>.enex</code> e <code>.opml</code> e criar notas a partir dos arquivos contidos nesses arquivos compactados. Se estiver desmarcada, o Trilium irá anexar os próprios arquivos compactados à nota.",
"explodeArchives": "Ler conteúdos de arquivos <code>.zip</code>, <code>.enex</code> e <code>.opml</code>.",
"shrinkImagesTooltip": "<p>Se você marcar esta opção, o Trilium tentará reduzir o tamanho das imagens importadas por meio de escala e otimização, o que pode afetar a qualidade visual das imagens. Se desmarcada, as imagens serão importadas sem alterações.</p><p>Isso não se aplica a importações de arquivos <code>.zip</code> com metadados, pois presume-se que esses arquivos já estejam otimizados.</p>",
"shrinkImages": "Reduzir imagens",
"textImportedAsText": "Importar arquivos HTML, Markdown e TXT como notas de texto caso não esteja claro pelos metadados",
"codeImportedAsCode": "Importar arquivos de código reconhecidos (por exemplo, <code>.json</code>) como notas de código caso não esteja claro pelos metadados",
"replaceUnderscoresWithSpaces": "Substituir sublinhados por espaços nos nomes das notas importadas",
"import": "Importar",
"failed": "Falha na importação: {{message}}.",
"html_import_tags": {
"title": "Tags de importação HTML",
"description": "Configurar quais tags HTML devem ser preservadas ao importar notas. As tags que não estiverem nesta lista serão removidas durante a importação. Algumas tags (como 'script') são sempre removidas por motivos de segurança.",
"placeholder": "Digite as tags HTML, uma por linha",
"reset_button": "Redefinir para lista padrão"
},
"import-status": "Status da importação",
"in-progress": "Importação em andamento: {{progress}}",
"successful": "Importação concluída com sucesso."
},
"include_note": {
"dialog_title": "Incluir nota",
"label_note": "Nota",
"placeholder_search": "pesquisar nota pelo nome",
"box_size_prompt": "Dimensão da caixa da nota incluída:",
"box_size_small": "pequeno (~ 10 linhas)",
"box_size_medium": "médio (~ 30 linhas)",
"box_size_full": "completo (a caixa exibe o texto completo)",
"button_include": "Incluir nota"
},
"info": {
"modalTitle": "Mensagem informativa",
"closeButton": "Fechar",
"okButton": "OK"
},
"jump_to_note": {
"search_placeholder": "Pesquise uma nota pelo nome ou digite > para comandos...",
"search_button": "Pesquisar em texto completo"
},
"markdown_import": {
"dialog_title": "Importar Markdown",
"modal_body_text": "Por motivos de segurança (sandbox do navegador), o JavaScript não pode acessar diretamente a área de transferência. Por favor, cole o conteúdo Markdown na área de texto abaixo e clique em Importar",
"import_button": "Importar",
"import_success": "O conteúdo Markdown foi importado para o documento."
},
"move_to": {
"dialog_title": "Mover notas para...",
"notes_to_move": "Notas para mover",
"target_parent_note": "Nota pai-alvo",
"search_placeholder": "pesquisar nota pelo nome",
"move_button": "Mover para a nota selecionada",
"error_no_path": "Nenhum caminho para mover.",
"move_success_message": "As notas selecionadas foram movidas para "
},
"note_type_chooser": {
"change_path_prompt": "Alterar onde criar a nova nota:",
"search_placeholder": "buscar caminho pelo nome (valor padrão se não for preenchido)",
"modal_title": "Escolher tipo de nota",
"modal_body": "Escolha o tipo/modelo da nova nota:",
"templates": "Modelos",
"builtin_templates": "Modelos Incorporados"
},
"password_not_set": {
"title": "A senha não está definida",
"body1": "Notas protegidas são criptografadas usando uma senha do usuário, mas a senha ainda não foi definida.",
"body2": "Para poder proteger notas, clique no botão abaixo para abrir a caixa de diálogo de Opções e definir sua senha.",
"go_to_password_options": "Ir para opções de Senha"
},
"prompt": {
"title": "Prompt",
"ok": "OK",
"defaultTitle": "Prompt"
},
"protected_session_password": {
"modal_title": "Sessão Protegida",
"help_title": "Ajuda sobre notas protegidas",
"close_label": "Fechar",
"form_label": "Para prosseguir com a ação solicitada, você precisa iniciar uma sessão protegida digitando a senha:",
"start_button": "Iniciar sessão protegida"
},
"recent_changes": {
"title": "Alterações recentes",
"erase_notes_button": "Remover permanentemente as notas excluídas agora",
"deleted_notes_message": "As notas excluídas foram removidas permanentemente.",
"no_changes_message": "Nenhuma alteração ainda...",
"undelete_link": "Restaurar",
"confirm_undelete": "Você deseja restaurar esta nota e suas subnotas?"
},
"revisions": {
"note_revisions": "Versões da nota",
"delete_all_revisions": "Excluir todas as versões desta nota",
"delete_all_button": "Excluir todas as versões",
"help_title": "Ajuda sobre as versões da nota",
"revision_last_edited": "Esta versão foi editada pela última vez em {{date}}",
"confirm_delete_all": "Você quer excluir todas as versões desta nota?",
"no_revisions": "Ainda não há versões para esta nota...",
"restore_button": "Recuperar",
"confirm_restore": "Deseja restaurar esta versão? Isso irá substituir o título e o conteúdo atuais da nota por esta versão.",
"delete_button": "Excluir",
"confirm_delete": "Deseja excluir esta versão?",
"revisions_deleted": "As versões da nota foram removidas.",
"revision_restored": "A versão da nota foi restaurada.",
"revision_deleted": "A versão da nota foi excluída.",
"snapshot_interval": "Intervalo de captura das versões da nota: {{seconds}}s.",
"maximum_revisions": "Limite de capturas das versões da nota: {{number}}.",
"settings": "Configurações de versões da nota",
"download_button": "Download",
"mime": "MIME: ",
"file_size": "Tamanho do arquivo:",
"preview": "Visualizar:",
"preview_not_available": "A visualização não está disponível para este tipo de nota."
},
"sort_child_notes": {
"sort_children_by": "Ordenar notas filhas por...",
"sorting_criteria": "Critérios de ordenação",
"title": "título",
"date_created": "data de criação",
"date_modified": "data de modificação",
"sorting_direction": "Direção de ordenação",
"ascending": "crescente",
"descending": "decrescente",
"folders": "Pastas",
"sort_folders_at_top": "colocar pastas no topo",
"natural_sort": "Ordenação Natural",
"sort_with_respect_to_different_character_sorting": "classificar de acordo com diferentes regras de ordenação de caracteres e colação em diferentes idiomas ou regiões.",
"natural_sort_language": "Linguagem da ordenação natural",
"the_language_code_for_natural_sort": "O código do idioma para ordenação natural, por exemplo, \"zh-CN\" para chinês.",
"sort": "Ordenar"
},
"upload_attachments": {
"upload_attachments_to_note": "Enviar anexos para a nota",
"choose_files": "Escolher arquivos",
"files_will_be_uploaded": "Os arquivos serão enviados como anexos para {{noteTitle}}",
"options": "Opções",
"shrink_images": "Reduzir imagens",
"upload": "Enviar",
"tooltip": "Se você marcar esta opção, o Trilium tentará reduzir as imagens enviadas redimensionando e otimizando, o que pode afetar a qualidade visual percebida. Se desmarcada, as imagens serão enviadas sem alterações."
},
"attribute_detail": {
"attr_detail_title": "Título Detalhado do Atributo",
"close_button_title": "Cancelar alterações e fechar",
"attr_is_owned_by": "O atributo pertence a",
"attr_name_title": "O nome do atributo pode ser composto apenas por caracteres alfanuméricos, dois-pontos e sublinhado",
"name": "Nome",
"value": "Valor",
"target_note_title": "Relação é uma conexão nomeada entre a nota de origem e a nota de destino.",
"target_note": "Nota de destino",
"promoted_title": "O atributo promovido é exibido de forma destacada na nota.",
"promoted": "Promovido",
"promoted_alias_title": "O nome a ser exibido na interface de atributos promovidos.",
"promoted_alias": "Alias",
"multiplicity_title": "Multiplicidade define quantos atributos com o mesmo nome podem ser criados — no máximo 1 ou mais de 1.",
"multiplicity": "Multiplicidade",
"single_value": "Valor único",
"multi_value": "Valor múltiplo",
"label_type_title": "O tipo do rótulo ajudará o Trilium a escolher a interface adequada para inserir o valor do rótulo.",
"label_type": "Tipo",
"text": "Texto",
"number": "Número",
"boolean": "Booleano",
"date": "Data",
"date_time": "Data e Hora",
"time": "Hora",
"url": "URL",
"precision_title": "Qual número de dígitos após o ponto decimal deve estar disponível na interface de configuração de valor.",
"precision": "Precisão",
"digits": "dígitos",
"inverse_relation_title": "Configuração opcional para definir a qual relação esta é oposta. Exemplo: Pai - Filho são relações inversas entre si.",
"inverse_relation": "Relação inversa",
"inheritable_title": "O atributo herdável será transmitido para todos os descendentes deste ramo.",
"inheritable": "Herdável",
"save_and_close": "Salvar e fechar <kbd>Ctrl+Enter</kbd>",
"delete": "Excluir",
"related_notes_title": "Outras notas com este rótulo",
"more_notes": "Mais notas",
"label": "Detalhe do rótulo",
"label_definition": "Detalhe da definição do rótulo",
"relation": "Detalhe da relação",
"relation_definition": "Detalhe da definição da relação",
"disable_versioning": "desativa a versão automática. Útil, por exemplo, para notas grandes, mas sem importância como grandes bibliotecas JS usadas para scripts",
"calendar_root": "marca a nota que deve ser usada como raiz para notas diárias. Apenas uma deve ser marcada assim.",
"archived": "notas com este rótulo não serão exibidas por padrão nos resultados de busca (também nos diálogos Ir para, Adicionar link, etc).",
"exclude_from_export": "notas (junto com sua subárvore) não serão incluídas em nenhuma exportação de notas",
"run": "define em quais eventos o script deve ser executado. Os valores possíveis são:\n<ul>\n<li>frontendStartup - quando o frontend do Trilium inicia (ou é atualizado), mas não no celular.</li>\n<li>mobileStartup - quando o frontend do Trilium inicia (ou é atualizado), no celular.</li>\n<li>backendStartup - quando o backend do Trilium inicia</li>\n<li>hourly - executa uma vez por hora. Você pode usar o rótulo adicional <code>runAtHour</code> para especificar em qual hora.</li>\n<li>daily - executa uma vez por dia</li>\n</ul>",
"run_on_instance": "Define em qual instância do Trilium isso deve ser executado. Por padrão, todas as instâncias.",
"run_at_hour": "Em qual hora isso deve ser executado. Deve ser usado junto com <code>#run=hourly</code>. Pode ser definido várias vezes para executar mais de uma vez ao dia.",
"disable_inclusion": "scripts com este rótulo não serão incluídos na execução do script pai.",
"sorted": "mantém as notas filhas ordenadas alfabeticamente pelo título",
"sort_direction": "ASC (padrão) ou DESC",
"sort_folders_first": "Pastas (notas com filhos) devem ser ordenadas no topo",
"top": "mantenha a nota fornecida no topo em seu pai (aplica-se apenas a pais ordenados)",
"hide_promoted_attributes": "Ocultar atributos promovidos nesta nota",
"read_only": "o editor está em modo somente leitura. Funciona apenas para notas de texto e código.",
"auto_read_only_disabled": "notas de texto/código podem ser automaticamente configuradas para modo de leitura quando são muito grandes. Você pode desabilitar esse comportamento por nota adicionando este rótulo à nota",
"app_css": "marca notas CSS que são carregadas no aplicativo Trilium e, portanto, podem ser usadas para modificar a aparência do Trilium.",
"app_theme": "marca notas CSS que são temas completos do Trilium e, portanto, estão disponíveis nas opções do Trilium.",
"app_theme_base": "defina como \"next\", \"next-light\" ou \"next-dark\" para usar o tema TriliumNext correspondente (auto, claro ou escuro) como base para um tema personalizado, em vez do tema legado.",
"css_class": "o valor deste rótulo é então adicionado como classe CSS ao nó que representa a nota específica na árvore. Isso pode ser útil para temas avançados. Pode ser usado em notas de modelo.",
"icon_class": "o valor deste rótulo é adicionado como uma classe CSS ao ícone na árvore, o que pode ajudar a distinguir visualmente as notas na árvore. Um exemplo pode ser bx bx-home os ícones são retirados do boxicons. Pode ser usado em notas de modelo.",
"page_size": "número de itens por página na listagem de notas",
"custom_request_handler": "veja <a href=\"javascript:\" data-help-page=\"custom-request-handler.html\">Manipulador de requisição personalizada</a>",
"custom_resource_provider": "veja <a href=\"javascript:\" data-help-page=\"custom-request-handler.html\">Manipulador de requisição personalizada</a>",
"widget": "marca esta nota como um widget personalizado que será adicionado à árvore de componentes do Trilium",
"workspace": "marca esta nota como um espaço de trabalho, o que permite fácil hoisting",
"workspace_icon_class": "define a classe CSS do ícone box que será usada na aba quando esta nota for hoisted",
"workspace_tab_background_color": "cor CSS usada na aba da nota quando esta nota é hoisted",
"workspace_calendar_root": "Define a raiz do calendário por espaço de trabalho",
"workspace_template": "Esta nota aparecerá na seleção de modelos disponíveis ao criar uma nova nota, mas apenas quando estiver destacada em um espaço de trabalho que contenha este modelo",
"search_home": "novas notas de pesquisa serão criadas como filhas desta nota",
"workspace_search_home": "novas notas de pesquisa serão criadas como filhas desta nota quando ela for destacada para algum ancestral desta nota de área de trabalho",
"inbox": "localização padrão da caixa de entrada para novas notas quando você cria uma nota usando o botão \"nova nota\" na barra lateral, as notas serão criadas como notas filhas na nota marcada com o rótulo <code>#inbox</code>.",
"workspace_inbox": "local padrão da caixa de entrada para novas notas quando esta nota for destacada para algum ancestral desta nota de área de trabalho",
"sql_console_home": "localização padrão das notas do console SQL",
"bookmark_folder": "nota com este rótulo aparecerá nos favoritos como uma pasta (permitindo acesso aos seus filhos)",
"share_hidden_from_tree": "esta nota está oculta na árvore de navegação à esquerda, mas ainda pode ser acessada via sua URL",
"share_external_link": "a nota funcionará como um link para um site externo na árvore de compartilhamento",
"share_alias": "defina um alias por meio do qual a nota ficará disponível em https://your_trilium_host/share/[your_alias]",
"share_omit_default_css": "o CSS padrão da página de compartilhamento será omitido. Use quando você fizer alterações extensas de estilo.",
"share_root": "marca a nota que é servida na raiz de /share.",
"share_description": "defina o texto a ser adicionado à meta tag HTML \"description\"",
"share_raw": "a nota será servida em seu formato bruto, sem o wrapper HTML",
"share_disallow_robot_indexing": "impedirá que robôs indexem esta nota por meio do cabeçalho <code>X-Robots-Tag: noindex</code>",
"share_credentials": "exigir credenciais para acessar esta nota compartilhada. O valor deve estar no formato 'usuário:senha'. Não se esqueça de tornar esta configuração herdável para que seja aplicada às notas-filhas/imagens.",
"share_index": "notas com este rótulo irão listar todas as raízes das notas compartilhadas",
"display_relations": "nomes das relações separados por vírgula que devem ser exibidos. Todas as outras serão ocultadas.",
"hide_relations": "nomes das relações separados por vírgula que devem ser ocultados. Todas as outras serão exibidas.",
"title_template": "Título padrão das notas criadas como filhas desta nota. O valor é avaliado como uma string JavaScript e pode ser enriquecido com conteúdo dinâmico usando as variáveis injetadas <code>now</code> e <code>parentNote</code>. Exemplos:\n\n<ul>\n <li><code>${parentNote.getLabelValue('authorName')}'s literary works</code></li>\n <li><code>Log for ${now.format('YYYY-MM-DD HH:mm:ss')}</code></li>\n</ul>\n\nVeja a <a href=\"https://triliumnext.github.io/Docs/Wiki/default-note-title.html\">wiki com detalhes</a>, a documentação da API para <a href=\"https://zadam.github.io/trilium/backend_api/Note.html\">parentNote</a> e para <a href=\"https://day.js.org/docs/en/display/format\">now</a> para mais informações.",
"template": "Esta nota aparecerá na seleção de modelos disponíveis ao criar uma nova nota",
"toc": "<code>#toc</code> ou <code>#toc=show</code> irá forçar a exibição do Sumário, <code>#toc=hide</code> irá forçar que ele fique oculto. Se o rótulo não existir, será considerado o ajuste global",
"color": "define a cor da nota na árvore de notas, links etc. Use qualquer valor de cor CSS válido, como 'red' ou #a13d5f",
"keyboard_shortcut": "Define um atalho de teclado que irá pular imediatamente para esta nota. Exemplo: 'ctrl+alt+e'. É necessário recarregar o frontend para que a alteração tenha efeito."
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
{
"about": {
"title": "Podrobnosti Trilium Notes",
"homepage": "Domača stran:",
"app_version": "Verzija aplikacije:",
"db_version": "Verzija DB:",
"sync_version": "Verzija Sync:"
}
}

View File

@@ -1,7 +1,6 @@
{
"about": {
"title": "O Trilium Belеškama",
"close": "Zatvori",
"homepage": "Početna stranica:",
"app_version": "Verzija aplikacije:",
"db_version": "Verzija baze podataka:",
@@ -28,7 +27,6 @@
"add_link": {
"add_link": "Dodaj link",
"help_on_links": "Pomoć na linkovima",
"close": "Zatvori",
"note": "Beleška",
"search_note": "potražite belešku po njenom imenu",
"link_title_mirrors": "naziv linka preslikava trenutan naziv beleške",
@@ -39,14 +37,12 @@
"branch_prefix": {
"edit_branch_prefix": "Izmeni prefiks grane",
"help_on_tree_prefix": "Pomoć na prefiksu Drveta",
"close": "Zatvori",
"prefix": "Prefiks: ",
"save": "Sačuvaj",
"branch_prefix_saved": "Prefiks grane je sačuvan."
},
"bulk_actions": {
"bulk_actions": "Grupne akcije",
"close": "Zatvori",
"affected_notes": "Pogođene beleške",
"include_descendants": "Obuhvati potomke izabranih beleški",
"available_actions": "Dostupne akcije",
@@ -61,7 +57,6 @@
},
"clone_to": {
"clone_notes_to": "Klonirajte beleške u...",
"close": "Zatvori",
"help_on_links": "Pomoć na linkovima",
"notes_to_clone": "Beleške za kloniranje",
"target_parent_note": "Ciljna nadređena beleška",
@@ -74,7 +69,6 @@
},
"confirm": {
"confirmation": "Potvrda",
"close": "Zatvori",
"cancel": "Otkaži",
"ok": "U redu",
"are_you_sure_remove_note": "Da li ste sigurni da želite da uklonite belešku \"{{title}}\" iz mape odnosa? ",
@@ -113,8 +107,6 @@
"format_pdf": "PDF - za namene štampanja ili deljenja."
},
"help": {
"fullDocumentation": "Pomoć (puna dokumentacija je dostupna <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
"close": "Zatvori",
"noteNavigation": "Navigacija beleški",
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> - kretanje gore/dole u listi sa beleškama",
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - sakupi/proširi čvor",
@@ -122,12 +114,12 @@
"goBackForwards": "idi u nazad/napred kroz istoriju",
"showJumpToNoteDialog": "prikaži <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Idi na\" dijalog</a>",
"scrollToActiveNote": "skroluj do aktivne beleške",
"jumpToParentNote": "<kbd>Backspace</kbd> - idi do nadređene beleške",
"jumpToParentNote": "idi do nadređene beleške",
"collapseWholeTree": "sakupi celo drvo beleški",
"collapseSubTree": "sakupi pod-drvo",
"tabShortcuts": "Prečice na karticama",
"newTabNoteLink": "<kbd>Ctrl+click</kbd> - (ili <kbd>middle mouse click</kbd>) na link beleške otvara belešku u novoj kartici",
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+click</kbd> - (ili <kbd>Shift+middle mouse click</kbd>) na link beleške otvara i aktivira belešku u novoj kartici",
"newTabNoteLink": "na link beleške otvara belešku u novoj kartici",
"newTabWithActivationNoteLink": "na link beleške otvara i aktivira belešku u novoj kartici",
"onlyInDesktop": "Samo na dektop-u (Electron verzija)",
"openEmptyTab": "otvori praznu karticu",
"closeActiveTab": "zatvori aktivnu karticu",
@@ -142,14 +134,14 @@
"moveNoteUpHierarchy": "pomeri belešku na gore u hijerarhiji",
"multiSelectNote": "višestruki izbor beleški iznad/ispod",
"selectAllNotes": "izaberi sve beleške u trenutnom nivou",
"selectNote": "<kbd>Shift+click</kbd> - izaberi belešku",
"selectNote": "izaberi belešku",
"copyNotes": "kopiraj aktivnu belešku (ili trenutni izbor) u privremenu memoriju (koristi se za <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">kloniranje</a>)",
"cutNotes": "iseci trenutnu belešku (ili trenutni izbor) u privremenu memoriju (koristi se za premeštanje beleški)",
"pasteNotes": "nalepi belešku/e kao podbelešku u aktivnoj belešci (koja se ili premešta ili klonira u zavisnosti od toga da li je beleška kopirana ili isečena u privremenu memoriju)",
"deleteNotes": "obriši belešku / podstablo",
"editingNotes": "Izmena beleški",
"editNoteTitle": "u ravni drveta će se prebaciti sa ravni drveta na naslov beleške. Ulaz sa naslova beleške će prebaciti fokus na uređivač teksta. <kbd>Ctrl+.</kbd> će se vratiti sa uređivača na ravan drveta.",
"createEditLink": "<kbd>Ctrl+K</kbd> - napravi / izmeni spoljašnji link",
"createEditLink": "napravi / izmeni spoljašnji link",
"createInternalLink": "napravi unutrašnji link",
"followLink": "prati link ispod kursora",
"insertDateTime": "ubaci trenutan datum i vreme na poziciju kursora",
@@ -169,7 +161,6 @@
},
"import": {
"importIntoNote": "Uvezi u belešku",
"close": "Zatvori",
"chooseImportFile": "Izaberi datoteku za uvoz",
"importDescription": "Sadržaj izabranih datoteka će biti uvezen kao podbeleške u",
"options": "Opcije",
@@ -196,14 +187,13 @@
},
"include_note": {
"dialog_title": "Uključi belešku",
"close": "Zatvori",
"label_note": "Beleška",
"placeholder_search": "pretraži belešku po njenom imenu",
"box_size_prompt": "Veličina kutije priložene beleške:",
"box_size_small": "mala (~ 10 redova)",
"box_size_medium": "srednja (~ 30 redova)",
"box_size_full": "puna (kutija prikazuje ceo tekst)",
"button_include": "Uključi belešku <kbd>enter</kbd>"
"button_include": "Uključi belešku"
},
"info": {
"modalTitle": "Informativna poruka",
@@ -212,23 +202,20 @@
},
"jump_to_note": {
"search_placeholder": "Pretraži belešku po njenom imenu ili unesi > za komande...",
"close": "Zatvori",
"search_button": "Pretraga u punom tekstu <kbd>Ctrl+Enter</kbd>"
},
"markdown_import": {
"dialog_title": "Uvoz za Markdown",
"close": "Zatvori",
"modal_body_text": "Zbog Sandbox-a pretraživača nije moguće direktno učitati privremenu memoriju iz JavaScript-a. Molimo vas da nalepite Markdown za uvoz u tekstualno polje ispod i kliknete na dugme za uvoz",
"import_button": "Uvoz Ctrl+Enter",
"import_button": "Uvoz",
"import_success": "Markdown sadržaj je učitan u dokument."
},
"move_to": {
"dialog_title": "Premesti beleške u ...",
"close": "Zatvori",
"notes_to_move": "Beleške za premeštanje",
"target_parent_note": "Ciljana nadbeleška",
"search_placeholder": "potraži belešku po njenom imenu",
"move_button": "Pređi na izabranu belešku <kbd>enter</kbd>",
"move_button": "Pređi na izabranu belešku",
"error_no_path": "Nema putanje za premeštanje.",
"move_success_message": "Izabrane beleške su premeštene u "
},
@@ -236,19 +223,16 @@
"change_path_prompt": "Promenite gde će se napraviti nova beleška:",
"search_placeholder": "pretraži putanju po njenom imenu (podrazumevano ako je prazno)",
"modal_title": "Izaberite tip beleške",
"close": "Zatvori",
"modal_body": "Izaberite tip beleške / šablon za novu belešku:",
"templates": "Šabloni:"
"templates": "Šabloni"
},
"password_not_set": {
"title": "Lozinka nije podešena",
"close": "Zatvori",
"body1": "Zaštićene beleške su enkriptovane sa korisničkom lozinkom, ali lozinka još uvek nije podešena.",
"body2": "Za biste mogli da sačuvate beleške, kliknite <a class=\"open-password-options-button\" href=\"javascript:\">ovde</a> da otvorite dijalog sa Opcijama i podesite svoju lozinku."
},
"prompt": {
"title": "Upit",
"close": "Zatvori",
"ok": "U redu <kbd>enter</kbd>",
"defaultTitle": "Upit"
},
@@ -257,12 +241,11 @@
"help_title": "Pomoć za Zaštićene beleške",
"close_label": "Zatvori",
"form_label": "Da biste nastavili sa traženom akcijom moraćete započeti zaštićenu sesiju tako što ćete uneti lozinku:",
"start_button": "Započni zaštićenu sesiju <kbd>enter</kbd>"
"start_button": "Započni zaštićenu sesiju"
},
"recent_changes": {
"title": "Nedavne promene",
"erase_notes_button": "Obriši izabrane beleške odmah",
"close": "Zatvori",
"deleted_notes_message": "Obrisane beleške su uklonjene.",
"no_changes_message": "Još uvek nema izmena...",
"undelete_link": "poništi brisanje",
@@ -273,7 +256,6 @@
"delete_all_revisions": "Obriši sve revizije ove beleške",
"delete_all_button": "Obriši sve revizije",
"help_title": "Pomoć za Revizije beleški",
"close": "Zatvori",
"revision_last_edited": "Ova revizija je poslednji put izmenjena {{date}}",
"confirm_delete_all": "Da li želite da obrišete sve revizije ove beleške?",
"no_revisions": "Još uvek nema revizija za ovu belešku...",
@@ -295,7 +277,6 @@
},
"sort_child_notes": {
"sort_children_by": "Sortiranje podbeleški po...",
"close": "Zatvori",
"sorting_criteria": "Kriterijum za sortiranje",
"title": "naslov",
"date_created": "datum kreiranja",
@@ -309,13 +290,12 @@
"sort_with_respect_to_different_character_sorting": "sortiranje sa poštovanjem različitih pravila sortiranja karaktera i kolacija u različitim jezicima ili regionima.",
"natural_sort_language": "Jezik za prirodno sortiranje",
"the_language_code_for_natural_sort": "Kod jezika za prirodno sortiranje, npr. \"zh-CN\" za Kineski.",
"sort": "Sortiraj <kbd>enter</kbd>"
"sort": "Sortiraj"
},
"upload_attachments": {
"upload_attachments_to_note": "Otpremite priloge uz belešku",
"close": "Zatvori",
"choose_files": "Izaberite datoteke",
"files_will_be_uploaded": "Datoteke će biti otpremljene kao prilozi u",
"files_will_be_uploaded": "Datoteke će biti otpremljene kao prilozi u {{noteTitle}}",
"options": "Opcije",
"shrink_images": "Smanji slike",
"upload": "Otpremi",

View File

@@ -0,0 +1,26 @@
{
"about": {
"homepage": "Giriş sayfası:",
"app_version": "Uygulama versiyonu:",
"db_version": "Veritabanı versiyonu:"
},
"branch_prefix": {
"save": "Kaydet"
},
"delete_notes": {
"close": "Kapat"
},
"export": {
"close": "Kapat"
},
"import": {
"chooseImportFile": "İçe aktarım dosyası",
"importDescription": "Seçilen dosya(lar) alt not olarak içe aktarılacaktır"
},
"info": {
"closeButton": "Kapat"
},
"protected_session_password": {
"close_label": "Kapat"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
{
"add_link": {
"add_link": "Додати посилання",
"help_on_links": "Довідка щодо посилань",
"note": "Нотатка",
"search_note": "Знайти нотатку за ім'ям",
"link_title_mirrors": "заголовок посилання відображає назву нотатки",
"link_title_arbitrary": "свій заголовок посилання",
"link_title": "Заголовок посилання",
"button_add_link": "Додати посилання"
},
"branch_prefix": {
"save": "Зберегти",
"edit_branch_prefix": "Редагувати префікс гілки",
"help_on_tree_prefix": "Довідка щодо префіксів гілок",
"prefix": "Префікс: ",
"branch_prefix_saved": "Префікс гілки збережено."
},
"about": {
"app_version": "Версія програми:",
"db_version": "Версія БД:",
"build_date": "Дата збірки:",
"build_revision": "Ревізія збірки:",
"data_directory": "Директорія даних:",
"homepage": "Домашня сторінка:",
"title": "Про Trilium Notes"
},
"global_menu": {
"about": "Про Trilium Notes"
},
"modal": {
"help_title": "Показати більше інформації про це вікно"
},
"toast": {
"critical-error": {
"title": "Критична помилка"
}
},
"bulk_actions": {
"bulk_actions": "Масові дії",
"affected_notes": "Зачеплені нотатки",
"available_actions": "Доступні дії",
"chosen_actions": "Обрані дії",
"execute_bulk_actions": "Виконати масові дії",
"bulk_actions_executed": "Масові дії успішно виконано.",
"none_yet": "Поки ніяких.. Додайте дію натиснувши на одну з наданих вище."
},
"clone_to": {
"clone_notes_to": "Клонувати нотатки до...",
"target_parent_note": "Цільова батьківська нотатка",
"search_for_note_by_its_name": "Знайти нотатку за ім'ям"
},
"clipboard": {
"copied": "Нотатку(-и) було скопійовано в буфер.",
"copy_failed": "Не вдалося скопіювати в буфер через проблеми з дозволами.",
"copy_success": "Скопійовано в буфер."
},
"entrypoints": {
"sql-error": "Виникла помилка при виконанні запиту SQL: {{message}}"
},
"branches": {
"undeleting-notes-finished-successfully": "Нотатки вдало відновлено.",
"undeleting-notes-in-progress": "Відновлюємо нотатки: {{count}}",
"delete-notes-in-progress": "Видаляємо нотатки: {{count}}",
"delete-finished-successfully": "Нотатки вдало видалено."
},
"launcher_context_menu": {
"add-spacer": "Додати розділювач",
"reset": "Скинути"
},
"editable-text": {
"auto-detect-language": "Автовизначена"
},
"highlighting": {
"color-scheme": "Схема кольорів"
},
"code_block": {
"copy_title": "Скопіювати в буфер"
},
"classic_editor_toolbar": {
"title": "Форматування"
},
"editor": {
"title": "Редактор"
},
"editing": {
"editor_type": {
"label": "Панель інструментів форматування"
}
}
}

View File

@@ -0,0 +1,77 @@
{
"about": {
"homepage": "Trang chủ:",
"title": "Về Trilium Notes"
},
"add_link": {
"add_link": "Thêm liên kết",
"button_add_link": "Thêm liên kết"
},
"bulk_actions": {
"other": "Khác"
},
"branch_prefix": {
"save": "Lưu"
},
"confirm": {
"ok": "OK",
"cancel": "Huỷ"
},
"delete_notes": {
"close": "Đóng",
"ok": "OK",
"cancel": "Huỷ"
},
"export": {
"close": "Đóng"
},
"help": {
"other": "Khác"
},
"toast": {
"critical-error": {
"title": "Lỗi nghiêm trọng"
}
},
"import": {
"options": "Tuỳ chọn"
},
"info": {
"okButton": "OK",
"closeButton": "Đóng"
},
"move_to": {
"dialog_title": "Chuyển ghi chép tới..."
},
"prompt": {
"ok": "OK"
},
"protected_session_password": {
"close_label": "Đóng"
},
"revisions": {
"restore_button": "Khôi phục",
"delete_button": "Xoá"
},
"upload_attachments": {
"options": "Tuỳ chọn"
},
"attribute_detail": {
"name": "Tên",
"value": "Giá trị",
"text": "Văn bản",
"number": "Số",
"delete": "Xoá"
},
"rename_note": {
"rename_note": "Đổi tên ghi chép"
},
"add_label": {
"add_label": "Thêm nhãn",
"label_name_placeholder": "tên nhãn",
"help_text_item2": "hoặc thay đổi giá trị của nhãn có sẵn"
},
"rename_label": {
"rename_label": "Đặt lại tên nhãn"
}
}

View File

@@ -0,0 +1,54 @@
import { ComponentChildren } from "preact";
import { memo } from "preact/compat";
import AbstractBulkAction from "./abstract_bulk_action";
interface BulkActionProps {
label: string | ComponentChildren;
children?: ComponentChildren;
helpText?: ComponentChildren;
bulkAction: AbstractBulkAction;
}
// Define styles as constants to prevent recreation
const flexContainerStyle = { display: "flex", alignItems: "center" } as const;
const labelStyle = { marginRight: "10px" } as const;
const textStyle = { marginRight: "10px", marginLeft: "10px" } as const;
const BulkAction = memo(({ label, children, helpText, bulkAction }: BulkActionProps) => {
return (
<tr>
<td colSpan={2}>
<div style={flexContainerStyle}>
<div style={labelStyle} className="text-nowrap">{label}</div>
{children}
</div>
</td>
<td className="button-column">
{helpText && <div className="dropdown help-dropdown">
<span className="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div className="dropdown-menu dropdown-menu-right p-4">
{helpText}
</div>
</div>}
<span
className="bx bx-x icon-action action-conf-del"
onClick={() => bulkAction?.deleteAction()}
/>
</td>
</tr>
);
});
export default BulkAction;
export const BulkActionText = memo(({ text }: { text: string }) => {
return (
<div
style={textStyle}
className="text-nowrap">
{text}
</div>
);
});

View File

@@ -1,10 +1,9 @@
import { t } from "../../services/i18n.js";
import server from "../../services/server.js";
import ws from "../../services/ws.js";
import utils from "../../services/utils.js";
import type FAttribute from "../../entities/fattribute.js";
import { VNode } from "preact";
interface ActionDefinition {
export interface ActionDefinition {
script: string;
relationName: string;
targetNoteId: string;
@@ -27,26 +26,9 @@ export default abstract class AbstractBulkAction {
this.actionDef = actionDef;
}
render() {
try {
const $rendered = this.doRender();
$rendered
.find(".action-conf-del")
.on("click", () => this.deleteAction())
.attr("title", t("abstract_bulk_action.remove_this_search_action"));
utils.initHelpDropdown($rendered);
return $rendered;
} catch (e: any) {
logError(`Failed rendering search action: ${JSON.stringify(this.attribute.dto)} with error: ${e.message} ${e.stack}`);
return null;
}
}
// to be overridden
abstract doRender(): JQuery<HTMLElement>;
abstract doRender(): VNode;
static get actionName() {
return "";
}
@@ -66,9 +48,6 @@ export default abstract class AbstractBulkAction {
async deleteAction() {
await server.remove(`notes/${this.attribute.noteId}/attributes/${this.attribute.attributeId}`);
await ws.waitForMaxKnownEntityChangeId();
//await this.triggerCommand('refreshSearchDefinition');
}
}

View File

@@ -1,58 +0,0 @@
import { t } from "../../services/i18n.js";
import SpacedUpdate from "../../services/spaced_update.js";
import AbstractBulkAction from "./abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td>
${t("execute_script.execute_script")}
</td>
<td>
<input type="text"
class="form-control script"
placeholder="note.title = note.title + '- suffix';"/>
</td>
<td class="button-column">
<div style="display: flex; align-items: center; justify-content: space-between;">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
${t("execute_script.help_text")}
${t("execute_script.example_1")}
<pre>note.title = note.title + ' - suffix';</pre>
${t("execute_script.example_2")}
<pre>for (const attr of note.getOwnedAttributes) { attr.markAsDeleted(); }</pre>
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</div>
</td>
</tr>`;
export default class ExecuteScriptBulkAction extends AbstractBulkAction {
static get actionName() {
return "executeScript";
}
static get actionTitle() {
return t("execute_script.execute_script");
}
doRender() {
const $action = $(TPL);
const $script = $action.find(".script");
$script.val(this.actionDef.script || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({ script: $script.val() });
}, 1000);
$script.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,50 @@
import { useEffect, useState } from "preact/hooks";
import { t } from "../../services/i18n.js";
import FormTextBox from "../react/FormTextBox.jsx";
import AbstractBulkAction, { ActionDefinition } from "./abstract_bulk_action.js";
import BulkAction from "./BulkAction.jsx";
import { useSpacedUpdate } from "../react/hooks.jsx";
function ExecuteScriptBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ script, setScript ] = useState(actionDef.script);
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ script }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ script ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("execute_script.execute_script")}
helpText={<>
{t("execute_script.help_text")}
{t("execute_script.example_1")}
<pre>note.title = note.title + ' - suffix';</pre>
{t("execute_script.example_2")}
<pre>{"for (const attr of note.getOwnedAttributes) { attr.markAsDeleted(); }"}</pre>
</>}
>
<FormTextBox
placeholder="note.title = note.title + '- suffix';"
currentValue={script} onChange={setScript}
/>
</BulkAction>
);
}
export default class ExecuteScriptBulkAction extends AbstractBulkAction {
static get actionName() {
return "executeScript";
}
static get actionTitle() {
return t("execute_script.execute_script");
}
doRender() {
return <ExecuteScriptBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,70 +0,0 @@
import { t } from "../../../services/i18n.js";
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px;" class="text-nowrap">${t("add_label.add_label")}</div>
<input type="text"
class="form-control label-name"
placeholder="${t("add_label.label_name_placeholder")}"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("add_label.label_name_title")}"/>
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("add_label.to_value")}</div>
<input type="text" class="form-control label-value" placeholder="${t("add_label.new_value_placeholder")}"/>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>${t("add_label.help_text")}</p>
<ul>
<li>${t("add_label.help_text_item1")}</li>
<li>${t("add_label.help_text_item2")}</li>
</ul>
${t("add_label.help_text_note")}
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class AddLabelBulkAction extends AbstractBulkAction {
static get actionName() {
return "addLabel";
}
static get actionTitle() {
return t("add_label.add_label");
}
doRender() {
const $action = $(TPL);
const $labelName = $action.find(".label-name");
$labelName.val(this.actionDef.labelName || "");
const $labelValue = $action.find(".label-value");
$labelValue.val(this.actionDef.labelValue || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
labelName: $labelName.val(),
labelValue: $labelValue.val()
});
}, 1000);
$labelName.on("input", () => spacedUpdate.scheduleUpdate());
$labelValue.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,57 @@
import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n";
import FormTextBox from "../../react/FormTextBox";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action";
import BulkAction, { BulkActionText } from "../BulkAction";
import { useSpacedUpdate } from "../../react/hooks";
function AddLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ labelName, setLabelName ] = useState<string>(actionDef.labelName ?? "");
const [ labelValue, setLabelValue ] = useState<string>(actionDef.labelValue ?? "");
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName, labelValue }));
useEffect(() => spacedUpdate.scheduleUpdate(), [labelName, labelValue]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("add_label.add_label")}
helpText={<>
<p>{t("add_label.help_text")}</p>
<ul>
<li>{t("add_label.help_text_item1")}</li>
<li>{t("add_label.help_text_item2")}</li>
</ul>
{t("add_label.help_text_note")}
</>}
>
<FormTextBox
placeholder={t("add_label.label_name_placeholder")}
pattern="[\\p{L}\\p{N}_:]+"
title={t("add_label.label_name_title")}
currentValue={labelName} onChange={setLabelName}
/>
<BulkActionText text={t("add_label.to_value")} />
<FormTextBox
placeholder={t("add_label.new_value_placeholder")}
currentValue={labelValue} onChange={setLabelValue}
/>
</BulkAction>
)
}
export default class AddLabelBulkAction extends AbstractBulkAction {
doRender() {
return <AddLabelBulkActionComponent bulkAction={this} actionDef={this.actionDef} />;
}
static get actionName() {
return "addLabel";
}
static get actionTitle() {
return t("add_label.add_label");
}
}

View File

@@ -1,43 +0,0 @@
import { t } from "../../../services/i18n.js";
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td>
${t("delete_label.delete_label")}
</td>
<td>
<input type="text"
class="form-control label-name"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("delete_label.label_name_title")}"
placeholder="${t("delete_label.label_name_placeholder")}"/>
</td>
<td class="button-column">
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class DeleteLabelBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteLabel";
}
static get actionTitle() {
return t("delete_label.delete_label");
}
doRender() {
const $action = $(TPL);
const $labelName = $action.find(".label-name");
$labelName.val(this.actionDef.labelName || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({ labelName: $labelName.val() });
}, 1000);
$labelName.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,39 @@
import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n.js";
import FormTextBox from "../../react/FormTextBox.jsx";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import { useSpacedUpdate } from "../../react/hooks.jsx";
import BulkAction from "../BulkAction.jsx";
function DeleteLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
const [ labelName, setLabelName ] = useState<string>(actionDef.labelName ?? "");
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName }));
useEffect(() => spacedUpdate.scheduleUpdate(), [labelName]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("delete_label.delete_label")}
>
<FormTextBox
pattern="[\\p{L}\\p{N}_:]+"
title={t("delete_label.label_name_title")}
placeholder={t("delete_label.label_name_placeholder")}
currentValue={labelName} onChange={setLabelName}
/>
</BulkAction>
);
}
export default class DeleteLabelBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteLabel";
}
static get actionTitle() {
return t("delete_label.delete_label");
}
doRender() {
return <DeleteLabelBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,60 +0,0 @@
import { t } from "../../../services/i18n.js";
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px; flex-shrink: 0;">${t("rename_label.rename_label_from")}</div>
<input type="text"
class="form-control old-label-name"
placeholder="${t("rename_label.old_name_placeholder")}"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("rename_label.name_title")}"/>
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("rename_label.to")}</div>
<input type="text"
class="form-control new-label-name"
placeholder="${t("rename_label.new_name_placeholder")}"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("rename_label.name_title")}"/>
</div>
</td>
<td class="button-column">
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class RenameLabelBulkAction extends AbstractBulkAction {
static get actionName() {
return "renameLabel";
}
static get actionTitle() {
return t("rename_label.rename_label");
}
doRender() {
const $action = $(TPL);
const $oldLabelName = $action.find(".old-label-name");
$oldLabelName.val(this.actionDef.oldLabelName || "");
const $newLabelName = $action.find(".new-label-name");
$newLabelName.val(this.actionDef.newLabelName || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
oldLabelName: $oldLabelName.val(),
newLabelName: $newLabelName.val()
});
}, 1000);
$oldLabelName.on("input", () => spacedUpdate.scheduleUpdate());
$newLabelName.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,49 @@
import { useEffect, useState } from "preact/hooks";
import { t } from "../../../services/i18n.js";
import FormTextBox from "../../react/FormTextBox.jsx";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function RenameLabelBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
const [ oldLabelName, setOldLabelName ] = useState(actionDef.oldLabelName);
const [ newLabelName, setNewLabelName ] = useState(actionDef.newLabelName);
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ oldLabelName, newLabelName }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ oldLabelName, newLabelName ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("rename_label.rename_label_from")}
>
<FormTextBox
placeholder={t("rename_label.old_name_placeholder")}
pattern="[\\p{L}\\p{N}_:]+"
title={t("rename_label.name_title")}
currentValue={oldLabelName} onChange={setOldLabelName}
/>
<BulkActionText text={t("rename_label.to")} />
<FormTextBox
placeholder={t("rename_label.new_name_placeholder")}
pattern="[\\p{L}\\p{N}_:]+"
title={t("rename_label.name_title")}
currentValue={newLabelName} onChange={setNewLabelName}
/>
</BulkAction>
)
}
export default class RenameLabelBulkAction extends AbstractBulkAction {
static get actionName() {
return "renameLabel";
}
static get actionTitle() {
return t("rename_label.rename_label");
}
doRender() {
return <RenameLabelBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,65 +0,0 @@
import { t } from "../../../services/i18n.js";
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px;" class="text-nowrap">${t("update_label_value.update_label_value")}</div>
<input type="text"
class="form-control label-name"
placeholder="${t("update_label_value.label_name_placeholder")}"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("update_label_value.label_name_title")}"/>
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("update_label_value.to_value")}</div>
<input type="text" class="form-control label-value" placeholder="${t("update_label_value.new_value_placeholder")}"/>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>${t("update_label_value.help_text")}</p>
${t("update_label_value.help_text_note")}
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class UpdateLabelValueBulkAction extends AbstractBulkAction {
static get actionName() {
return "updateLabelValue";
}
static get actionTitle() {
return t("update_label_value.update_label_value");
}
doRender() {
const $action = $(TPL);
const $labelName = $action.find(".label-name");
$labelName.val(this.actionDef.labelName || "");
const $labelValue = $action.find(".label-value");
$labelValue.val(this.actionDef.labelValue || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
labelName: $labelName.val(),
labelValue: $labelValue.val()
});
}, 1000);
$labelName.on("input", () => spacedUpdate.scheduleUpdate());
$labelValue.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,50 @@
import { t } from "../../../services/i18n.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import FormTextBox from "../../react/FormTextBox.jsx";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import { useSpacedUpdate } from "../../react/hooks.jsx";
import { useEffect, useState } from "preact/hooks";
function UpdateLabelValueComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
const [ labelName, setLabelName ] = useState<string>(actionDef.labelName ?? "");
const [ labelValue, setLabelValue ] = useState<string>(actionDef.labelValue ?? "");
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ labelName, labelValue }));
useEffect(() => spacedUpdate.scheduleUpdate(), [labelName, labelValue]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("update_label_value.update_label_value")}
helpText={<>
<p>{t("update_label_value.help_text")}</p>
{t("update_label_value.help_text_note")}
</>}
>
<FormTextBox
placeholder={t("update_label_value.label_name_placeholder")}
pattern="[\\p{L}\\p{N}_:]+"
title={t("update_label_value.label_name_title")}
currentValue={labelName} onChange={setLabelName}
/>
<BulkActionText text={t("update_label_value.to_value")} />
<FormTextBox
placeholder={t("update_label_value.new_value_placeholder")}
currentValue={labelValue} onChange={setLabelValue}
/>
</BulkAction>
)
}
export default class UpdateLabelValueBulkAction extends AbstractBulkAction {
static get actionName() {
return "updateLabelValue";
}
static get actionTitle() {
return t("update_label_value.update_label_value");
}
doRender() {
return <UpdateLabelValueComponent bulkAction={this} actionDef={this.actionDef} />;
}
}

View File

@@ -1,38 +0,0 @@
import { t } from "../../../services/i18n.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<span class="bx bx-trash"></span>
${t("delete_note.delete_matched_notes")}
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>${t("delete_note.delete_matched_notes_description")}</p>
<p>${t("delete_note.undelete_notes_instruction")}</p>
${t("delete_note.erase_notes_instruction")}
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class DeleteNoteBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteNote";
}
static get actionTitle() {
return t("delete_note.delete_note");
}
doRender() {
return $(TPL);
}
}

View File

@@ -0,0 +1,33 @@
import { t } from "../../../services/i18n.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import BulkAction from "../BulkAction.jsx";
import Icon from "../../react/Icon.jsx";
function DeleteNoteBulkActionComponent({ bulkAction }: { bulkAction: AbstractBulkAction }) {
return (
<BulkAction
bulkAction={bulkAction}
label={<><Icon icon="bx bx-trash" /> {t("delete_note.delete_matched_notes")}</>}
helpText={<>
<p>{t("delete_note.delete_matched_notes_description")}</p>
<p>{t("delete_note.undelete_notes_instruction")}</p>
{t("delete_note.erase_notes_instruction")}
</>}
/>
);
}
export default class DeleteNoteBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteNote";
}
static get actionTitle() {
return t("delete_note.delete_note");
}
doRender() {
return <DeleteNoteBulkActionComponent bulkAction={this} />
}
}

View File

@@ -1,32 +0,0 @@
import { t } from "../../../services/i18n.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<span class="bx bx-trash"></span>
${t("delete_revisions.delete_note_revisions")}
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
${t("delete_revisions.all_past_note_revisions")}
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class DeleteRevisionsBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteRevisions";
}
static get actionTitle() {
return t("delete_revisions.delete_note_revisions");
}
doRender() {
return $(TPL);
}
}

View File

@@ -0,0 +1,28 @@
import { t } from "../../../services/i18n.js";
import Icon from "../../react/Icon.jsx";
import AbstractBulkAction from "../abstract_bulk_action.js";
import BulkAction from "../BulkAction.jsx";
function DeleteRevisionsBulkActionComponent({ bulkAction }: { bulkAction: AbstractBulkAction }) {
return (
<BulkAction
bulkAction={bulkAction}
label={<><Icon icon="bx bx-trash" /> {t("delete_revisions.delete_note_revisions")}</>}
helpText={t("delete_revisions.all_past_note_revisions")}
/>
)
}
export default class DeleteRevisionsBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteRevisions";
}
static get actionTitle() {
return t("delete_revisions.delete_note_revisions");
}
doRender() {
return <DeleteRevisionsBulkActionComponent bulkAction={this} />
}
}

View File

@@ -1,64 +0,0 @@
import { t } from "../../../services/i18n.js";
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import noteAutocompleteService from "../../../services/note_autocomplete.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px;" class="text-nowrap">${t("move_note.move_note")}</div>
<div style="margin-right: 10px;" class="text-nowrap">${t("move_note.to")}</div>
<div class="input-group">
<input type="text" class="form-control target-parent-note" placeholder="${t("move_note.target_parent_note")}"/>
</div>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>${t("move_note.on_all_matched_notes")}:</p>
<ul style="margin-bottom: 0;">
<li>${t("move_note.move_note_new_parent")}</li>
<li>${t("move_note.clone_note_new_parent")}</li>
<li>${t("move_note.nothing_will_happen")}</li>
</ul>
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class MoveNoteBulkAction extends AbstractBulkAction {
static get actionName() {
return "moveNote";
}
static get actionTitle() {
return t("move_note.move_note");
}
doRender() {
const $action = $(TPL);
const $targetParentNote = $action.find(".target-parent-note");
noteAutocompleteService.initNoteAutocomplete($targetParentNote);
$targetParentNote.setNote(this.actionDef.targetParentNoteId);
$targetParentNote.on("autocomplete:closed", () => spacedUpdate.scheduleUpdate());
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
targetParentNoteId: $targetParentNote.getSelectedNoteId()
});
}, 1000);
$targetParentNote.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,50 @@
import { t } from "../../../services/i18n.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function MoveNoteBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ targetParentNoteId, setTargetParentNoteId ] = useState<string>();
const spacedUpdate = useSpacedUpdate(() => {
return bulkAction.saveAction({ targetParentNoteId: targetParentNoteId })
});
useEffect(() => spacedUpdate.scheduleUpdate(), [ targetParentNoteId ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("move_note.move_note")}
helpText={<>
<p>{t("move_note.on_all_matched_notes")}:</p>
<ul style="margin-bottom: 0;">
<li>{t("move_note.move_note_new_parent")}</li>
<li>{t("move_note.clone_note_new_parent")}</li>
<li>{t("move_note.nothing_will_happen")}</li>
</ul>
</>}
>
<BulkActionText text={t("move_note.to")} />
<NoteAutocomplete
placeholder={t("move_note.target_parent_note")}
noteId={targetParentNoteId} noteIdChanged={setTargetParentNoteId}
/>
</BulkAction>
)
}
export default class MoveNoteBulkAction extends AbstractBulkAction {
static get actionName() {
return "moveNote";
}
static get actionTitle() {
return t("move_note.move_note");
}
doRender() {
return <MoveNoteBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,61 +0,0 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px; flex-shrink: 0;">${t("rename_note.rename_note_title_to")}</div>
<input type="text"
class="form-control new-title"
placeholder="${t("rename_note.new_note_title")}"
title="${t("rename_note.click_help_icon")}"/>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>${t("rename_note.evaluated_as_js_string")}</p>
<ul>
<li>${t("rename_note.example_note")}</li>
<li>${t("rename_note.example_new_title")}</li>
<li>${t("rename_note.example_date_prefix")}</li>
</ul>
${t("rename_note.api_docs")}
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class RenameNoteBulkAction extends AbstractBulkAction {
static get actionName() {
return "renameNote";
}
static get actionTitle() {
return t("rename_note.rename_note");
}
doRender() {
const $action = $(TPL);
const $newTitle = $action.find(".new-title");
$newTitle.val(this.actionDef.newTitle || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
newTitle: $newTitle.val()
});
}, 1000);
$newTitle.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,53 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
import BulkAction from "../BulkAction.jsx";
import FormTextBox from "../../react/FormTextBox.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
import RawHtml from "../../react/RawHtml.jsx";
function RenameNoteBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition}) {
const [ newTitle, setNewTitle ] = useState<string>(actionDef.newTitle ?? "");
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ newTitle }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ newTitle ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("rename_note.rename_note_title_to")}
helpText={<>
<p>{t("rename_note.evaluated_as_js_string")}</p>
<ul>
<li><RawHtml html={t("rename_note.example_note")} /></li>
<li><RawHtml html={t("rename_note.example_new_title")} /></li>
<li><RawHtml html={t("rename_note.example_date_prefix")} /></li>
</ul>
<RawHtml html={t("rename_note.api_docs")} />
</>}
>
<FormTextBox
placeholder={t("rename_note.new_note_title")}
title={("rename_note.click_help_icon")}
currentValue={newTitle} onChange={setNewTitle}
/>
</BulkAction>
)
}
export default class RenameNoteBulkAction extends AbstractBulkAction {
static get actionName() {
return "renameNote";
}
static get actionTitle() {
return t("rename_note.rename_note");
}
doRender() {
return <RenameNoteBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,70 +0,0 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import noteAutocompleteService from "../../../services/note_autocomplete.js";
import { t } from "../../../services/i18n.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px;" class="text-nowrap">${t("add_relation.add_relation")}</div>
<input type="text"
class="form-control relation-name"
placeholder="${t("add_relation.relation_name")}"
pattern="[\\p{L}\\p{N}_:]+"
style="flex-shrink: 3"
title="${t("add_relation.allowed_characters")}"/>
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("add_relation.to")}</div>
<div class="input-group" style="flex-shrink: 2">
<input type="text" class="form-control target-note" placeholder="${t("add_relation.target_note")}"/>
</div>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
${t("add_relation.create_relation_on_all_matched_notes")}
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class AddRelationBulkAction extends AbstractBulkAction {
static get actionName() {
return "addRelation";
}
static get actionTitle() {
return t("add_relation.add_relation");
}
doRender() {
const $action = $(TPL);
const $relationName = $action.find(".relation-name");
$relationName.val(this.actionDef.relationName || "");
const $targetNote = $action.find(".target-note");
noteAutocompleteService.initNoteAutocomplete($targetNote);
$targetNote.setNote(this.actionDef.targetNoteId);
$targetNote.on("autocomplete:closed", () => spacedUpdate.scheduleUpdate());
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
relationName: $relationName.val(),
targetNoteId: $targetNote.getSelectedNoteId()
});
}, 1000);
$relationName.on("input", () => spacedUpdate.scheduleUpdate());
$targetNote.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,53 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import noteAutocompleteService from "../../../services/note_autocomplete.js";
import { t } from "../../../services/i18n.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
import FormTextBox from "../../react/FormTextBox.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function AddRelationBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ relationName, setRelationName ] = useState<string>(actionDef.relationName);
const [ targetNoteId, setTargetNoteId ] = useState<string>(actionDef.targetNoteId);
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ relationName, targetNoteId }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ relationName, targetNoteId ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("add_relation.add_relation")}
helpText={t("add_relation.create_relation_on_all_matched_notes")}
>
<FormTextBox
placeholder={t("add_relation.relation_name")}
pattern="[\\p{L}\\p{N}_:]+"
style={{ flexShrink: 3 }}
title={t("add_relation.allowed_characters")}
currentValue={relationName} onChange={setRelationName}
/>
<BulkActionText text={t("add_relation.to")} />
<NoteAutocomplete
placeholder={t("add_relation.target_note")}
noteId={targetNoteId} noteIdChanged={setTargetNoteId}
/>
</BulkAction>
)
}
export default class AddRelationBulkAction extends AbstractBulkAction {
static get actionName() {
return "addRelation";
}
static get actionTitle() {
return t("add_relation.add_relation");
}
doRender() {
return <AddRelationBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,45 +0,0 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
const TPL = /*html*/`
<tr>
<td>
${t("delete_relation.delete_relation")}
</td>
<td>
<div style="display: flex; align-items: center">
<input type="text"
class="form-control relation-name"
pattern="[\\p{L}\\p{N}_:]+"
placeholder="${t("delete_relation.relation_name")}"
title="${t("delete_relation.allowed_characters")}"/>
</div>
</td>
<td class="button-column">
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class DeleteRelationBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteRelation";
}
static get actionTitle() {
return t("delete_relation.delete_relation");
}
doRender() {
const $action = $(TPL);
const $relationName = $action.find(".relation-name");
$relationName.val(this.actionDef.relationName || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({ relationName: $relationName.val() });
}, 1000);
$relationName.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,41 @@
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
import BulkAction from "../BulkAction.jsx";
import FormTextBox from "../../react/FormTextBox.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function DeleteRelationBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ relationName, setRelationName ] = useState(actionDef.relationName);
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ relationName }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ relationName ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("delete_relation.delete_relation")}
>
<FormTextBox
pattern="[\\p{L}\\p{N}_:]+"
placeholder={t("delete_relation.relation_name")}
title={t("delete_relation.allowed_characters")}
currentValue={relationName} onChange={setRelationName}
/>
</BulkAction>
)
}
export default class DeleteRelationBulkAction extends AbstractBulkAction {
static get actionName() {
return "deleteRelation";
}
static get actionTitle() {
return t("delete_relation.delete_relation");
}
doRender() {
return <DeleteRelationBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,60 +0,0 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px; flex-shrink: 0;">${t("rename_relation.rename_relation_from")}</div>
<input type="text"
class="form-control old-relation-name"
placeholder="${t("rename_relation.old_name")}"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("rename_relation.allowed_characters")}"/>
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("rename_relation.to")}</div>
<input type="text"
class="form-control new-relation-name"
placeholder="${t("rename_relation.new_name")}"
pattern="[\\p{L}\\p{N}_:]+"
title="${t("rename_relation.allowed_characters")}"/>
</div>
</td>
<td class="button-column">
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class RenameRelationBulkAction extends AbstractBulkAction {
static get actionName() {
return "renameRelation";
}
static get actionTitle() {
return t("rename_relation.rename_relation");
}
doRender() {
const $action = $(TPL);
const $oldRelationName = $action.find(".old-relation-name");
$oldRelationName.val(this.actionDef.oldRelationName || "");
const $newRelationName = $action.find(".new-relation-name");
$newRelationName.val(this.actionDef.newRelationName || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
oldRelationName: $oldRelationName.val(),
newRelationName: $newRelationName.val()
});
}, 1000);
$oldRelationName.on("input", () => spacedUpdate.scheduleUpdate());
$newRelationName.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,49 @@
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import FormTextBox from "../../react/FormTextBox.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function RenameRelationBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ oldRelationName, setOldRelationName ] = useState(actionDef.oldRelationName);
const [ newRelationName, setNewRelationName ] = useState(actionDef.newRelationName);
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ oldRelationName, newRelationName }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ oldRelationName, newRelationName ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("rename_relation.rename_relation_from")}
>
<FormTextBox
placeholder={t("rename_relation.old_name")}
pattern="[\\p{L}\\p{N}_:]+"
title={t("rename_relation.allowed_characters")}
currentValue={oldRelationName} onChange={setOldRelationName}
/>
<BulkActionText text={t("rename_relation.to")} />
<FormTextBox
placeholder={t("rename_relation.new_name")}
pattern="[\\p{L}\\p{N}_:]+"
title={t("rename_relation.allowed_characters")}
currentValue={newRelationName} onChange={setNewRelationName}
/>
</BulkAction>
)
}
export default class RenameRelationBulkAction extends AbstractBulkAction {
static get actionName() {
return "renameRelation";
}
static get actionTitle() {
return t("rename_relation.rename_relation");
}
doRender() {
return <RenameRelationBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -1,74 +0,0 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import noteAutocompleteService from "../../../services/note_autocomplete.js";
import { t } from "../../../services/i18n.js";
const TPL = /*html*/`
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px;" class="text-nowrap">${t("update_relation_target.update_relation")}</div>
<input type="text"
class="form-control relation-name"
placeholder="${t("update_relation_target.relation_name")}"
pattern="[\\p{L}\\p{N}_:]+"
style="flex-shrink: 3"
title="${t("update_relation_target.allowed_characters")}"/>
<div style="margin-right: 10px; margin-left: 10px;" class="text-nowrap">${t("update_relation_target.to")}</div>
<div class="input-group" style="flex-shrink: 2">
<input type="text" class="form-control target-note" placeholder="${t("update_relation_target.target_note")}"/>
</div>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>${t("update_relation_target.on_all_matched_notes")}:</p>
<ul style="margin-bottom: 0;">
<li>${t("update_relation_target.change_target_note")}</li>
</ul>
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class UpdateRelationTargetBulkAction extends AbstractBulkAction {
static get actionName() {
return "updateRelationTarget";
}
static get actionTitle() {
return t("update_relation_target.update_relation_target");
}
doRender() {
const $action = $(TPL);
const $relationName = $action.find(".relation-name");
$relationName.val(this.actionDef.relationName || "");
const $targetNote = $action.find(".target-note");
noteAutocompleteService.initNoteAutocomplete($targetNote);
$targetNote.setNote(this.actionDef.targetNoteId);
$targetNote.on("autocomplete:closed", () => spacedUpdate.scheduleUpdate());
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
relationName: $relationName.val(),
targetNoteId: $targetNote.getSelectedNoteId()
});
}, 1000);
$relationName.on("input", () => spacedUpdate.scheduleUpdate());
$targetNote.on("input", () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@@ -0,0 +1,60 @@
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import FormTextBox from "../../react/FormTextBox.jsx";
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function UpdateRelationTargetComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
const [ relationName, setRelationName ] = useState(actionDef.relationName);
const [ targetNoteId, setTargetNoteId ] = useState(actionDef.targetNoteId);
const spacedUpdate = useSpacedUpdate(() => bulkAction.saveAction({ relationName, targetNoteId }));
useEffect(() => spacedUpdate.scheduleUpdate(), [ relationName, targetNoteId ]);
return (
<BulkAction
bulkAction={bulkAction}
label={t("update_relation_target.update_relation")}
helpText={<>
<p>{t("update_relation_target.on_all_matched_notes")}:</p>
<ul style="margin-bottom: 0;">
<li>{t("update_relation_target.change_target_note")}</li>
</ul>
</>}
>
<FormTextBox
placeholder={t("update_relation_target.relation_name")}
pattern="[\\p{L}\\p{N}_:]+"
style={{ flexShrink: 3 }}
title={t("update_relation_target.allowed_characters")}
currentValue={relationName} onChange={setRelationName}
/>
<BulkActionText text={t("update_relation_target.to")} />
<NoteAutocomplete
placeholder={t("update_relation_target.target_note")}
containerStyle={{ flexShrink: 2 }}
noteId={targetNoteId} noteIdChanged={setTargetNoteId}
/>
</BulkAction>
)
}
export default class UpdateRelationTargetBulkAction extends AbstractBulkAction {
static get actionName() {
return "updateRelationTarget";
}
static get actionTitle() {
return t("update_relation_target.update_relation_target");
}
doRender() {
return <UpdateRelationTargetComponent bulkAction={this} actionDef={this.actionDef} />
}
}

View File

@@ -363,7 +363,6 @@ export default class GlobalMenuWidget extends BasicWidget {
this.$zoomState = this.$widget.find(".zoom-state");
this.$toggleZenMode = this.$widget.find('[data-trigger-command="toggleZenMode"');
this.$toggleZenMode.toggle(!utils.isMobile());
this.$widget.on("show.bs.dropdown", () => this.#onShown());
if (this.tooltip) {
this.$widget.on("hide.bs.dropdown", () => this.tooltip.enable());
@@ -415,7 +414,7 @@ export default class GlobalMenuWidget extends BasicWidget {
}
async fetchLatestVersion() {
const RELEASES_API_URL = "https://api.github.com/repos/TriliumNext/Notes/releases/latest";
const RELEASES_API_URL = "https://api.github.com/repos/TriliumNext/Trilium/releases/latest";
const resp = await fetch(RELEASES_API_URL);
const data = await resp.json();

View File

@@ -1,5 +1,6 @@
import BasicWidget from "./basic_widget.js";
import { t } from "../services/i18n.js";
import utils from "../services/utils.js";
const TPL = /*html*/`\
<div class="close-zen-container">
@@ -28,6 +29,10 @@ const TPL = /*html*/`\
-webkit-app-region: no-drag;
}
body.zen.mobile .close-zen-container {
top: -2px;
}
body.zen.electron:not(.platform-darwin):not(.native-titlebar) .close-zen-container {
left: calc(env(titlebar-area-width) - var(--zen-button-size) - 2px);
right: unset;

View File

@@ -1,4 +1,3 @@
import { openDialog } from "../../services/dialog.js";
import ReactBasicWidget from "../react/ReactBasicWidget.js";
import Modal from "../react/Modal.js";
import { t } from "../../services/i18n.js";
@@ -9,59 +8,64 @@ import openService from "../../services/open.js";
import { useState } from "preact/hooks";
import type { CSSProperties } from "preact/compat";
import type { AppInfo } from "@triliumnext/commons";
import useTriliumEvent from "../react/hooks.jsx";
function AboutDialogComponent() {
let [appInfo, setAppInfo] = useState<AppInfo | null>(null);
async function onShown() {
const appInfo = await server.get<AppInfo>("app-info");
setAppInfo(appInfo);
}
let [shown, setShown] = useState(false);
const forceWordBreak: CSSProperties = { wordBreak: "break-all" };
useTriliumEvent("openAboutDialog", () => setShown(true));
return (
<Modal className="about-dialog" size="lg" title={t("about.title")} onShown={onShown}>
{(appInfo !== null) ? (
<table className="table table-borderless">
<tbody>
<tr>
<th>{t("about.homepage")}</th>
<td><a className="tn-link external" href="https://github.com/TriliumNext/Trilium" style={forceWordBreak}>https://github.com/TriliumNext/Trilium</a></td>
</tr>
<tr>
<th>{t("about.app_version")}</th>
<td className="app-version">{appInfo.appVersion}</td>
</tr>
<tr>
<th>{t("about.db_version")}</th>
<td className="db-version">{appInfo.dbVersion}</td>
</tr>
<tr>
<th>{t("about.sync_version")}</th>
<td className="sync-version">{appInfo.syncVersion}</td>
</tr>
<tr>
<th>{t("about.build_date")}</th>
<td className="build-date">{formatDateTime(appInfo.buildDate)}</td>
</tr>
<tr>
<th>{t("about.build_revision")}</th>
<td>
<a className="tn-link build-revision external" href={`https://github.com/TriliumNext/Trilium/commit/${appInfo.buildRevision}`} target="_blank" style={forceWordBreak}>{appInfo.buildRevision}</a>
</td>
</tr>
<tr>
<th>{t("about.data_directory")}</th>
<td className="data-directory">
<DirectoryLink directory={appInfo.dataDirectory} style={forceWordBreak} />
</td>
</tr>
</tbody>
</table>
) : (
<div className="loading-spinner"></div>
)}
<Modal className="about-dialog"
size="lg"
title={t("about.title")}
show={shown}
onShown={async () => {
const appInfo = await server.get<AppInfo>("app-info");
setAppInfo(appInfo);
}}
onHidden={() => setShown(false)}
>
<table className="table table-borderless">
<tbody>
<tr>
<th>{t("about.homepage")}</th>
<td><a className="tn-link external" href="https://github.com/TriliumNext/Trilium" style={forceWordBreak}>https://github.com/TriliumNext/Trilium</a></td>
</tr>
<tr>
<th>{t("about.app_version")}</th>
<td className="app-version">{appInfo?.appVersion}</td>
</tr>
<tr>
<th>{t("about.db_version")}</th>
<td className="db-version">{appInfo?.dbVersion}</td>
</tr>
<tr>
<th>{t("about.sync_version")}</th>
<td className="sync-version">{appInfo?.syncVersion}</td>
</tr>
<tr>
<th>{t("about.build_date")}</th>
<td className="build-date">
{appInfo?.buildDate ? formatDateTime(appInfo.buildDate) : ""}
</td>
</tr>
<tr>
<th>{t("about.build_revision")}</th>
<td>
{appInfo?.buildRevision && <a className="tn-link build-revision external" href={`https://github.com/TriliumNext/Trilium/commit/${appInfo.buildRevision}`} target="_blank" style={forceWordBreak}>{appInfo.buildRevision}</a>}
</td>
</tr>
<tr>
<th>{t("about.data_directory")}</th>
<td className="data-directory">
{appInfo?.dataDirectory && (<DirectoryLink directory={appInfo.dataDirectory} style={forceWordBreak} />)}
</td>
</tr>
</tbody>
</table>
</Modal>
);
}
@@ -85,7 +89,4 @@ export default class AboutDialog extends ReactBasicWidget {
return <AboutDialogComponent />;
}
async openAboutDialogEvent() {
openDialog(this.$widget);
}
}

View File

@@ -1,188 +0,0 @@
import { t } from "../../services/i18n.js";
import treeService from "../../services/tree.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
import BasicWidget from "../basic_widget.js";
import type { Suggestion } from "../../services/note_autocomplete.js";
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="add-link-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" style="max-width: 1000px" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title flex-grow-1">${t("add_link.add_link")}</h5>
<button type="button" class="help-button" title="${t("add_link.help_on_links")}" data-help-page="links.html">?</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("add_link.close")}"></button>
</div>
<form class="add-link-form">
<div class="modal-body">
<div class="form-group">
<label for="add-link-note-autocomplete">${t("add_link.note")}</label>
<div class="input-group">
<input class="add-link-note-autocomplete form-control" placeholder="${t("add_link.search_note")}">
</div>
</div>
<div class="add-link-title-settings">
<div class="add-link-title-radios form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="link-type" value="reference-link" checked>
${t("add_link.link_title_mirrors")}
</label>
</div>
<div class="add-link-title-radios form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="link-type" value="hyper-link">
${t("add_link.link_title_arbitrary")}
</label>
</div>
<div class="add-link-title-form-group form-group">
<br/>
<label>
${t("add_link.link_title")}
<input class="link-title form-control" style="width: 100%;">
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">${t("add_link.button_add_link")}</button>
</div>
</form>
</div>
</div>
</div>`;
export default class AddLinkDialog extends BasicWidget {
private $form!: JQuery<HTMLElement>;
private $autoComplete!: JQuery<HTMLElement>;
private $linkTitle!: JQuery<HTMLElement>;
private $addLinkTitleSettings!: JQuery<HTMLElement>;
private $addLinkTitleRadios!: JQuery<HTMLElement>;
private $addLinkTitleFormGroup!: JQuery<HTMLElement>;
private textTypeWidget: TextTypeWidget | null = null;
doRender() {
this.$widget = $(TPL);
this.$form = this.$widget.find(".add-link-form");
this.$autoComplete = this.$widget.find(".add-link-note-autocomplete");
this.$linkTitle = this.$widget.find(".link-title");
this.$addLinkTitleSettings = this.$widget.find(".add-link-title-settings");
this.$addLinkTitleRadios = this.$widget.find(".add-link-title-radios");
this.$addLinkTitleFormGroup = this.$widget.find(".add-link-title-form-group");
this.$form.on("submit", () => {
if (this.$autoComplete.getSelectedNotePath()) {
this.$widget.modal("hide");
const linkTitle = this.getLinkType() === "reference-link" ? null : this.$linkTitle.val() as string;
this.textTypeWidget?.addLink(this.$autoComplete.getSelectedNotePath()!, linkTitle);
} else if (this.$autoComplete.getSelectedExternalLink()) {
this.$widget.modal("hide");
this.textTypeWidget?.addLink(this.$autoComplete.getSelectedExternalLink()!, this.$linkTitle.val() as string, true);
} else {
logError("No link to add.");
}
return false;
});
}
async showAddLinkDialogEvent({ textTypeWidget, text = "" }: EventData<"showAddLinkDialog">) {
this.textTypeWidget = textTypeWidget;
this.$addLinkTitleSettings.toggle(!this.textTypeWidget.hasSelection());
this.$addLinkTitleSettings.find("input[type=radio]").on("change", () => this.updateTitleSettingsVisibility());
// with selection hyperlink is implied
if (this.textTypeWidget.hasSelection()) {
this.$addLinkTitleSettings.find("input[value='hyper-link']").prop("checked", true);
} else {
this.$addLinkTitleSettings.find("input[value='reference-link']").prop("checked", true);
}
this.updateTitleSettingsVisibility();
await openDialog(this.$widget);
this.$autoComplete.val("");
this.$linkTitle.val("");
const setDefaultLinkTitle = async (noteId: string) => {
const noteTitle = await treeService.getNoteTitle(noteId);
this.$linkTitle.val(noteTitle);
};
noteAutocompleteService.initNoteAutocomplete(this.$autoComplete, {
allowExternalLinks: true,
allowCreatingNotes: true
});
this.$autoComplete.on("autocomplete:noteselected", (event: JQuery.Event, suggestion: Suggestion) => {
if (!suggestion.notePath) {
return false;
}
this.updateTitleSettingsVisibility();
const noteId = treeService.getNoteIdFromUrl(suggestion.notePath);
if (noteId) {
setDefaultLinkTitle(noteId);
}
});
this.$autoComplete.on("autocomplete:externallinkselected", (event: JQuery.Event, suggestion: Suggestion) => {
if (!suggestion.externalLink) {
return false;
}
this.updateTitleSettingsVisibility();
this.$linkTitle.val(suggestion.externalLink);
});
this.$autoComplete.on("autocomplete:cursorchanged", (event: JQuery.Event, suggestion: Suggestion) => {
if (suggestion.externalLink) {
this.$linkTitle.val(suggestion.externalLink);
} else {
const noteId = treeService.getNoteIdFromUrl(suggestion.notePath!);
if (noteId) {
setDefaultLinkTitle(noteId);
}
}
});
if (text && text.trim()) {
noteAutocompleteService.setText(this.$autoComplete, text);
} else {
noteAutocompleteService.showRecentNotes(this.$autoComplete);
}
this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
}
private getLinkType() {
if (this.$autoComplete.getSelectedExternalLink()) {
return "external-link";
}
return this.$addLinkTitleSettings.find("input[type=radio]:checked").val();
}
private updateTitleSettingsVisibility() {
const linkType = this.getLinkType();
this.$addLinkTitleFormGroup.toggle(linkType !== "reference-link");
this.$addLinkTitleRadios.toggle(linkType !== "external-link");
}
}

View File

@@ -0,0 +1,162 @@
import { t } from "../../services/i18n";
import Modal from "../react/Modal";
import ReactBasicWidget from "../react/ReactBasicWidget";
import Button from "../react/Button";
import FormRadioGroup from "../react/FormRadioGroup";
import NoteAutocomplete from "../react/NoteAutocomplete";
import { useRef, useState, useEffect } from "preact/hooks";
import tree from "../../services/tree";
import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
import { default as TextTypeWidget } from "../type_widgets/editable_text.js";
import { logError } from "../../services/ws";
import FormGroup from "../react/FormGroup.js";
import { refToJQuerySelector } from "../react/react_utils";
import useTriliumEvent from "../react/hooks";
type LinkType = "reference-link" | "external-link" | "hyper-link";
function AddLinkDialogComponent() {
const [ textTypeWidget, setTextTypeWidget ] = useState<TextTypeWidget>();
const initialText = useRef<string>();
const [ linkTitle, setLinkTitle ] = useState("");
const hasSelection = textTypeWidget?.hasSelection();
const [ linkType, setLinkType ] = useState<LinkType>(hasSelection ? "hyper-link" : "reference-link");
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
const [ shown, setShown ] = useState(false);
useTriliumEvent("showAddLinkDialog", ( { textTypeWidget, text }) => {
setTextTypeWidget(textTypeWidget);
initialText.current = text;
setShown(true);
});
async function setDefaultLinkTitle(noteId: string) {
const noteTitle = await tree.getNoteTitle(noteId);
setLinkTitle(noteTitle);
}
function resetExternalLink() {
if (linkType === "external-link") {
setLinkType("reference-link");
}
}
useEffect(() => {
if (!suggestion) {
resetExternalLink();
return;
}
if (suggestion.notePath) {
const noteId = tree.getNoteIdFromUrl(suggestion.notePath);
if (noteId) {
setDefaultLinkTitle(noteId);
}
resetExternalLink();
}
if (suggestion.externalLink) {
setLinkTitle(suggestion.externalLink);
setLinkType("external-link");
}
}, [suggestion]);
function onShown() {
const $autocompleteEl = refToJQuerySelector(autocompleteRef);
if (!initialText.current) {
note_autocomplete.showRecentNotes($autocompleteEl);
} else {
note_autocomplete.setText($autocompleteEl, initialText.current);
}
// to be able to quickly remove entered text
$autocompleteEl
.trigger("focus")
.trigger("select");
}
function onSubmit() {
if (suggestion?.notePath) {
// Handle note link
setShown(false);
textTypeWidget?.addLink(suggestion.notePath, linkType === "reference-link" ? null : linkTitle);
} else if (suggestion?.externalLink) {
// Handle external link
setShown(false);
textTypeWidget?.addLink(suggestion.externalLink, linkTitle, true);
} else {
logError("No link to add.");
}
}
const autocompleteRef = useRef<HTMLInputElement>(null);
return (
<Modal
className="add-link-dialog"
size="lg"
maxWidth={1000}
title={t("add_link.add_link")}
helpPageId="QEAPj01N5f7w"
footer={<Button text={t("add_link.button_add_link")} keyboardShortcut="Enter" />}
onSubmit={onSubmit}
onShown={onShown}
onHidden={() => {
setSuggestion(null);
setShown(false);
}}
show={shown}
>
<FormGroup label={t("add_link.note")}>
<NoteAutocomplete
inputRef={autocompleteRef}
onChange={setSuggestion}
opts={{
allowExternalLinks: true,
allowCreatingNotes: true
}}
/>
</FormGroup>
{!hasSelection && (
<div className="add-link-title-settings">
{(linkType !== "external-link") && (
<>
<FormRadioGroup
name="link-type"
currentValue={linkType}
values={[
{ value: "reference-link", label: t("add_link.link_title_mirrors") },
{ value: "hyper-link", label: t("add_link.link_title_arbitrary") }
]}
onChange={(newValue) => setLinkType(newValue as LinkType)}
/>
</>
)}
{(linkType !== "reference-link" && (
<div className="add-link-title-form-group form-group">
<br/>
<label>
{t("add_link.link_title")}
<input className="link-title form-control" style={{ width: "100%" }}
value={linkTitle}
onInput={e => setLinkTitle((e.target as HTMLInputElement)?.value ?? "")}
/>
</label>
</div>
))}
</div>
)}
</Modal>
);
}
export default class AddLinkDialog extends ReactBasicWidget {
get component() {
return <AddLinkDialogComponent />;
}
}

View File

@@ -1,108 +0,0 @@
import treeService from "../../services/tree.js";
import server from "../../services/server.js";
import froca from "../../services/froca.js";
import toastService from "../../services/toast.js";
import BasicWidget from "../basic_widget.js";
import appContext from "../../components/app_context.js";
import { t } from "../../services/i18n.js";
import { Modal } from "bootstrap";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`<div class="branch-prefix-dialog modal fade mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<form class="branch-prefix-form">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title flex-grow-1">${t("branch_prefix.edit_branch_prefix")}</h5>
<button class="help-button" type="button" data-help-page="tree-concepts.html#prefix" title="${t("branch_prefix.help_on_tree_prefix")}">?</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("branch_prefix.close")}"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="branch-prefix-input">${t("branch_prefix.prefix")}</label> &nbsp;
<div class="input-group">
<input class="branch-prefix-input form-control">
<div class="branch-prefix-note-title input-group-text"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary btn-sm">${t("branch_prefix.save")}</button>
</div>
</div>
</form>
</div>
</div>`;
export default class BranchPrefixDialog extends BasicWidget {
private modal!: Modal;
private $form!: JQuery<HTMLElement>;
private $treePrefixInput!: JQuery<HTMLElement>;
private $noteTitle!: JQuery<HTMLElement>;
private branchId: string | null = null;
doRender() {
this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$form = this.$widget.find(".branch-prefix-form");
this.$treePrefixInput = this.$widget.find(".branch-prefix-input");
this.$noteTitle = this.$widget.find(".branch-prefix-note-title");
this.$form.on("submit", () => {
this.savePrefix();
return false;
});
this.$widget.on("shown.bs.modal", () => this.$treePrefixInput.trigger("focus"));
}
async refresh(notePath: string) {
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
if (!noteId || !parentNoteId) {
return;
}
const newBranchId = await froca.getBranchId(parentNoteId, noteId);
if (!newBranchId) {
return;
}
this.branchId = newBranchId;
const branch = froca.getBranch(this.branchId);
if (!branch || branch.noteId === "root") {
return;
}
const parentNote = await froca.getNote(branch.parentNoteId);
if (!parentNote || parentNote.type === "search") {
return;
}
this.$treePrefixInput.val(branch.prefix || "");
const noteTitle = await treeService.getNoteTitle(noteId);
this.$noteTitle.text(` - ${noteTitle}`);
}
async editBranchPrefixEvent() {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (!notePath) {
return;
}
await this.refresh(notePath);
openDialog(this.$widget);
}
async savePrefix() {
const prefix = this.$treePrefixInput.val();
await server.put(`branches/${this.branchId}/set-prefix`, { prefix: prefix });
this.modal.hide();
toastService.showMessage(t("branch_prefix.branch_prefix_saved"));
}
}

View File

@@ -0,0 +1,89 @@
import { useRef, useState } from "preact/hooks";
import appContext from "../../components/app_context.js";
import { t } from "../../services/i18n.js";
import server from "../../services/server.js";
import toast from "../../services/toast.js";
import Modal from "../react/Modal.jsx";
import ReactBasicWidget from "../react/ReactBasicWidget.js";
import froca from "../../services/froca.js";
import tree from "../../services/tree.js";
import Button from "../react/Button.jsx";
import FormGroup from "../react/FormGroup.js";
import useTriliumEvent from "../react/hooks.jsx";
import FBranch from "../../entities/fbranch.js";
function BranchPrefixDialogComponent() {
const [ shown, setShown ] = useState(false);
const [ branch, setBranch ] = useState<FBranch>();
const [ prefix, setPrefix ] = useState(branch?.prefix ?? "");
const branchInput = useRef<HTMLInputElement>(null);
useTriliumEvent("editBranchPrefix", async () => {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (!notePath) {
return;
}
const { noteId, parentNoteId } = tree.getNoteIdAndParentIdFromUrl(notePath);
if (!noteId || !parentNoteId) {
return;
}
const newBranchId = await froca.getBranchId(parentNoteId, noteId);
if (!newBranchId) {
return;
}
const parentNote = await froca.getNote(parentNoteId);
if (!parentNote || parentNote.type === "search") {
return;
}
setBranch(froca.getBranch(newBranchId));
setShown(true);
});
async function onSubmit() {
if (!branch) {
return;
}
savePrefix(branch.branchId, prefix);
setShown(false);
}
return (
<Modal
className="branch-prefix-dialog"
title={t("branch_prefix.edit_branch_prefix")}
size="lg"
onShown={() => branchInput.current?.focus()}
onHidden={() => setShown(false)}
onSubmit={onSubmit}
helpPageId="TBwsyfadTA18"
footer={<Button text={t("branch_prefix.save")} />}
show={shown}
>
<FormGroup label={t("branch_prefix.prefix")}>
<div class="input-group">
<input class="branch-prefix-input form-control" value={prefix} ref={branchInput}
onChange={(e) => setPrefix((e.target as HTMLInputElement).value)} />
<div class="branch-prefix-note-title input-group-text"> - {branch && branch.getNoteFromCache().title}</div>
</div>
</FormGroup>
</Modal>
);
}
export default class BranchPrefixDialog extends ReactBasicWidget {
get component() {
return <BranchPrefixDialogComponent />;
}
}
async function savePrefix(branchId: string, prefix: string) {
await server.put(`branches/${branchId}/set-prefix`, { prefix: prefix });
toast.showMessage(t("branch_prefix.branch_prefix_saved"));
}

View File

@@ -0,0 +1,24 @@
.bulk-actions-dialog .modal-body h4:not(:first-child) {
margin-top: 20px;
}
.bulk-actions-dialog .bulk-available-action-list button {
padding: 2px 7px;
margin-right: 10px;
margin-bottom: 5px;
}
.bulk-actions-dialog .bulk-existing-action-list {
width: 100%;
}
.bulk-actions-dialog .bulk-existing-action-list td {
padding: 7px;
}
.bulk-actions-dialog .bulk-existing-action-list .button-column {
/* minimal width so that table remains static sized and most space remains for middle column with settings */
width: 50px;
white-space: nowrap;
text-align: right;
}

View File

@@ -1,175 +0,0 @@
import BasicWidget from "../basic_widget.js";
import froca from "../../services/froca.js";
import bulkActionService from "../../services/bulk_action.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="bulk-actions-dialog modal mx-auto" tabindex="-1" role="dialog">
<style>
.bulk-actions-dialog .modal-body h4:not(:first-child) {
margin-top: 20px;
}
.bulk-actions-dialog .bulk-available-action-list button {
padding: 2px 7px;
margin-right: 10px;
margin-bottom: 5px;
}
.bulk-actions-dialog .bulk-existing-action-list {
width: 100%;
}
.bulk-actions-dialog .bulk-existing-action-list td {
padding: 7px;
}
.bulk-actions-dialog .bulk-existing-action-list .button-column {
/* minimal width so that table remains static sized and most space remains for middle column with settings */
width: 50px;
white-space: nowrap;
text-align: right;
}
</style>
<div class="modal-dialog modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${t("bulk_actions.bulk_actions")}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("bulk_actions.close")}"></button>
</div>
<div class="modal-body">
<h4>${t("bulk_actions.affected_notes")}: <span class="affected-note-count">0</span></h4>
<div class="form-check">
<label for="include-descendants" class="form-check-label tn-checkbox">
<input id="include-descendants" class="include-descendants form-check-input" type="checkbox" value="">
${t("bulk_actions.include_descendants")}
</label>
</div>
<h4>${t("bulk_actions.available_actions")}</h4>
<table class="bulk-available-action-list"></table>
<h4>${t("bulk_actions.chosen_actions")}</h4>
<table class="bulk-existing-action-list"></table>
</div>
<div class="modal-footer">
<button type="submit" class="execute-bulk-actions btn btn-primary">${t("bulk_actions.execute_bulk_actions")}</button>
</div>
</div>
</div>
</div>`;
export default class BulkActionsDialog extends BasicWidget {
private $includeDescendants!: JQuery<HTMLElement>;
private $affectedNoteCount!: JQuery<HTMLElement>;
private $availableActionList!: JQuery<HTMLElement>;
private $existingActionList!: JQuery<HTMLElement>;
private $executeButton!: JQuery<HTMLElement>;
private selectedOrActiveNoteIds: string[] | null = null;
doRender() {
this.$widget = $(TPL);
this.$includeDescendants = this.$widget.find(".include-descendants");
this.$includeDescendants.on("change", () => this.refresh());
this.$affectedNoteCount = this.$widget.find(".affected-note-count");
this.$availableActionList = this.$widget.find(".bulk-available-action-list");
this.$existingActionList = this.$widget.find(".bulk-existing-action-list");
this.$widget.on("click", "[data-action-add]", async (event) => {
const actionName = $(event.target).attr("data-action-add");
if (!actionName) {
return;
}
await bulkActionService.addAction("_bulkAction", actionName);
await this.refresh();
});
this.$executeButton = this.$widget.find(".execute-bulk-actions");
this.$executeButton.on("click", async () => {
await server.post("bulk-action/execute", {
noteIds: this.selectedOrActiveNoteIds,
includeDescendants: this.$includeDescendants.is(":checked")
});
toastService.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
closeActiveDialog();
});
}
async refresh() {
this.renderAvailableActions();
if (!this.selectedOrActiveNoteIds) {
return;
}
const { affectedNoteCount } = await server.post("bulk-action/affected-notes", {
noteIds: this.selectedOrActiveNoteIds,
includeDescendants: this.$includeDescendants.is(":checked")
}) as { affectedNoteCount: number };
this.$affectedNoteCount.text(affectedNoteCount);
const bulkActionNote = await froca.getNote("_bulkAction");
if (!bulkActionNote) {
return;
}
const actions = bulkActionService.parseActions(bulkActionNote);
this.$existingActionList.empty();
if (actions.length > 0) {
this.$existingActionList.append(...actions.map((action) => action.render()).filter((action) => action !== null));
} else {
this.$existingActionList.append($("<p>").text(t("bulk_actions.none_yet")));
}
}
renderAvailableActions() {
this.$availableActionList.empty();
for (const actionGroup of bulkActionService.ACTION_GROUPS) {
const $actionGroupList = $("<td>");
const $actionGroup = $("<tr>")
.append($("<td>").text(`${actionGroup.title}: `))
.append($actionGroupList);
for (const action of actionGroup.actions) {
$actionGroupList.append($('<button class="btn btn-sm">').attr("data-action-add", action.actionName).text(action.actionTitle));
}
this.$availableActionList.append($actionGroup);
}
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
// only refreshing deleted attrs, otherwise components update themselves
if (loadResults.getAttributeRows().find((row) => row.type === "label" && row.name === "action" && row.noteId === "_bulkAction" && row.isDeleted)) {
// this may be triggered from e.g., sync without open widget, then no need to refresh the widget
if (this.selectedOrActiveNoteIds && this.$widget.is(":visible")) {
this.refresh();
}
}
}
async openBulkActionsDialogEvent({ selectedOrActiveNoteIds }: EventData<"openBulkActionsDialog">) {
this.selectedOrActiveNoteIds = selectedOrActiveNoteIds;
this.$includeDescendants.prop("checked", false);
await this.refresh();
openDialog(this.$widget);
}
}

View File

@@ -0,0 +1,126 @@
import { useEffect, useState, useCallback } from "preact/hooks";
import { t } from "../../services/i18n";
import Modal from "../react/Modal";
import ReactBasicWidget from "../react/ReactBasicWidget";
import "./bulk_actions.css";
import { BulkActionAffectedNotes } from "@triliumnext/commons";
import server from "../../services/server";
import FormCheckbox from "../react/FormCheckbox";
import Button from "../react/Button";
import bulk_action from "../../services/bulk_action";
import toast from "../../services/toast";
import RenameNoteBulkAction from "../bulk_actions/note/rename_note";
import FNote from "../../entities/fnote";
import froca from "../../services/froca";
import useTriliumEvent from "../react/hooks";
function BulkActionComponent() {
const [ selectedOrActiveNoteIds, setSelectedOrActiveNoteIds ] = useState<string[]>();
const [ bulkActionNote, setBulkActionNote ] = useState<FNote | null>();
const [ includeDescendants, setIncludeDescendants ] = useState(false);
const [ affectedNoteCount, setAffectedNoteCount ] = useState(0);
const [ existingActions, setExistingActions ] = useState<RenameNoteBulkAction[]>([]);
const [ shown, setShown ] = useState(false);
useTriliumEvent("openBulkActionsDialog", async ({ selectedOrActiveNoteIds }) => {
setSelectedOrActiveNoteIds(selectedOrActiveNoteIds);
setBulkActionNote(await froca.getNote("_bulkAction"));
setShown(true);
});
useEffect(() => {
if (!selectedOrActiveNoteIds || !bulkActionNote) return;
server.post<BulkActionAffectedNotes>("bulk-action/affected-notes", {
noteIds: selectedOrActiveNoteIds,
includeDescendants
}).then(({ affectedNoteCount }) => setAffectedNoteCount(affectedNoteCount));
}, [ selectedOrActiveNoteIds, includeDescendants, bulkActionNote ]);
const refreshExistingActions = useCallback(() => {
if (!bulkActionNote) return;
setExistingActions(bulk_action.parseActions(bulkActionNote));
}, [bulkActionNote]);
useEffect(() => {
refreshExistingActions();
}, [refreshExistingActions]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows().find((row) =>
row.type === "label" && row.name === "action" && row.noteId === "_bulkAction")) {
refreshExistingActions();
}
}, shown);
return (
<Modal
className="bulk-actions-dialog"
size="xl"
title={t("bulk_actions.bulk_actions")}
footer={<Button text={t("bulk_actions.execute_bulk_actions")} primary />}
show={shown}
onSubmit={async () => {
await server.post("bulk-action/execute", {
noteIds: selectedOrActiveNoteIds,
includeDescendants
});
toast.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
setShown(false);
}}
onHidden={() => setShown(false)}
>
<h4>{t("bulk_actions.affected_notes")}: <span>{affectedNoteCount}</span></h4>
<FormCheckbox
name="include-descendants" label={t("bulk_actions.include_descendants")}
currentValue={includeDescendants} onChange={setIncludeDescendants}
/>
<h4>{t("bulk_actions.available_actions")}</h4>
<AvailableActionsList />
<h4>{t("bulk_actions.chosen_actions")}</h4>
<ExistingActionsList existingActions={existingActions} />
</Modal>
)
}
function AvailableActionsList() {
return <table class="bulk-available-action-list">
{bulk_action.ACTION_GROUPS.map((actionGroup) => {
return (
<tr>
<td>{ actionGroup.title }:</td>
{actionGroup.actions.map(({ actionName, actionTitle }) =>
<Button
small text={actionTitle}
onClick={() => bulk_action.addAction("_bulkAction", actionName)}
/>
)}
</tr>
);
})}
</table>;
}
function ExistingActionsList({ existingActions }: { existingActions?: RenameNoteBulkAction[] }) {
return (
<table class="bulk-existing-action-list">
{ existingActions
? existingActions
.map(action => action.doRender())
.filter(renderedAction => renderedAction !== null)
: <p>{t("bulk_actions.none_yet")}</p>
}
</table>
);
}
export default class BulkActionsDialog extends ReactBasicWidget {
get component() {
return <BulkActionComponent />
}
}

View File

@@ -0,0 +1,57 @@
import { useState } from "preact/hooks";
import Button from "../react/Button";
import Modal from "../react/Modal";
import ReactBasicWidget from "../react/ReactBasicWidget";
import { CallToAction, dismissCallToAction, getCallToActions } from "./call_to_action_definitions";
function CallToActionDialogComponent({ activeCallToActions }: { activeCallToActions: CallToAction[] }) {
if (!activeCallToActions.length) {
return <></>;
}
const [ activeIndex, setActiveIndex ] = useState(0);
const [ shown, setShown ] = useState(true);
const activeItem = activeCallToActions[activeIndex];
function goToNext() {
if (activeIndex + 1 < activeCallToActions.length) {
setActiveIndex(activeIndex + 1);
} else {
setShown(false);
}
}
return (
<Modal
className="call-to-action"
size="md"
title={activeItem.title}
show={shown}
onHidden={() => setShown(false)}
footerAlignment="between"
footer={<>
<Button text="Dismiss" onClick={async () => {
await dismissCallToAction(activeItem.id);
goToNext();
}} />
{activeItem.buttons.map((button) =>
<Button text={button.text} onClick={async () => {
await dismissCallToAction(activeItem.id);
await button.onClick();
goToNext();
}}/>
)}
</>}
>
<p>{activeItem.message}</p>
</Modal>
)
}
export class CallToActionDialog extends ReactBasicWidget {
get component() {
return <CallToActionDialogComponent activeCallToActions={getCallToActions()} />
}
}

View File

@@ -0,0 +1,116 @@
import utils from "../../services/utils";
import options from "../../services/options";
import { t } from "../../services/i18n";
/**
* A "call-to-action" is an interactive message for the user, generally to present new features.
*/
export interface CallToAction {
/**
* A unique identifier to allow the call-to-action to be dismissed by the user and then never shown again.
*/
id: string;
/**
* The title of the call-to-action, displayed as a heading in the message.
*/
title: string;
/**
* The message body of the call-to-action.
*/
message: string;
/**
* Function that determines whether the call-to-action can be displayed to the user. The check can be based on options or
* the platform of the user. If the user already dismissed this call-to-action, the value of this function doesn't matter.
*
* @returns whether to allow this call-to-action or to skip it, based on the user's environment.
*/
enabled: () => boolean;
/**
* A list of buttons to display in the footer of the modal.
*/
buttons: {
/**
* The text displayed on the button.
*/
text: string;
/**
* The listener that will be called when the button is pressed. Async methods are supported and will be awaited before proceeding to the next step.
*/
onClick: () => (void | Promise<void>);
}[];
}
function isNextTheme() {
return [ "next", "next-light", "next-dark" ].includes(options.get("theme"));
}
const CALL_TO_ACTIONS: CallToAction[] = [
{
id: "next_theme",
title: t("call_to_action.next_theme_title"),
message: t("call_to_action.next_theme_message"),
enabled: () => !isNextTheme(),
buttons: [
{
text: t("call_to_action.next_theme_button"),
async onClick() {
await options.save("theme", "next");
await options.save("backgroundEffects", "true");
utils.reloadFrontendApp("call-to-action");
}
}
]
},
{
id: "background_effects",
title: t("call_to_action.background_effects_title"),
message: t("call_to_action.background_effects_message"),
enabled: () => false,
buttons: [
{
text: t("call_to_action.background_effects_button"),
async onClick() {
await options.save("backgroundEffects", "true");
utils.restartDesktopApp();
}
}
]
}
];
/**
* Obtains the list of available call-to-actions, meaning those that are enabled based on the user's environment but also with
* without the call-to-actions already dismissed by the user.
*
* @returns a list iof call to actions to display to the user.
*/
export function getCallToActions() {
const seenCallToActions = new Set(getSeenCallToActions());
return CALL_TO_ACTIONS.filter((callToAction) =>
!seenCallToActions.has(callToAction.id) && callToAction.enabled());
}
/**
* Marks the call-to-action as dismissed by the user, meaning that {@link getCallToActions()} will no longer list this particular action.
*
* @param id the corresponding ID of the call to action.
* @returns a promise with the option saving. Generally it's best to wait for the promise to resolve before refreshing the page.
*/
export async function dismissCallToAction(id: string) {
const seenCallToActions = getSeenCallToActions();
if (seenCallToActions.find(seenId => seenId === id)) {
return;
}
seenCallToActions.push(id);
await options.save("seenCallToActions", JSON.stringify(seenCallToActions));
}
function getSeenCallToActions() {
try {
return JSON.parse(options.get("seenCallToActions")) as string[];
} catch (e) {
return [];
}
}

View File

@@ -1,140 +0,0 @@
import noteAutocompleteService from "../../services/note_autocomplete.js";
import treeService from "../../services/tree.js";
import toastService from "../../services/toast.js";
import froca from "../../services/froca.js";
import branchService from "../../services/branches.js";
import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="clone-to-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" style="max-width: 1000px" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title flex-grow-1">${t("clone_to.clone_notes_to")}</h5>
<button type="button" class="help-button" title="${t("clone_to.help_on_links")}" data-help-page="cloning-notes.html">?</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("clone_to.close")}"></button>
</div>
<form class="clone-to-form">
<div class="modal-body">
<h5>${t("clone_to.notes_to_clone")}</h5>
<ul class="clone-to-note-list" style="max-height: 200px; overflow: auto;"></ul>
<div class="form-group">
<label style="width: 100%">
${t("clone_to.target_parent_note")}
<div class="input-group">
<input class="clone-to-note-autocomplete form-control" placeholder="${t("clone_to.search_for_note_by_its_name")}">
</div>
</label>
</div>
<div class="form-group" title="${t("clone_to.cloned_note_prefix_title")}">
<label style="width: 100%">
${t("clone_to.prefix_optional")}
<input class="clone-prefix form-control" style="width: 100%;">
</label>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">${t("clone_to.clone_to_selected_note")}</button>
</div>
</form>
</div>
</div>
</div>`;
export default class CloneToDialog extends BasicWidget {
private $form!: JQuery<HTMLElement>;
private $noteAutoComplete!: JQuery<HTMLElement>;
private $clonePrefix!: JQuery<HTMLElement>;
private $noteList!: JQuery<HTMLElement>;
private clonedNoteIds: string[] | null = null;
constructor() {
super();
}
doRender() {
this.$widget = $(TPL);
this.$form = this.$widget.find(".clone-to-form");
this.$noteAutoComplete = this.$widget.find(".clone-to-note-autocomplete");
this.$clonePrefix = this.$widget.find(".clone-prefix");
this.$noteList = this.$widget.find(".clone-to-note-list");
this.$form.on("submit", () => {
const notePath = this.$noteAutoComplete.getSelectedNotePath();
if (notePath) {
this.$widget.modal("hide");
this.cloneNotesTo(notePath);
} else {
logError(t("clone_to.no_path_to_clone_to"));
}
return false;
});
}
async cloneNoteIdsToEvent({ noteIds }: EventData<"cloneNoteIdsTo">) {
if (!noteIds || noteIds.length === 0) {
noteIds = [appContext.tabManager.getActiveContextNoteId() ?? ""];
}
this.clonedNoteIds = [];
for (const noteId of noteIds) {
if (!this.clonedNoteIds.includes(noteId)) {
this.clonedNoteIds.push(noteId);
}
}
openDialog(this.$widget);
this.$noteAutoComplete.val("").trigger("focus");
this.$noteList.empty();
for (const noteId of this.clonedNoteIds) {
const note = await froca.getNote(noteId);
if (!note) {
continue;
}
this.$noteList.append($("<li>").text(note.title));
}
noteAutocompleteService.initNoteAutocomplete(this.$noteAutoComplete);
noteAutocompleteService.showRecentNotes(this.$noteAutoComplete);
}
async cloneNotesTo(notePath: string) {
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
if (!noteId || !parentNoteId) {
return;
}
const targetBranchId = await froca.getBranchId(parentNoteId, noteId);
if (!targetBranchId || !this.clonedNoteIds) {
return;
}
for (const cloneNoteId of this.clonedNoteIds) {
await branchService.cloneNoteToBranch(cloneNoteId, targetBranchId, this.$clonePrefix.val() as string);
const clonedNote = await froca.getNote(cloneNoteId);
const targetBranch = froca.getBranch(targetBranchId);
if (!clonedNote || !targetBranch) {
continue;
}
const targetNote = await targetBranch.getNote();
if (!targetNote) {
continue;
}
toastService.showMessage(t("clone_to.note_cloned", { clonedTitle: clonedNote.title, targetTitle: targetNote.title }));
}
}
}

View File

@@ -0,0 +1,120 @@
import { useRef, useState } from "preact/hooks";
import appContext from "../../components/app_context";
import { t } from "../../services/i18n";
import Modal from "../react/Modal";
import ReactBasicWidget from "../react/ReactBasicWidget";
import NoteAutocomplete from "../react/NoteAutocomplete";
import froca from "../../services/froca";
import FormGroup from "../react/FormGroup";
import FormTextBox from "../react/FormTextBox";
import Button from "../react/Button";
import { Suggestion, triggerRecentNotes } from "../../services/note_autocomplete";
import { logError } from "../../services/ws";
import tree from "../../services/tree";
import branches from "../../services/branches";
import toast from "../../services/toast";
import NoteList from "../react/NoteList";
import useTriliumEvent from "../react/hooks";
function CloneToDialogComponent() {
const [ clonedNoteIds, setClonedNoteIds ] = useState<string[]>();
const [ prefix, setPrefix ] = useState("");
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
const [ shown, setShown ] = useState(false);
const autoCompleteRef = useRef<HTMLInputElement>(null);
useTriliumEvent("cloneNoteIdsTo", ({ noteIds }) => {
if (!noteIds || noteIds.length === 0) {
noteIds = [appContext.tabManager.getActiveContextNoteId() ?? ""];
}
const clonedNoteIds: string[] = [];
for (const noteId of noteIds) {
if (!clonedNoteIds.includes(noteId)) {
clonedNoteIds.push(noteId);
}
}
setClonedNoteIds(clonedNoteIds);
setShown(true);
});
function onSubmit() {
if (!clonedNoteIds) {
return;
}
const notePath = suggestion?.notePath;
if (!notePath) {
logError(t("clone_to.no_path_to_clone_to"));
return;
}
setShown(false);
cloneNotesTo(notePath, clonedNoteIds, prefix);
}
return (
<Modal
className="clone-to-dialog"
title={t("clone_to.clone_notes_to")}
helpPageId="IakOLONlIfGI"
size="lg"
footer={<Button text={t("clone_to.clone_to_selected_note")} keyboardShortcut="Enter" />}
onSubmit={onSubmit}
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
onHidden={() => setShown(false)}
show={shown}
>
<h5>{t("clone_to.notes_to_clone")}</h5>
<NoteList style={{ maxHeight: "200px", overflow: "auto" }} noteIds={clonedNoteIds} />
<FormGroup label={t("clone_to.target_parent_note")}>
<NoteAutocomplete
placeholder={t("clone_to.search_for_note_by_its_name")}
onChange={setSuggestion}
inputRef={autoCompleteRef}
/>
</FormGroup>
<FormGroup label={t("clone_to.prefix_optional")} title={t("clone_to.cloned_note_prefix_title")}>
<FormTextBox name="clone-prefix" onChange={setPrefix} />
</FormGroup>
</Modal>
)
}
export default class CloneToDialog extends ReactBasicWidget {
get component() {
return <CloneToDialogComponent />;
}
}
async function cloneNotesTo(notePath: string, clonedNoteIds: string[], prefix?: string) {
const { noteId, parentNoteId } = tree.getNoteIdAndParentIdFromUrl(notePath);
if (!noteId || !parentNoteId) {
return;
}
const targetBranchId = await froca.getBranchId(parentNoteId, noteId);
if (!targetBranchId || !clonedNoteIds) {
return;
}
for (const cloneNoteId of clonedNoteIds) {
await branches.cloneNoteToBranch(cloneNoteId, targetBranchId, prefix);
const clonedNote = await froca.getNote(cloneNoteId);
const targetBranch = froca.getBranch(targetBranchId);
if (!clonedNote || !targetBranch) {
continue;
}
const targetNote = await targetBranch.getNote();
if (!targetNote) {
continue;
}
toast.showMessage(t("clone_to.note_cloned", { clonedTitle: clonedNote.title, targetTitle: targetNote.title }));
}
}

View File

@@ -1,151 +0,0 @@
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import { Modal } from "bootstrap";
const DELETE_NOTE_BUTTON_CLASS = "confirm-dialog-delete-note";
const TPL = /*html*/`
<div class="confirm-dialog modal mx-auto" tabindex="-1" role="dialog" style="z-index: 2000;">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${t("confirm.confirmation")}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("confirm.close")}"></button>
</div>
<div class="modal-body">
<div class="confirm-dialog-content"></div>
<div class="confirm-dialog-custom"></div>
</div>
<div class="modal-footer">
<button class="confirm-dialog-cancel-button btn btn-sm">${t("confirm.cancel")}</button>
&nbsp;
<button class="confirm-dialog-ok-button btn btn-primary btn-sm">${t("confirm.ok")}</button>
</div>
</div>
</div>
</div>`;
export type ConfirmDialogResult = false | ConfirmDialogOptions;
export type ConfirmDialogCallback = (val?: ConfirmDialogResult) => void;
export interface ConfirmDialogOptions {
confirmed: boolean;
isDeleteNoteChecked: boolean;
}
// For "showConfirmDialog"
export interface ConfirmWithMessageOptions {
message: string | HTMLElement | JQuery<HTMLElement>;
callback: ConfirmDialogCallback;
}
export interface ConfirmWithTitleOptions {
title: string;
callback: ConfirmDialogCallback;
}
export default class ConfirmDialog extends BasicWidget {
private resolve: ConfirmDialogCallback | null;
private modal!: Modal;
private $originallyFocused!: JQuery<HTMLElement> | null;
private $confirmContent!: JQuery<HTMLElement>;
private $okButton!: JQuery<HTMLElement>;
private $cancelButton!: JQuery<HTMLElement>;
private $custom!: JQuery<HTMLElement>;
constructor() {
super();
this.resolve = null;
this.$originallyFocused = null; // element focused before the dialog was opened, so we can return to it afterward
}
doRender() {
this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$confirmContent = this.$widget.find(".confirm-dialog-content");
this.$okButton = this.$widget.find(".confirm-dialog-ok-button");
this.$cancelButton = this.$widget.find(".confirm-dialog-cancel-button");
this.$custom = this.$widget.find(".confirm-dialog-custom");
this.$widget.on("shown.bs.modal", () => this.$okButton.trigger("focus"));
this.$widget.on("hidden.bs.modal", () => {
if (this.resolve) {
this.resolve(false);
}
if (this.$originallyFocused) {
this.$originallyFocused.trigger("focus");
this.$originallyFocused = null;
}
});
this.$cancelButton.on("click", () => this.doResolve(false));
this.$okButton.on("click", () => this.doResolve(true));
}
showConfirmDialogEvent({ message, callback }: ConfirmWithMessageOptions) {
this.$originallyFocused = $(":focus");
this.$custom.hide();
glob.activeDialog = this.$widget;
if (typeof message === "string") {
message = $("<div>").text(message);
}
this.$confirmContent.empty().append(message);
this.modal.show();
this.resolve = callback;
}
showConfirmDeleteNoteBoxWithNoteDialogEvent({ title, callback }: ConfirmWithTitleOptions) {
glob.activeDialog = this.$widget;
this.$confirmContent.text(`${t("confirm.are_you_sure_remove_note", { title: title })}`);
this.$custom
.empty()
.append("<br/>")
.append(
$("<div>")
.addClass("form-check")
.append(
$("<label>")
.addClass("form-check-label")
.attr("style", "text-decoration: underline dotted var(--main-text-color)")
.attr("title", `${t("confirm.if_you_dont_check")}`)
.append($("<input>").attr("type", "checkbox").addClass(`form-check-input ${DELETE_NOTE_BUTTON_CLASS}`))
.append(`${t("confirm.also_delete_note")}`)
)
);
this.$custom.show();
this.modal.show();
this.resolve = callback;
}
doResolve(ret: boolean) {
if (this.resolve) {
this.resolve({
confirmed: ret,
isDeleteNoteChecked: this.$widget.find(`.${DELETE_NOTE_BUTTON_CLASS}:checked`).length > 0
});
}
this.resolve = null;
this.modal.hide();
}
}

View File

@@ -0,0 +1,102 @@
import ReactBasicWidget from "../react/ReactBasicWidget";
import Modal from "../react/Modal";
import Button from "../react/Button";
import { t } from "../../services/i18n";
import { useState } from "preact/hooks";
import FormCheckbox from "../react/FormCheckbox";
import useTriliumEvent from "../react/hooks";
interface ConfirmDialogProps {
title?: string;
message?: string | HTMLElement;
callback?: ConfirmDialogCallback;
isConfirmDeleteNoteBox?: boolean;
}
function ConfirmDialogComponent() {
const [ opts, setOpts ] = useState<ConfirmDialogProps>();
const [ isDeleteNoteChecked, setIsDeleteNoteChecked ] = useState(false);
const [ shown, setShown ] = useState(false);
function showDialog(title: string | null, message: MessageType, callback: ConfirmDialogCallback, isConfirmDeleteNoteBox: boolean) {
setOpts({
title: title ?? undefined,
message: (typeof message === "object" && "length" in message ? message[0] : message),
callback,
isConfirmDeleteNoteBox
});
setShown(true);
}
useTriliumEvent("showConfirmDialog", ({ message, callback }) => showDialog(null, message, callback, false));
useTriliumEvent("showConfirmDeleteNoteBoxWithNoteDialog", ({ title, callback }) => showDialog(title, t("confirm.are_you_sure_remove_note", { title: title }), callback, true));
return (
<Modal
className="confirm-dialog"
title={opts?.title ?? t("confirm.confirmation")}
size="md"
zIndex={2000}
scrollable={true}
onHidden={() => {
opts?.callback?.({
confirmed: false,
isDeleteNoteChecked
});
setShown(false);
}}
footer={<>
<Button text={t("confirm.cancel")} onClick={() => setShown(false)} />
<Button text={t("confirm.ok")} onClick={() => {
opts?.callback?.({
confirmed: true,
isDeleteNoteChecked
});
setShown(false);
}} />
</>}
show={shown}
stackable
>
{!opts?.message || typeof opts?.message === "string"
? <div>{(opts?.message as string) ?? ""}</div>
: <div dangerouslySetInnerHTML={{ __html: opts?.message.outerHTML ?? "" }} />}
{opts?.isConfirmDeleteNoteBox && (
<FormCheckbox
name="confirm-dialog-delete-note"
label={t("confirm.also_delete_note")}
hint={t("confirm.if_you_dont_check")}
currentValue={isDeleteNoteChecked} onChange={setIsDeleteNoteChecked} />
)}
</Modal>
);
}
export type ConfirmDialogResult = false | ConfirmDialogOptions;
export type ConfirmDialogCallback = (val?: ConfirmDialogResult) => void;
type MessageType = string | HTMLElement | JQuery<HTMLElement>;
export interface ConfirmDialogOptions {
confirmed: boolean;
isDeleteNoteChecked: boolean;
}
export interface ConfirmWithMessageOptions {
message: MessageType;
callback: ConfirmDialogCallback;
}
// For "showConfirmDialog"
export interface ConfirmWithTitleOptions {
title: string;
callback: ConfirmDialogCallback;
}
export default class ConfirmDialog extends ReactBasicWidget {
get component() {
return <ConfirmDialogComponent />;
}
}

View File

@@ -1,198 +0,0 @@
import server from "../../services/server.js";
import froca from "../../services/froca.js";
import linkService from "../../services/link.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import type { FAttributeRow } from "../../entities/fattribute.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
// TODO: Use common with server.
interface Response {
noteIdsToBeDeleted: string[];
brokenRelations: FAttributeRow[];
}
export interface ResolveOptions {
proceed: boolean;
deleteAllClones?: boolean;
eraseNotes?: boolean;
}
interface ShowDeleteNotesDialogOpts {
branchIdsToDelete: string[];
callback: (opts: ResolveOptions) => void;
forceDeleteAllClones: boolean;
}
const TPL = /*html*/`
<div class="delete-notes-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-scrollable modal-xl" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">${t("delete_notes.delete_notes_preview")}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("delete_notes.close")}"></button>
</div>
<div class="modal-body">
<div class="form-checkbox">
<label for="delete-all-clones" class="form-check-label tn-checkbox">
<input id="delete-all-clones" class="delete-all-clones form-check-input" value="1" type="checkbox">
${t("delete_notes.delete_all_clones_description")}
</label>
</div>
<div class="form-checkbox" style="margin-bottom: 1rem">
<label for="erase-notes" class="form-check-label tn-checkbox">
<input id="erase-notes" class="erase-notes form-check-input" value="1" type="checkbox">
${t("delete_notes.erase_notes_warning")}
</label>
</div>
<div class="delete-notes-list-wrapper">
<h4>${t("delete_notes.notes_to_be_deleted", { noteCount: '<span class="deleted-notes-count"></span>' })}</h4>
<ul class="delete-notes-list" style="max-height: 200px; overflow: auto;"></ul>
</div>
<div class="no-note-to-delete-wrapper alert alert-info">
${t("delete_notes.no_note_to_delete")}
</div>
<div class="broken-relations-wrapper">
<div class="alert alert-danger">
<h4>${t("delete_notes.broken_relations_to_be_deleted", { relationCount: '<span class="broke-relations-count"></span>' })}</h4>
<ul class="broken-relations-list" style="max-height: 200px; overflow: auto;"></ul>
</div>
</div>
</div>
<div class="modal-footer">
<button class="delete-notes-dialog-cancel-button btn btn-sm">${t("delete_notes.cancel")}</button>
&nbsp;
<button class="delete-notes-dialog-ok-button btn btn-primary btn-sm">${t("delete_notes.ok")}</button>
</div>
</div>
</div>
</div>`;
export default class DeleteNotesDialog extends BasicWidget {
private branchIds: string[] | null;
private resolve!: (options: ResolveOptions) => void;
private $content!: JQuery<HTMLElement>;
private $okButton!: JQuery<HTMLElement>;
private $cancelButton!: JQuery<HTMLElement>;
private $deleteNotesList!: JQuery<HTMLElement>;
private $brokenRelationsList!: JQuery<HTMLElement>;
private $deletedNotesCount!: JQuery<HTMLElement>;
private $noNoteToDeleteWrapper!: JQuery<HTMLElement>;
private $deleteNotesListWrapper!: JQuery<HTMLElement>;
private $brokenRelationsListWrapper!: JQuery<HTMLElement>;
private $brokenRelationsCount!: JQuery<HTMLElement>;
private $deleteAllClones!: JQuery<HTMLElement>;
private $eraseNotes!: JQuery<HTMLElement>;
private forceDeleteAllClones?: boolean;
constructor() {
super();
this.branchIds = null;
}
doRender() {
this.$widget = $(TPL);
this.$content = this.$widget.find(".recent-changes-content");
this.$okButton = this.$widget.find(".delete-notes-dialog-ok-button");
this.$cancelButton = this.$widget.find(".delete-notes-dialog-cancel-button");
this.$deleteNotesList = this.$widget.find(".delete-notes-list");
this.$brokenRelationsList = this.$widget.find(".broken-relations-list");
this.$deletedNotesCount = this.$widget.find(".deleted-notes-count");
this.$noNoteToDeleteWrapper = this.$widget.find(".no-note-to-delete-wrapper");
this.$deleteNotesListWrapper = this.$widget.find(".delete-notes-list-wrapper");
this.$brokenRelationsListWrapper = this.$widget.find(".broken-relations-wrapper");
this.$brokenRelationsCount = this.$widget.find(".broke-relations-count");
this.$deleteAllClones = this.$widget.find(".delete-all-clones");
this.$eraseNotes = this.$widget.find(".erase-notes");
this.$widget.on("shown.bs.modal", () => this.$okButton.trigger("focus"));
this.$cancelButton.on("click", () => {
closeActiveDialog();
this.resolve({ proceed: false });
});
this.$okButton.on("click", () => {
closeActiveDialog();
this.resolve({
proceed: true,
deleteAllClones: this.forceDeleteAllClones || this.isDeleteAllClonesChecked(),
eraseNotes: this.isEraseNotesChecked()
});
});
this.$deleteAllClones.on("click", () => this.renderDeletePreview());
}
async renderDeletePreview() {
const response = await server.post<Response>("delete-notes-preview", {
branchIdsToDelete: this.branchIds,
deleteAllClones: this.forceDeleteAllClones || this.isDeleteAllClonesChecked()
});
this.$deleteNotesList.empty();
this.$brokenRelationsList.empty();
this.$deleteNotesListWrapper.toggle(response.noteIdsToBeDeleted.length > 0);
this.$noNoteToDeleteWrapper.toggle(response.noteIdsToBeDeleted.length === 0);
for (const note of await froca.getNotes(response.noteIdsToBeDeleted)) {
this.$deleteNotesList.append($("<li>").append(await linkService.createLink(note.noteId, { showNotePath: true })));
}
this.$deletedNotesCount.text(response.noteIdsToBeDeleted.length);
this.$brokenRelationsListWrapper.toggle(response.brokenRelations.length > 0);
this.$brokenRelationsCount.text(response.brokenRelations.length);
await froca.getNotes(response.brokenRelations.map((br) => br.noteId));
for (const attr of response.brokenRelations) {
this.$brokenRelationsList.append(
$("<li>").html(
t("delete_notes.deleted_relation_text", {
note: (await linkService.createLink(attr.value)).html(),
relation: `<code>${attr.name}</code>`,
source: (await linkService.createLink(attr.noteId)).html()
})
)
);
}
}
async showDeleteNotesDialogEvent({ branchIdsToDelete, callback, forceDeleteAllClones }: ShowDeleteNotesDialogOpts) {
this.branchIds = branchIdsToDelete;
this.forceDeleteAllClones = forceDeleteAllClones;
await this.renderDeletePreview();
openDialog(this.$widget);
this.$deleteAllClones.prop("checked", !!forceDeleteAllClones).prop("disabled", !!forceDeleteAllClones);
this.$eraseNotes.prop("checked", false);
this.resolve = callback;
}
isDeleteAllClonesChecked() {
return this.$deleteAllClones.is(":checked");
}
isEraseNotesChecked() {
return this.$eraseNotes.is(":checked");
}
}

View File

@@ -0,0 +1,181 @@
import { useRef, useState, useEffect } from "preact/hooks";
import { t } from "../../services/i18n.js";
import FormCheckbox from "../react/FormCheckbox.js";
import Modal from "../react/Modal.js";
import ReactBasicWidget from "../react/ReactBasicWidget.js";
import type { DeleteNotesPreview } from "@triliumnext/commons";
import server from "../../services/server.js";
import froca from "../../services/froca.js";
import FNote from "../../entities/fnote.js";
import link from "../../services/link.js";
import Button from "../react/Button.jsx";
import Alert from "../react/Alert.jsx";
import useTriliumEvent from "../react/hooks.jsx";
export interface ResolveOptions {
proceed: boolean;
deleteAllClones?: boolean;
eraseNotes?: boolean;
}
interface ShowDeleteNotesDialogOpts {
branchIdsToDelete?: string[];
callback?: (opts: ResolveOptions) => void;
forceDeleteAllClones?: boolean;
}
interface BrokenRelationData {
note: string;
relation: string;
source: string;
}
function DeleteNotesDialogComponent() {
const [ opts, setOpts ] = useState<ShowDeleteNotesDialogOpts>({});
const [ deleteAllClones, setDeleteAllClones ] = useState(false);
const [ eraseNotes, setEraseNotes ] = useState(!!opts.forceDeleteAllClones);
const [ brokenRelations, setBrokenRelations ] = useState<DeleteNotesPreview["brokenRelations"]>([]);
const [ noteIdsToBeDeleted, setNoteIdsToBeDeleted ] = useState<DeleteNotesPreview["noteIdsToBeDeleted"]>([]);
const [ shown, setShown ] = useState(false);
const okButtonRef = useRef<HTMLButtonElement>(null);
useTriliumEvent("showDeleteNotesDialog", (opts) => {
setOpts(opts);
setShown(true);
})
useEffect(() => {
const { branchIdsToDelete, forceDeleteAllClones } = opts;
if (!branchIdsToDelete || branchIdsToDelete.length === 0) {
return;
}
server.post<DeleteNotesPreview>("delete-notes-preview", {
branchIdsToDelete,
deleteAllClones: forceDeleteAllClones || deleteAllClones
}).then(response => {
setBrokenRelations(response.brokenRelations);
setNoteIdsToBeDeleted(response.noteIdsToBeDeleted);
});
}, [ opts, deleteAllClones ]);
return (
<Modal
className="delete-notes-dialog"
size="xl"
scrollable
title={t("delete_notes.delete_notes_preview")}
onShown={() => okButtonRef.current?.focus()}
onHidden={() => {
opts.callback?.({ proceed: false })
setShown(false);
}}
footer={<>
<Button text={t("delete_notes.cancel")}
onClick={() => setShown(false)} />
<Button text={t("delete_notes.ok")} primary
buttonRef={okButtonRef}
onClick={() => {
opts.callback?.({ proceed: true, deleteAllClones, eraseNotes });
setShown(false);
}} />
</>}
show={shown}
>
<FormCheckbox name="delete-all-clones" label={t("delete_notes.delete_all_clones_description")}
currentValue={deleteAllClones} onChange={setDeleteAllClones}
/>
<FormCheckbox
name="erase-notes" label={t("delete_notes.erase_notes_warning")}
disabled={opts.forceDeleteAllClones}
currentValue={eraseNotes} onChange={setEraseNotes}
/>
<DeletedNotes noteIdsToBeDeleted={noteIdsToBeDeleted} />
<BrokenRelations brokenRelations={brokenRelations} />
</Modal>
);
}
function DeletedNotes({ noteIdsToBeDeleted }: { noteIdsToBeDeleted: DeleteNotesPreview["noteIdsToBeDeleted"] }) {
const [ noteLinks, setNoteLinks ] = useState<string[]>([]);
useEffect(() => {
froca.getNotes(noteIdsToBeDeleted).then(async (notes: FNote[]) => {
const noteLinks: string[] = [];
for (const note of notes) {
noteLinks.push((await link.createLink(note.noteId, { showNotePath: true })).html());
}
setNoteLinks(noteLinks);
});
}, [noteIdsToBeDeleted]);
if (noteIdsToBeDeleted.length) {
return (
<div className="delete-notes-list-wrapper">
<h4>{t("delete_notes.notes_to_be_deleted", { notesCount: noteIdsToBeDeleted.length })}</h4>
<ul className="delete-notes-list" style={{ maxHeight: "200px", overflow: "auto" }}>
{noteLinks.map((link, index) => (
<li key={index} dangerouslySetInnerHTML={{ __html: link }} />
))}
</ul>
</div>
);
} else {
return (
<Alert type="info">
{t("delete_notes.no_note_to_delete")}
</Alert>
)
}
}
function BrokenRelations({ brokenRelations }: { brokenRelations: DeleteNotesPreview["brokenRelations"] }) {
const [ notesWithBrokenRelations, setNotesWithBrokenRelations ] = useState<BrokenRelationData[]>([]);
useEffect(() => {
const noteIds = brokenRelations
.map(relation => relation.noteId)
.filter(noteId => noteId) as string[];
froca.getNotes(noteIds).then(async (notes) => {
const notesWithBrokenRelations: BrokenRelationData[] = [];
for (const attr of brokenRelations) {
notesWithBrokenRelations.push({
note: (await link.createLink(attr.value)).html(),
relation: `<code>${attr.name}</code>`,
source: (await link.createLink(attr.noteId)).html()
});
}
setNotesWithBrokenRelations(notesWithBrokenRelations);
});
}, [brokenRelations]);
if (brokenRelations.length) {
return (
<Alert type="danger" title={t("delete_notes.broken_relations_to_be_deleted", { relationCount: brokenRelations.length })}>
<ul className="broken-relations-list" style={{ maxHeight: "200px", overflow: "auto" }}>
{brokenRelations.map((_, index) => {
return (
<li key={index}>
<span dangerouslySetInnerHTML={{ __html: t("delete_notes.deleted_relation_text", notesWithBrokenRelations[index] as unknown as Record<string, string>) }} />
</li>
);
})}
</ul>
</Alert>
);
} else {
return <></>;
}
}
export default class DeleteNotesDialog extends ReactBasicWidget {
get component() {
return <DeleteNotesDialogComponent />;
}
}

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