Compare commits

..

397 Commits

Author SHA1 Message Date
renovate[bot]
5f16ecf02d fix(deps): update dependency react-window to v2.2.7 2026-02-14 02:06:40 +00:00
Elian Doran
5da9963f31 fix(website): web clipper URL incorrect 2026-02-13 19:43:07 +02:00
Elian Doran
34e885812f fix(global_menu): advanced menu not accessible in other languages (closes #8694) 2026-02-13 19:40:51 +02:00
Elian Doran
a9ac11452d fix(relation_map): crash when valid JSON, but missing data (closes #8702) 2026-02-13 19:32:29 +02:00
Elian Doran
04b91308b1 chore(search): remove redundant border on mobile 2026-02-13 19:26:08 +02:00
Elian Doran
b09ef222f5 fix(search): errors not displayed (closes #8704) 2026-02-13 19:22:21 +02:00
Elian Doran
2d0ed06d50 Update Node.js to v24.13.1 (#8629) 2026-02-13 13:23:22 +02:00
renovate[bot]
8dd7cf6085 Update Node.js to v24.13.1 2026-02-13 05:50:03 +00:00
Elian Doran
4999bd4f1e Translations update from Hosted Weblate (#8701) 2026-02-13 07:48:43 +02:00
green
22f408addb Translated using Weblate (Japanese)
Currently translated at 100.0% (1777 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-13 06:48:06 +01:00
AggelosPnS
9ca1dbe638 Translated using Weblate (Greek)
Currently translated at 8.9% (35 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/el/
2026-02-13 06:48:06 +01:00
AggelosPnS
26662952e3 Translated using Weblate (Greek)
Currently translated at 2.0% (36 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/el/
2026-02-13 06:48:06 +01:00
Hosted Weblate
f0c9fa4ca3 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/
2026-02-13 06:48:06 +01:00
Elian Doran
b51aa1dd71 Update pnpm to v10.29.3 (#8683) 2026-02-13 07:48:00 +02:00
Elian Doran
846253c9e3 Update dependency @codemirror/view to v6.39.14 (#8695) 2026-02-13 07:47:46 +02:00
Elian Doran
6ab6ea97ac Update dependency i18next to v25.8.6 (#8696) 2026-02-13 07:47:31 +02:00
Elian Doran
bf41f70b98 Update dependency wxt to v0.20.17 (#8697) 2026-02-13 07:46:48 +02:00
Elian Doran
67ddbedd08 Update dependency dotenv to v17.3.1 (#8698) 2026-02-13 07:45:17 +02:00
renovate[bot]
2573e219dc Update dependency dotenv to v17.3.1 2026-02-13 01:02:52 +00:00
renovate[bot]
7e368678ab Update dependency wxt to v0.20.17 2026-02-13 01:02:01 +00:00
renovate[bot]
4a9fcf7ab6 Update dependency i18next to v25.8.6 2026-02-13 01:01:10 +00:00
renovate[bot]
65856c61c5 Update dependency @codemirror/view to v6.39.14 2026-02-13 01:00:19 +00:00
renovate[bot]
b5a97bffab Update pnpm to v10.29.3 2026-02-12 17:58:49 +00:00
Elian Doran
30ccd3487a Translations update from Hosted Weblate (#8666) 2026-02-12 07:50:46 +02:00
Toto Yullian
75e012f2c9 Translated using Weblate (Indonesian)
Currently translated at 3.5% (63 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/id/
2026-02-12 06:48:36 +01:00
Ulices
5ecb1d1e2d Translated using Weblate (Spanish)
Currently translated at 100.0% (1777 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2026-02-12 06:48:36 +01:00
Toto Yullian
f8c24c838a Translated using Weblate (Indonesian)
Currently translated at 41.1% (65 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/id/
2026-02-12 06:48:35 +01:00
noobhjy
4ad9cfcdf4 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1777 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2026-02-12 06:48:34 +01:00
Toto Yullian
a57253dd35 Translated using Weblate (Indonesian)
Currently translated at 65.5% (76 of 116 strings)

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/id/
2026-02-12 06:48:33 +01:00
Marcel
222e65bd45 Translated using Weblate (German)
Currently translated at 100.0% (1777 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2026-02-12 06:48:32 +01:00
Jason Kuanca
3192ea3383 Translated using Weblate (Indonesian)
Currently translated at 60.3% (70 of 116 strings)

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/id/
2026-02-12 06:48:32 +01:00
green
3a27f873cd Translated using Weblate (Japanese)
Currently translated at 100.0% (1776 of 1776 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-12 06:48:31 +01:00
noobhjy
e8fb279036 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1776 of 1776 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2026-02-12 06:48:30 +01:00
Ulices
21ec7078d2 Translated using Weblate (Spanish)
Currently translated at 100.0% (1776 of 1776 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2026-02-12 06:48:29 +01:00
Aindriú Mac Giolla Eoin
94937e9fa4 Translated using Weblate (Irish)
Currently translated at 100.0% (1774 of 1774 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
2026-02-12 06:48:28 +01:00
Giovi
36401a20b8 Translated using Weblate (Italian)
Currently translated at 99.7% (1769 of 1774 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/it/
2026-02-12 06:48:27 +01:00
noobhjy
8d1c4e4661 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1774 of 1774 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2026-02-12 06:48:27 +01:00
Kuzma Simonov
14362060c8 Translated using Weblate (Russian)
Currently translated at 99.3% (1763 of 1774 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2026-02-12 06:48:26 +01:00
Kuzma Simonov
de47e94f62 Translated using Weblate (Russian)
Currently translated at 99.3% (157 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ru/
2026-02-12 06:48:25 +01:00
Giovi
c78ed78bf6 Translated using Weblate (Italian)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/it/
2026-02-12 06:48:24 +01:00
Ulices
7674c95124 Translated using Weblate (Spanish)
Currently translated at 100.0% (1774 of 1774 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2026-02-12 06:48:24 +01:00
Giovi
ec522c20b2 Translated using Weblate (Italian)
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/it/
2026-02-12 06:48:23 +01:00
green
81f9578526 Translated using Weblate (Japanese)
Currently translated at 100.0% (1774 of 1774 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-12 06:48:22 +01:00
Hosted Weblate
20bca751d4 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/
2026-02-12 06:48:22 +01:00
Francis C.
49b5c49776 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/zh_Hant/
2026-02-12 06:48:20 +01:00
Francis C.
5d514fae61 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2026-02-12 06:48:20 +01:00
Hosted Weblate
9e17e93dd7 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/
2026-02-12 06:48:19 +01:00
Elian Doran
7f70c641dc chore(deps): update dependency dotenv to v17.2.4 (#8630) 2026-02-12 07:47:59 +02:00
Elian Doran
7e3af4b7bc chore(deps): update dependency happy-dom to v20.6.1 (#8662) 2026-02-12 07:47:18 +02:00
Elian Doran
59ff4c0aef chore(deps): update dependency wxt to v0.20.15 (#8682) 2026-02-12 07:46:46 +02:00
Elian Doran
875e6b8e53 fix(deps): update dependency i18next to v25.8.5 (#8685) 2026-02-12 07:45:10 +02:00
Elian Doran
fafc44de7c fix(deps): update dependency marked to v17.0.2 (#8686) 2026-02-12 07:44:53 +02:00
Elian Doran
e0d7eb10d5 fix(deps): update dependency mathlive to v0.108.3 (#8687) 2026-02-12 07:44:40 +02:00
Elian Doran
ae01eb28a3 chore(deps): update dependency @redocly/cli to v2.18.0 (#8688) 2026-02-12 07:44:20 +02:00
Elian Doran
637c66c04f chore(deps): update dependency electron to v40.4.0 (#8689) 2026-02-12 07:44:01 +02:00
renovate[bot]
1c061e4428 chore(deps): update dependency electron to v40.4.0 2026-02-12 02:18:16 +00:00
renovate[bot]
cbe0626572 chore(deps): update dependency @redocly/cli to v2.18.0 2026-02-12 02:17:42 +00:00
renovate[bot]
86dc98174a fix(deps): update dependency mathlive to v0.108.3 2026-02-12 02:17:07 +00:00
renovate[bot]
f0ac8ea977 fix(deps): update dependency marked to v17.0.2 2026-02-12 02:16:29 +00:00
renovate[bot]
35d4c2cdfc fix(deps): update dependency i18next to v25.8.5 2026-02-12 02:15:53 +00:00
renovate[bot]
394f7c0d09 chore(deps): update dependency wxt to v0.20.15 2026-02-12 02:13:10 +00:00
Elian Doran
b83eee9bdc Merge branch 'main' of https://github.com/TriliumNext/Trilium 2026-02-11 20:21:55 +02:00
Elian Doran
e41b2e8d31 fix(options/shortcuts): filter text box forces lower case 2026-02-11 20:21:52 +02:00
Elian Doran
d93cec2bfd feat(options/shortcuts): add no results 2026-02-11 20:19:53 +02:00
Elian Doran
9eb87a39cd chore(options/shortcuts): refactor filtering 2026-02-11 20:15:39 +02:00
Elian Doran
07818ec1df fix(options): unnecessary full-height 2026-02-11 20:11:46 +02:00
Elian Doran
d4052dbe37 feat(options/shortcuts): equal height 2026-02-11 20:04:09 +02:00
Elian Doran
656f5e0a7f feat(options/shortcuts): make header and footer sticky (closes #8675) 2026-02-11 20:02:37 +02:00
Elian Doran
0cc5e4dac3 chore(deps): update dependency @ckeditor/ckeditor5-dev-build-tools to v54.3.3 (#8609) 2026-02-11 07:51:46 +02:00
Elian Doran
8bbfff3cb2 fix(deps): update dependency i18next to v25.8.4 (#8611) 2026-02-11 07:51:31 +02:00
renovate[bot]
93059798b0 chore(deps): update dependency dotenv to v17.2.4 2026-02-11 05:51:15 +00:00
Elian Doran
33fae88cad chore(deps): update dependency stylelint to v17.2.0 (#8612) 2026-02-11 07:51:15 +02:00
Elian Doran
4feb23e9ca fix(deps): update dependency @preact/signals to v2.7.1 (#8619) 2026-02-11 07:50:04 +02:00
Elian Doran
c2b6b7ba72 chore(deps): update dependency esbuild to v0.27.3 (#8631) 2026-02-11 07:48:22 +02:00
Elian Doran
fb76aee258 chore(deps): update dependency @anthropic-ai/sdk to v0.74.0 (#8633) 2026-02-11 07:47:46 +02:00
Elian Doran
320b1829cc chore(deps): update dependency openai to v6.21.0 (#8634) 2026-02-11 07:47:16 +02:00
Elian Doran
601f0255a4 chore(deps): update dependency @playwright/test to v1.58.2 (#8642) 2026-02-11 07:46:46 +02:00
Elian Doran
b6cc2b227a chore(deps): update dependency wxt to v0.20.14 (#8643) 2026-02-11 07:46:36 +02:00
renovate[bot]
cc7da4b948 chore(deps): update dependency happy-dom to v20.6.1 2026-02-11 05:46:19 +00:00
Elian Doran
1ae3be2fda fix(deps): update codemirror (#8644) 2026-02-11 07:44:55 +02:00
Elian Doran
5b4d35ea86 chore(deps): update dependency @redocly/cli to v2.17.0 (#8645) 2026-02-11 07:44:38 +02:00
Elian Doran
00735e6c8e chore(deps): update dependency axios to v1.13.5 (#8661) 2026-02-11 07:44:27 +02:00
Elian Doran
66a42a38c9 chore(deps): update dependency @smithy/middleware-retry to v4.4.31 (#8669) 2026-02-11 07:43:54 +02:00
Elian Doran
05af1fba80 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.11 (#8670) 2026-02-11 07:43:45 +02:00
Elian Doran
3b2dd0f5e9 chore(deps): update pnpm to v10.29.2 (#8671) 2026-02-11 07:43:33 +02:00
Elian Doran
1493a66a36 chore(deps): update dependency webdriverio to v9.24.0 (#8672) 2026-02-11 07:43:15 +02:00
Elian Doran
b4bf103fd8 chore(deps): update typescript-eslint monorepo to v8.55.0 (#8673) 2026-02-11 07:43:00 +02:00
renovate[bot]
94286becfd chore(deps): update typescript-eslint monorepo to v8.55.0 2026-02-11 01:04:05 +00:00
renovate[bot]
a68aade58c chore(deps): update dependency webdriverio to v9.24.0 2026-02-11 01:03:25 +00:00
renovate[bot]
014201edf4 chore(deps): update pnpm to v10.29.2 2026-02-11 01:02:09 +00:00
renovate[bot]
8ae6297148 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.11 2026-02-11 01:01:56 +00:00
renovate[bot]
5e0300aa8e chore(deps): update dependency @smithy/middleware-retry to v4.4.31 2026-02-11 01:01:18 +00:00
renovate[bot]
80a7e18413 chore(deps): update dependency openai to v6.21.0 2026-02-10 21:08:56 +00:00
Elian Doran
43be0a1a3f chore(desktop): disable dev tools for the print window 2026-02-10 17:29:00 +02:00
Elian Doran
5eb32744c3 feat(desktop): display print errors 2026-02-10 17:26:58 +02:00
Elian Doran
89d39f5f2b feat(desktop): add logs when printing 2026-02-10 17:12:28 +02:00
Elian Doran
1f4900dd1e fix(desktop): print failing on upgrade due to cache issues 2026-02-10 16:30:16 +02:00
renovate[bot]
1c561c1483 chore(deps): update dependency stylelint to v17.2.0 2026-02-10 11:44:18 +00:00
Adorian Doran
6baaf60b67 style/web view setup: update icon 2026-02-10 02:53:46 +02:00
Adorian Doran
dde73f6c2b style/attachments: tweak 2026-02-10 02:46:10 +02:00
Adorian Doran
f445a49b34 client/notes/web view: create a web view setup form 2026-02-10 02:45:43 +02:00
Adorian Doran
29016d1cf5 style/refactor: promote centered form as global style 2026-02-10 01:26:07 +02:00
Adorian Doran
c06435046b style/alert bars: use a global style, tweak spacing 2026-02-09 23:39:22 +02:00
Adorian Doran
134422802f style/protected session password request: improve layout and appearance 2026-02-09 23:14:32 +02:00
Adorian Doran
5e00d6a305 style/collapsible: use low profile style for the expand/collapse button 2026-02-09 20:34:58 +02:00
Adorian Doran
b5f0137d8e Merge branch 'main' of https://github.com/TriliumNext/Trilium 2026-02-09 20:33:32 +02:00
Adorian Doran
081d041cbc style/buttons: define a style for low profile buttons 2026-02-09 20:33:24 +02:00
Elian Doran
95e733a67c Merge branch 'main' of https://github.com/TriliumNext/Trilium 2026-02-09 20:22:43 +02:00
Elian Doran
a664057312 fix(website): missing RPM GPG key (closes #8618) 2026-02-09 20:22:41 +02:00
Elian Doran
5b1a2d93bf fix(tree): child note badge overlapping text (closes #8567) 2026-02-09 20:20:20 +02:00
Adorian Doran
0323f95828 style/status bar: do not wrap the action buttons text 2026-02-09 19:57:13 +02:00
Elian Doran
375838449f fix(geomap): z-index issues with toolbar and buttons (closes #8649) 2026-02-09 19:55:57 +02:00
Adorian Doran
4562de8c2c close #2137 2026-02-09 19:19:41 +02:00
Adorian Doran
68d21669e7 style/similar notes: fix font size variation according to similarity 2026-02-09 19:12:47 +02:00
renovate[bot]
625e0cf159 chore(deps): update dependency @redocly/cli to v2.17.0 2026-02-09 13:58:05 +00:00
renovate[bot]
551ef00c61 fix(deps): update dependency @preact/signals to v2.7.1 2026-02-09 10:46:41 +00:00
renovate[bot]
10518f6364 chore(deps): update dependency axios to v1.13.5 2026-02-09 00:39:09 +00:00
Elian Doran
1eafda36a9 Translations update from Hosted Weblate (#8652) 2026-02-08 21:02:38 +02:00
TS
871ecf0158 Translated using Weblate (Polish)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/pl/
2026-02-08 18:57:05 +00:00
TS
429000bdcb Translated using Weblate (Polish)
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/pl/
2026-02-08 18:57:04 +00:00
TS
607940ed60 Translated using Weblate (Polish)
Currently translated at 100.0% (116 of 116 strings)

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/pl/
2026-02-08 18:57:03 +00:00
noobhjy
46f61a4311 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/zh_Hans/
2026-02-08 18:57:02 +00:00
Marcel
3721df0502 Translated using Weblate (German)
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2026-02-08 18:57:02 +00:00
Marcel
11add681ec Translated using Weblate (German)
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/de/
2026-02-08 18:57:01 +00:00
TS
6e36eea6c8 Translated using Weblate (Polish)
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pl/
2026-02-08 18:57:00 +00:00
Aindriú Mac Giolla Eoin
6a82e7a24c Translated using Weblate (Irish)
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
2026-02-08 18:56:59 +00:00
Ulices
5f625fa9f3 Translated using Weblate (Spanish)
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/es/
2026-02-08 18:56:58 +00:00
Aindriú Mac Giolla Eoin
a95527674f Translated using Weblate (Irish)
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ga/
2026-02-08 18:56:58 +00:00
green
f6149c67dc Translated using Weblate (Japanese)
Currently translated at 100.0% (158 of 158 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ja/
2026-02-08 18:56:57 +00:00
Ulices
00c2a07e33 Translated using Weblate (Spanish)
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2026-02-08 18:56:56 +00:00
Elian Doran
bc49b22c32 docs(user): add note about icon packs 2026-02-08 20:55:48 +02:00
Elian Doran
fe1b1c8bc3 feat(website/resources): add note about icon packs 2026-02-08 20:52:13 +02:00
Elian Doran
a2825a06d6 fix(website): top level subpages not rendered 2026-02-08 20:43:48 +02:00
renovate[bot]
3c33b5b169 fix(deps): update codemirror 2026-02-08 13:39:10 +00:00
Elian Doran
934a867c83 chore(deps): update dependency electron to v40.2.1 (#8646) 2026-02-08 10:42:50 +02:00
Elian Doran
a36337fba8 chore(deps): update pnpm to v10.29.1 (#8655) 2026-02-08 10:42:17 +02:00
Elian Doran
b3bd53bdd0 fix(deps): update dependency mind-elixir to v5.8.0 (#8656) 2026-02-08 10:41:23 +02:00
Elian Doran
55518d4a8e fix(deps): update dependency eslint-linter-browserify to v10 (#8657) 2026-02-08 10:39:34 +02:00
renovate[bot]
2949c330d7 fix(deps): update dependency eslint-linter-browserify to v10 2026-02-08 00:28:40 +00:00
renovate[bot]
7ec056dbe0 fix(deps): update dependency mind-elixir to v5.8.0 2026-02-08 00:28:02 +00:00
renovate[bot]
983b60a8b9 chore(deps): update pnpm to v10.29.1 2026-02-08 00:27:27 +00:00
renovate[bot]
000c31b66c chore(deps): update dependency @anthropic-ai/sdk to v0.74.0 2026-02-07 20:46:58 +00:00
Elian Doran
2cd3d4bfb7 Simple content library for icon packs (#8650) 2026-02-07 16:52:08 +02:00
Elian Doran
9d9065c801 feat(website): add GitHub link on mid-size 2026-02-07 16:42:40 +02:00
Elian Doran
10e7fe56ab feat(website): add GitHub link on mobile 2026-02-07 16:36:53 +02:00
Elian Doran
c832e62389 fix(website): page not rendering properly on cold refresh 2026-02-07 16:29:34 +02:00
Elian Doran
328e488322 chore(website): address requested changes 2026-02-07 16:18:51 +02:00
Elian Doran
fd5a43cdb4 Translations update from Hosted Weblate (#8648) 2026-02-07 13:54:22 +02:00
renovate[bot]
fd7780abb0 chore(deps): update dependency esbuild to v0.27.3 2026-02-07 11:53:22 +00:00
Elian Doran
11c23e0b5e chore(deps): update dependency eslint to v10 (#8647) 2026-02-07 13:51:39 +02:00
Elian Doran
1170cfd4a7 fix(website): stargrazers count still not updating due to hydration 2026-02-07 13:47:25 +02:00
Elian Doran
2eddaa954e chore: address requested changes 2026-02-07 12:38:41 +02:00
Elian Doran
2c472fd03f docs(user): improve docuemntation slightly on icon packs 2026-02-07 12:37:10 +02:00
Elian Doran
16019a787e fix(website): stargazers count not updating 2026-02-07 12:27:16 +02:00
Elian Doran
717d0a75f4 chore(website): improve fit for full-height section 2026-02-07 12:20:24 +02:00
Elian Doran
cc9487bae8 chore(website): fix warnings & type issue 2026-02-07 12:17:14 +02:00
Elian Doran
5ed9ec8f46 fix(website): unnecessary horizontal scroll on md screen 2026-02-07 12:11:41 +02:00
Elian Doran
6ca37ca7f4 feat(header): improve header fit on smaller screens 2026-02-07 11:56:51 +02:00
Elian Doran
1007b8b15d fix(website): subpixel scaling causing issues at 719px 2026-02-07 11:47:30 +02:00
Elian Doran
be3a95fd54 chore(website): add resources to header 2026-02-07 11:19:46 +02:00
Elian Doran
109cb6cc3f chore(website/resources): add descriptions to fonts 2026-02-07 11:18:05 +02:00
Elian Doran
fe1509dcfc chore(website/resources): display version slightly smaller 2026-02-07 11:09:09 +02:00
Elian Doran
1797e33989 feat(website/resources): add intro blurb for icon packs 2026-02-07 11:08:03 +02:00
Elian Doran
6c504eeb3e feat(website/resources): make downloadable 2026-02-07 10:52:23 +02:00
Elian Doran
d4b16fcdd1 feat(website/resources): display icon packs 2026-02-07 10:36:00 +02:00
Elian Doran
9a08c079b5 chore(icon-pack-builder): build directly in website resource path 2026-02-07 10:24:32 +02:00
Elian Doran
98e75a7d6c chore(icon-pack-builder): generate meta alongside files 2026-02-07 10:17:58 +02:00
Elian Doran
675fd13391 chore(icon-pack-builder): add website and version meta 2026-02-07 10:15:55 +02:00
Elian Doran
e4f042aba4 fix(content_renderer): prevent iconPacks from rendering in preview 2026-02-07 09:59:53 +02:00
Elian Doran
19527845d1 fix(tree): wrong icon on multi selection with custom icons 2026-02-07 09:54:50 +02:00
Elian Doran
3aa981649c fix(icon-pack-builder): wrong icon for boxicons3-brands 2026-02-07 09:51:21 +02:00
Elian Doran
330d48f70d chore(website): create empty resources page 2026-02-07 09:42:42 +02:00
noobhjy
90e14aae99 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2026-02-07 02:01:52 +01:00
green
77e2ed7e01 Translated using Weblate (Japanese)
Currently translated at 100.0% (1772 of 1772 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-07 02:01:50 +01:00
renovate[bot]
f720f921c3 chore(deps): update dependency eslint to v10 2026-02-07 00:36:40 +00:00
renovate[bot]
328740909b chore(deps): update dependency electron to v40.2.1 2026-02-07 00:35:41 +00:00
renovate[bot]
c8a981e8d6 chore(deps): update dependency wxt to v0.20.14 2026-02-07 00:33:00 +00:00
renovate[bot]
faac45784c chore(deps): update dependency @playwright/test to v1.58.2 2026-02-07 00:32:04 +00:00
Elian Doran
25667e84b7 fix(deps): update dependency force-graph to v1.51.1 (#8632) 2026-02-06 22:40:03 +02:00
Elian Doran
72e0d77be5 chore(i18n): fix more issues related to Irish 2026-02-06 21:44:39 +02:00
Elian Doran
9ac4b9ed4f docs(dev): refresh adding new locale 2026-02-06 21:18:29 +02:00
Elian Doran
b3b89ba05c chore(i18n): fix calendar mapping for Irish 2026-02-06 21:16:07 +02:00
Elian Doran
00dc04df25 chire(pdfjs): fix handling of Irish 2026-02-06 20:46:26 +02:00
Elian Doran
21d47c3fef chore(i18n): fix issues with ga 2026-02-06 20:24:16 +02:00
Elian Doran
66de94f050 feat(i18n): enable Irish language 2026-02-06 19:56:37 +02:00
Elian Doran
1917adb322 chore(scripts): improve messages for translation check script 2026-02-06 19:38:24 +02:00
Elian Doran
3360b29354 Translations update from Hosted Weblate (#8599) 2026-02-06 10:32:09 +02:00
aloeaqua
646d281759 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1768 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2026-02-05 21:44:58 +00:00
aloeaqua
e268d92d52 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hant/
2026-02-05 21:44:57 +00:00
Aindriú Mac Giolla Eoin
fa81db2f03 Translated using Weblate (Irish)
Currently translated at 100.0% (1768 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
2026-02-05 21:44:56 +00:00
Marcel
830199ba3a Translated using Weblate (German)
Currently translated at 100.0% (1768 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2026-02-05 21:44:56 +00:00
Ulices
ea8dc506d3 Translated using Weblate (Spanish)
Currently translated at 100.0% (1768 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2026-02-05 21:44:55 +00:00
ibs-allaow
c95483ed94 Translated using Weblate (Arabic)
Currently translated at 49.1% (57 of 116 strings)

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/ar/
2026-02-05 21:44:54 +00:00
ibs-allaow
d9cb4480b2 Translated using Weblate (Arabic)
Currently translated at 59.8% (1058 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ar/
2026-02-05 21:44:53 +00:00
ibs-allaow
c69afd6074 Translated using Weblate (Arabic)
Currently translated at 64.4% (98 of 152 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ar/
2026-02-05 21:44:53 +00:00
ibs-allaow
f541790ab4 Translated using Weblate (Arabic)
Currently translated at 48.2% (56 of 116 strings)

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/ar/
2026-02-05 21:44:52 +00:00
green
707ac4ec36 Translated using Weblate (Japanese)
Currently translated at 100.0% (1768 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-05 21:44:51 +00:00
noobhjy
cd55133e96 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1768 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2026-02-05 21:44:50 +00:00
ibs-allaow
37a91f8529 Translated using Weblate (Arabic)
Currently translated at 59.7% (1057 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ar/
2026-02-05 21:44:49 +00:00
Aindriú Mac Giolla Eoin
0ed0fa37a1 Translated using Weblate (Irish)
Currently translated at 55.4% (981 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
2026-02-05 21:44:48 +00:00
ibs-allaow
26728b79b2 Translated using Weblate (Arabic)
Currently translated at 59.7% (1056 of 1768 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ar/
2026-02-05 21:44:47 +00:00
ibs-allaow
f7c8723539 Translated using Weblate (Arabic)
Currently translated at 63.1% (96 of 152 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ar/
2026-02-05 21:44:47 +00:00
Hosted Weblate
c313916771 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/
2026-02-05 21:44:46 +00:00
Elian Doran
f0b30c5e91 Mobile improvements v2 (#8615) 2026-02-05 23:44:31 +02:00
Elian Doran
73e3196124 docs(user): improve documentation on mobile support 2026-02-05 23:31:12 +02:00
Elian Doran
8a7bcc316e chore(mobile): address requested changes 2026-02-05 22:25:01 +02:00
Elian Doran
a2921cb982 fix(mobile): missing snap in board view 2026-02-05 22:15:41 +02:00
Elian Doran
29ce004974 chore(mobile): add possible work-around for note switcher sometimes disappearing 2026-02-05 22:13:09 +02:00
Elian Doran
026ba5ddce chore(mobile/attachments): improve layout 2026-02-05 22:02:11 +02:00
Elian Doran
ab0585609a chore(mobile/attachments): use bottom-style menu for actions 2026-02-05 21:57:49 +02:00
Elian Doran
14a8bdb0c0 chore(launch_bar): add backdrop on mobile for dropdown menus 2026-02-05 21:53:54 +02:00
Elian Doran
397d04dd88 style(client): fix rounded corners in launcher bar dropdown 2026-02-05 21:48:38 +02:00
Elian Doran
fbb0bb7491 Revert "feat(mobile): make the sidebar gesture easier to press"
This reverts commit f8b386e42d.
2026-02-05 21:47:40 +02:00
Elian Doran
ee987dae99 refactor(client): fix typo in container 2026-02-05 21:44:48 +02:00
Elian Doran
720281a8db feat(bookmarks): support bookmark folders on mobile 2026-02-05 21:43:58 +02:00
Elian Doran
ff0c89e5a3 feat(bookmarks): collapse on mobile into single icon (closes #5464) 2026-02-05 21:28:50 +02:00
Elian Doran
442937f540 feat(bookmarks): add launcher on mobile 2026-02-05 21:20:22 +02:00
Elian Doran
dc01b787c1 fix(launch_bar): cannot create new items in mobile launch bar (fixes #8054) 2026-02-05 21:10:05 +02:00
Elian Doran
c709b5d34c chore(mobile): relocate some note actions 2026-02-05 18:42:51 +02:00
Elian Doran
f8b386e42d feat(mobile): make the sidebar gesture easier to press 2026-02-05 16:43:06 +02:00
renovate[bot]
f6b454cb9a fix(deps): update dependency i18next to v25.8.4 2026-02-05 13:44:21 +00:00
Elian Doran
a82f8ce3ad Merge remote-tracking branch 'origin/main' into feature/mobile_improvements_v2 2026-02-05 10:28:02 +02:00
Elian Doran
529d45b762 chore(deps): update dependency @types/node to v24.10.10 (#8610) 2026-02-05 09:45:49 +02:00
Elian Doran
d31135bf21 fix(mobile): status bar color for Samsung Internet 2026-02-05 09:09:47 +02:00
Elian Doran
75a77acefe Merge remote-tracking branch 'origin/main' into feature/mobile_improvements_v2 2026-02-05 09:09:41 +02:00
renovate[bot]
715f42b6c3 fix(deps): update dependency force-graph to v1.51.1 2026-02-05 01:13:22 +00:00
Elian Doran
199bfc8a37 Adjust next theme development docu (#8616) 2026-02-04 21:50:36 +02:00
hulmgulm
e82ae762f0 More fixes 2026-02-04 20:48:36 +01:00
hulmgulm
f90cc9aff7 Adjust theme development docu 2026-02-04 20:39:58 +01:00
Elian Doran
ec915177ad feat(mobile): add search to launch bar 2026-02-04 21:36:00 +02:00
Elian Doran
5adee3e217 fix(mobile/search): missing rounded corners for bulk actions 2026-02-04 21:31:23 +02:00
Elian Doran
278d82645e feat(mobile): use safe area for bottom-aligned menus 2026-02-04 21:15:08 +02:00
Elian Doran
e66f13b471 feat(mobile/search): use bottom menus for large dropdowns 2026-02-04 21:11:43 +02:00
Elian Doran
0e2955b57e feat(mobile/search): group search buttons into split 2026-02-04 21:06:20 +02:00
Elian Doran
2a4d5ec1ec feat(mobile/search): group search options in dropdown 2026-02-04 20:55:55 +02:00
Elian Doran
07ce63de69 chore(mobile/search): remove redundant margin 2026-02-04 20:44:15 +02:00
Elian Doran
b539862eef fix(mobile/search): duplicate search parameters 2026-02-04 20:41:36 +02:00
Elian Doran
ac4be3f8a8 feat(mobile/global_menu): add option to search notes 2026-02-04 20:40:31 +02:00
Elian Doran
0dc2d07b58 fix(mobile): virtual keyboard detection not working on iOS 2026-02-04 19:09:18 +02:00
Elian Doran
b097f9dc21 fix(mobile): fixed tree overflowing container 2026-02-04 18:53:32 +02:00
Elian Doran
8aa4a97480 fix(mobile): fixed tree for launcher duplicated in split 2026-02-04 18:52:46 +02:00
Elian Doran
3e54d0ceae chore(hidden_tree): add icon to mobile tab switcher launcher 2026-02-04 18:48:37 +02:00
Elian Doran
e1cec3404a Merge remote-tracking branch 'origin/main' into feature/mobile_improvements_v2
; Conflicts:
;	apps/client/src/widgets/mobile_widgets/mobile_detail_menu.tsx
2026-02-04 18:46:38 +02:00
Elian Doran
9a2b7fbda1 Feature/mobile improvement bugs (#8614) 2026-02-04 18:45:35 +02:00
Elian Doran
e0b4ebed93 chore(mobile): address requested changes 2026-02-04 18:40:30 +02:00
Elian Doran
e00e3999c5 feat(mobile/note_actions): indicate current content language 2026-02-04 18:05:37 +02:00
Elian Doran
2cbe96d815 feat(mobile/note_actions): integrate text content language switcher 2026-02-04 18:03:17 +02:00
Elian Doran
ac3b289c9e feat(mobile): more stable fixed tree for launch bar config 2026-02-04 16:46:22 +02:00
Elian Doran
62534e0e93 chore(mobile/tab_switcher): launcher preview not looking good 2026-02-04 16:43:46 +02:00
Elian Doran
b802c3174c style(mobile): add top border to bottom bar 2026-02-04 16:29:51 +02:00
Elian Doran
aee1a6e1f0 fix(note_list): regression in the display of no previews 2026-02-04 16:28:07 +02:00
Elian Doran
46f1cd38e0 feat(mobile): enforce backdrop effects off 2026-02-04 16:24:14 +02:00
Elian Doran
7f891ef523 feat(mobile/tab_switcher): improve display for some text elements 2026-02-04 15:44:17 +02:00
Elian Doran
06af5e15cd feat(mobile/tab_switcher): consider view scope for preview 2026-02-04 15:41:15 +02:00
Elian Doran
af89a0a883 fix(new_layout): note title actions shown in non-standard view modes 2026-02-04 15:17:39 +02:00
Elian Doran
fa72eb2edb fix(mobile/tab_switcher): view mode not displayed in title 2026-02-04 14:53:00 +02:00
Elian Doran
c1ea94423b refactor(client): use CSS file for content renderer 2026-02-04 14:46:52 +02:00
Elian Doran
1e70d066bd fix(mobile/tab_switcher): wrong preview for relation map 2026-02-04 14:39:58 +02:00
Elian Doran
ab89f16e7c fix(mobile): unnecessary separator for custom note actions 2026-02-04 14:29:47 +02:00
Elian Doran
8c848a4cb5 fix(mobile): wrong context activation logic when creating new split 2026-02-04 14:04:47 +02:00
Elian Doran
e021a54d2d fix(mobile): formatting toolbar not appearing after read-only (closes #5368) 2026-02-04 13:42:30 +02:00
Elian Doran
70523574b0 refactor(client): extract mobile layout CSS to dedicated file 2026-02-04 13:37:04 +02:00
Elian Doran
66e0f1ab19 chore(mobile/tree): slightly bigger expanders 2026-02-04 13:29:45 +02:00
Elian Doran
671a05470e fix(mobile): missing badge style in tree 2026-02-04 13:24:08 +02:00
Elian Doran
99eec0c41e fix(mobile): duplicate promoted attributes 2026-02-04 13:18:26 +02:00
Elian Doran
48d06dcb06 fix(mobile): note context menu too tall in browser 2026-02-04 13:17:28 +02:00
Elian Doran
e38df0c731 fix(mobile): note paths dialog doesn't trigger clone note to location 2026-02-04 13:06:49 +02:00
Elian Doran
0f3f49915e fix(mobile): note actions should be at the bottom, not above launch bar 2026-02-04 12:24:56 +02:00
Elian Doran
416144265b fix(client): wrong positioning of modals due to mobile changes 2026-02-04 12:06:29 +02:00
renovate[bot]
2e7ced8e60 Update dependency @types/node to v24.10.10 2026-02-04 01:52:42 +00:00
renovate[bot]
563463782c Update dependency @ckeditor/ckeditor5-dev-build-tools to v54.3.3 2026-02-04 01:51:41 +00:00
Adorian Doran
fe02871e91 Merge branch 'main' of https://github.com/TriliumNext/Trilium 2026-02-04 00:45:03 +02:00
Adorian Doran
bb05aeeaf7 style/split: tweak transition for the current split indicator 2026-02-04 00:44:54 +02:00
Elian Doran
846358ccb0 Update dependency @redocly/cli to v2.15.1 (#8601) 2026-02-03 23:45:18 +02:00
Elian Doran
dabc779727 Update dependency @smithy/middleware-retry to v4.4.30 (#8602) 2026-02-03 23:45:02 +02:00
Elian Doran
08fd2ec64b Update dependency happy-dom to v20.5.0 (#8603) 2026-02-03 23:44:32 +02:00
Adorian Doran
c42c06d048 style/global menu: use proper heading for the development options section 2026-02-03 21:01:08 +02:00
Adorian Doran
e951d60800 style/options: hide collection properties 2026-02-03 20:54:56 +02:00
Elian Doran
0d8453f6a7 feat(mobile/note_actions): integrate code language switcher 2026-02-03 18:58:04 +02:00
Elian Doran
52b41b1bb0 feat(mobile/note_actions): integrate similar notes 2026-02-03 18:18:57 +02:00
Elian Doran
c7265017b3 feat(mobile/note_actions): integrate styling for note info 2026-02-03 18:09:02 +02:00
Elian Doran
634e0b6d30 feat(mobile/note_actions): integrate note info 2026-02-03 18:04:08 +02:00
renovate[bot]
1c260f5890 Update dependency happy-dom to v20.5.0 2026-02-03 01:34:19 +00:00
renovate[bot]
177aedeaae Update dependency @smithy/middleware-retry to v4.4.30 2026-02-03 01:33:28 +00:00
renovate[bot]
3f6f3d2565 Update dependency @redocly/cli to v2.15.1 2026-02-03 01:32:42 +00:00
Adorian Doran
38489dbfeb style/collapsible widget: fade content when expanding or collapsing 2026-02-03 03:03:50 +02:00
Adorian Doran
ff2a3d6a28 client/search: fix menu dropdowns getting clipped 2026-02-03 02:56:46 +02:00
Adorian Doran
2c74697fb1 style/text editor: tweak the content placeholder 2026-02-03 02:22:04 +02:00
Adorian Doran
110e9200b9 style/scrolling container: improve alignment when content centering is turned on, refactor 2026-02-03 02:11:57 +02:00
Adorian Doran
6e792f9735 style/tree/tree item icons: fix color for tinted tree items 2026-02-03 01:25:28 +02:00
Adorian Doran
51d8b13a81 style/tree: tweak the color of tree item icons for the dark color scheme 2026-02-03 01:19:47 +02:00
Adorian Doran
a05691fd07 style/tree: add a CSS variable to customize the color of tree item icons 2026-02-03 00:52:36 +02:00
Adorian Doran
d25e7915d9 style/tree/context menu: use the proper color for menu item icons 2026-02-03 00:41:41 +02:00
Elian Doran
1a025dfef3 Translations update from Hosted Weblate (#8581) 2026-02-02 22:09:13 +02:00
ibs-allaow
84cc4194aa Translated using Weblate (Arabic)
Currently translated at 61.1% (93 of 152 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ar/
2026-02-02 21:07:55 +01:00
ibs-allaow
331a56277c Translated using Weblate (Arabic)
Currently translated at 59.7% (1055 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ar/
2026-02-02 21:07:54 +01:00
ibs-allaow
bc2915adb9 Translated using Weblate (Arabic)
Currently translated at 42.2% (49 of 116 strings)

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/ar/
2026-02-02 21:07:53 +01:00
Elian Doran
703fe9a71b Translated using Weblate (Romanian)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ro/
2026-02-02 21:07:52 +01:00
Elian Doran
6c50664046 Translated using Weblate (Romanian)
Currently translated at 99.9% (1766 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ro/
2026-02-02 21:07:51 +01:00
Aindriú Mac Giolla Eoin
673cbc97e1 Translated using Weblate (Irish)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ga/
2026-02-02 21:07:49 +01:00
Aindriú Mac Giolla Eoin
3e83766099 Translated using Weblate (Irish)
Currently translated at 0.1% (1 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
2026-02-02 21:07:48 +01:00
Aindriú Mac Giolla Eoin
b453589077 Translated using Weblate (Irish)
Currently translated at 100.0% (152 of 152 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ga/
2026-02-02 21:07:47 +01:00
Hosted Weblate
654fa18ab1 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/
2026-02-02 21:07:45 +01:00
Elian Doran
7b0d91534c Add subtreeHidden and map:* attributes to labels documentation (#8594) 2026-02-02 22:07:34 +02:00
Elian Doran
8d4801bb6f Mobile: integrate new layout v1 (#8595) 2026-02-02 21:47:34 +02:00
Elian Doran
9e36f4f625 Merge branch 'main' into feature/mobile_improvements 2026-02-02 21:44:22 +02:00
Elian Doran
d83a824812 chore(client): address requested changes 2026-02-02 21:43:27 +02:00
Elian Doran
ca128f2fa9 chore(client): address requested changes 2026-02-02 21:19:35 +02:00
Elian Doran
c02642d0f9 feat(mobile/note_actions): display backlinks & note paths on same row 2026-02-02 21:16:02 +02:00
Adorian Doran
7340709111 style/quick edit: refactor 2026-02-02 21:13:16 +02:00
hulmgulm
56d0383372 Merge branch 'main' into main 2026-02-02 20:10:42 +01:00
Adorian Doran
49d33ea19a style/quick edit: allow object selection rectangle to go outside of the editor area 2026-02-02 21:10:00 +02:00
hulmgulm
979fa0359a updated texts 2026-02-02 20:08:55 +01:00
Elian Doran
c7381d058a feat(mobile/note_actions): proper styling of note paths 2026-02-02 21:05:25 +02:00
Adorian Doran
5db298f031 style/quick edit: increase the horizontal padding of the text to make space for the block toolbar button 2026-02-02 21:03:02 +02:00
Elian Doran
171d948a00 feat(mobile/note_actions): basic integration of note paths 2026-02-02 21:00:05 +02:00
Adorian Doran
facd56cdf4 style/text editor/block toolbar button: tweak 2026-02-02 20:57:38 +02:00
Elian Doran
ba17ec4be4 chore(backlinks): show multiple excerpts properly 2026-02-02 20:44:48 +02:00
Elian Doran
6c163b5479 chore(mobile/note_actions): flickerless backlinks item 2026-02-02 20:39:37 +02:00
Elian Doran
79649805b8 chore(mobile/note_actions): missing translation for backlinks 2026-02-02 20:34:52 +02:00
Elian Doran
220ca8a570 chore(mobile/note_actions): use new layout styling for backlinks 2026-02-02 20:32:06 +02:00
Adorian Doran
348c00f86d style/text editor: fix layout issues 2026-02-02 20:13:00 +02:00
Elian Doran
12b641b522 feat(mobile/note_actions): basic integration of backlinks 2026-02-02 20:10:20 +02:00
hulmgulm
6855bc1de6 Merge branch 'TriliumNext:main' into main 2026-02-02 19:09:08 +01:00
Adorian Doran
a9c5b99ae8 style/text editor/block toolbar button: fix position and z-index 2026-02-02 20:06:05 +02:00
Elian Doran
76f36e2fd3 fix(mobile/custom_note_actions): unable to close empty pane 2026-02-02 18:39:40 +02:00
Adorian Doran
afe710321c style/text editor: fix layout issues 2026-02-02 18:34:22 +02:00
Elian Doran
0d444daaca fix(mobile/custom_note_actions): note icon shown in empty note 2026-02-02 18:29:21 +02:00
Elian Doran
c8a0c9fd23 chore(mobile/custom_note_actions): text not fitting 2026-02-02 18:26:40 +02:00
Elian Doran
79f07ae923 chore(mobile/custom_note_actions): disable split orientation button 2026-02-02 18:23:54 +02:00
Elian Doran
c77e7a568b chore(mobile/custom_note_actions): duplicate open help 2026-02-02 18:11:58 +02:00
Elian Doran
ff9ec2057b chore(mobile/custom_note_actions): hide open note externally 2026-02-02 18:10:26 +02:00
Elian Doran
6a313b99e4 fix(mobile/custom_note_actions): missing file upload 2026-02-02 18:02:47 +02:00
Elian Doran
8a92370042 fix(mobile/custom_note_actions): missing separator 2026-02-02 17:57:30 +02:00
Elian Doran
411a59ec54 feat(mobile): display custom note actions in note actions 2026-02-02 17:46:18 +02:00
Elian Doran
2d4022044d chore(mobile): get rid of floating buttons 2026-02-02 17:30:35 +02:00
Elian Doran
bbc5ebd76b chore(mobile/header): improve button sizes 2026-02-02 17:06:27 +02:00
Elian Doran
e9c90fcde8 chore(mobile/header): prevent badges from shrinking 2026-02-02 17:02:32 +02:00
Elian Doran
911f78867f chore(mobile/header): make icons easier to press 2026-02-02 16:58:06 +02:00
Elian Doran
5507cc5abc chore(mobile): slightly smaller note title icon 2026-02-02 16:51:57 +02:00
Elian Doran
0e5aa401ef chore(mobile/note_icon): redundant separator 2026-02-02 16:44:08 +02:00
Elian Doran
d48473ab87 feat(mobile/note_icon): single menu for filtering & resetting 2026-02-02 16:38:12 +02:00
hulmgulm
734efaf40c Update apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/Attributes/Labels.html
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-02 14:41:38 +01:00
hulmgulm
f89718d88a Add subtreeHidden and map:* attributes to Labels.html 2026-02-02 14:38:11 +01:00
Elian Doran
aa4942a0da fix(mobile/note_icon): wrong icons displayed 2026-02-02 13:45:13 +02:00
Elian Doran
90bb162a88 feat(mobile/note_icon): bigger touch area 2026-02-02 13:31:29 +02:00
Elian Doran
0382a4b30e fix(mobile/note_icon): consistent height and proper margins 2026-02-02 13:27:34 +02:00
Elian Doran
490d940cd1 feat(mobile/note_icon): improve height and fit 2026-02-02 13:23:55 +02:00
Elian Doran
b090eb9359 feat(mobile/note_icon): calculate number of columns dynamically 2026-02-02 13:22:37 +02:00
Elian Doran
cb9e67ce84 fix(mobile/note_icon): small horizontal scroll 2026-02-02 13:14:42 +02:00
Elian Doran
c4d131dd23 feat(mobile): improve note icon selector fit 2026-02-02 13:13:13 +02:00
Elian Doran
2667f266bf feat(mobile): use modal for icon selector 2026-02-02 13:05:20 +02:00
Elian Doran
8258936d6c feat(mobile): integrate icon display in header 2026-02-02 12:53:54 +02:00
Elian Doran
e4e7449078 feat(mobile): integrate inline title with title actions 2026-02-02 12:51:29 +02:00
Elian Doran
11b020e859 feat(mobile): integrate part of the new layout title row 2026-02-02 12:47:43 +02:00
Elian Doran
72d6b83ec5 docs(user): add nightly release script for Windows 2026-02-02 12:20:38 +02:00
Elian Doran
fbe5152cb3 chore(deps): update dependency webdriverio to v9.23.3 (#8583) 2026-02-02 07:48:30 +02:00
Elian Doran
6bef01f755 fix(deps): update dependency globals to v17.3.0 (#8584) 2026-02-02 07:48:17 +02:00
Elian Doran
f0d4b4a6d9 fix(deps): update dependency mind-elixir to v5.7.1 (#8585) 2026-02-02 07:48:05 +02:00
renovate[bot]
a54fe62643 fix(deps): update dependency mind-elixir to v5.7.1 2026-02-02 04:31:50 +00:00
renovate[bot]
eb4bbd49fb fix(deps): update dependency globals to v17.3.0 2026-02-02 01:52:08 +00:00
renovate[bot]
ab4f1bd4f4 chore(deps): update dependency webdriverio to v9.23.3 2026-02-02 01:51:29 +00:00
Elian Doran
f8b414c354 feat(etapi): add attachments etapi endpoint (#8578) 2026-02-01 22:34:37 +02:00
Elian Doran
82a21624c3 Translations update from Hosted Weblate (#8579) 2026-02-01 22:32:35 +02:00
Elian Doran
1b212ac720 Improved mobile note actions (#8580) 2026-02-01 22:30:45 +02:00
Elian Doran
c36ce3ea14 fix(mobile/note_actions): insert child note not working 2026-02-01 22:29:43 +02:00
Elian Doran
841fab77a8 chore(client): address requested changes 2026-02-01 22:22:25 +02:00
Elian Doran
fd6f910824 fix(mobile/note_actions): backdrop remains when closing split 2026-02-01 22:02:54 +02:00
Elian Doran
ce9ca1917d fix(mobile/note_actions): reintroduce split buttons 2026-02-01 22:01:26 +02:00
perfectra1n
c702fb273c feat(tests): add tests for new attachments endpoint 2026-02-01 11:46:03 -08:00
Elian Doran
4c72d8691b fix(mobile/note_actions): reintroduce help button 2026-02-01 21:45:43 +02:00
Elian Doran
35ac5fc514 fix(mobile/note_actions): reintroduce insert child note 2026-02-01 21:42:48 +02:00
Elian Doran
92991cc03c fix(promoted_attributes): displayed in non-default view modes 2026-02-01 21:37:20 +02:00
Elian Doran
76492475e3 fix(mobile/note_actions): find not working 2026-02-01 21:27:24 +02:00
Elian Doran
e2363d860c fix(mobile/note_actions): font too big 2026-02-01 21:25:40 +02:00
Elian Doran
c06a90913a fix(mobile/note_actions): submenus not working 2026-02-01 21:12:50 +02:00
Elian Doran
e88c0f7326 fix(mobile/note_actions): toggles on two rows 2026-02-01 21:02:46 +02:00
Elian Doran
2a4280b5bf feat(mobile/note_actions): remove bottom rounded corners 2026-02-01 20:59:50 +02:00
Elian Doran
d3c733c57f feat(mobile/note_actions): add backdrop 2026-02-01 20:57:01 +02:00
Elian Doran
f91add3cd4 chore(mobile/note_actions): position like global menu 2026-02-01 20:53:33 +02:00
Elian Doran
90e3f7508a chore(mobile): enforce new layout 2026-02-01 20:50:00 +02:00
Elian Doran
5e981da4df feat(mobile): use the desktop version of note actions 2026-02-01 20:48:55 +02:00
Aindriú Mac Giolla Eoin
2ddd5d75fc Added translation using Weblate (Irish) 2026-02-01 19:33:10 +01:00
Aindriú Mac Giolla Eoin
88f509cbb6 Added translation using Weblate (Irish) 2026-02-01 19:33:08 +01:00
Aindriú Mac Giolla Eoin
ca161bc881 Added translation using Weblate (Irish) 2026-02-01 19:33:05 +01:00
Aindriú Mac Giolla Eoin
676ca44cdf Added translation using Weblate (Irish) 2026-02-01 19:33:01 +01:00
green
5d0c91202f Translated using Weblate (Japanese)
Currently translated at 100.0% (1767 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-01 19:32:59 +01:00
Ulices
a166f049d5 Translated using Weblate (Spanish)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/es/
2026-02-01 19:32:59 +01:00
noobhjy
0dc4692dfc Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1767 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2026-02-01 19:32:59 +01:00
ibs-allaow
996607d096 Translated using Weblate (Arabic)
Currently translated at 59.5% (1053 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ar/
2026-02-01 19:32:58 +01:00
Ulices
bd907ea008 Translated using Weblate (Spanish)
Currently translated at 100.0% (1767 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2026-02-01 19:32:58 +01:00
noobhjy
b1573b1f3b Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hans/
2026-02-01 19:32:57 +01:00
Marcel
246849ce94 Translated using Weblate (German)
Currently translated at 100.0% (1767 of 1767 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2026-02-01 19:32:57 +01:00
Marcel
2c2c68261a Translated using Weblate (German)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2026-02-01 19:32:56 +01:00
green
e38a0361bf Translated using Weblate (Japanese)
Currently translated at 100.0% (389 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2026-02-01 19:32:56 +01:00
ibs-allaow
cbf879bd32 Translated using Weblate (Arabic)
Currently translated at 59.2% (90 of 152 strings)

Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/ar/
2026-02-01 19:32:56 +01:00
perfectra1n
808625e564 feat(etapi): add attachments etapi endpoint 2026-02-01 09:19:37 -08:00
231 changed files with 8671 additions and 3432 deletions

2
.nvmrc
View File

@@ -1 +1 @@
24.13.0
24.13.1

View File

@@ -42,5 +42,8 @@
},
"eslint.rules.customizations": [
{ "rule": "*", "severity": "warn" }
],
"cSpell.words": [
"Trilium"
]
}
}

View File

@@ -9,9 +9,9 @@
"keywords": [],
"author": "Elian Doran <contact@eliandoran.me>",
"license": "AGPL-3.0-only",
"packageManager": "pnpm@10.28.2",
"packageManager": "pnpm@10.29.3",
"devDependencies": {
"@redocly/cli": "2.15.0",
"@redocly/cli": "2.18.0",
"archiver": "7.0.1",
"fs-extra": "11.3.3",
"react": "19.2.4",

View File

@@ -27,7 +27,7 @@
"@mermaid-js/layout-elk": "0.2.0",
"@mind-elixir/node-menu": "5.0.1",
"@popperjs/core": "2.11.8",
"@preact/signals": "2.6.2",
"@preact/signals": "2.7.1",
"@triliumnext/ckeditor5": "workspace:*",
"@triliumnext/codemirror": "workspace:*",
"@triliumnext/commons": "workspace:*",
@@ -42,9 +42,9 @@
"color": "5.0.3",
"debounce": "3.0.0",
"draggabilly": "3.0.0",
"force-graph": "1.51.0",
"globals": "17.2.0",
"i18next": "25.8.0",
"force-graph": "1.51.1",
"globals": "17.3.0",
"i18next": "25.8.6",
"i18next-http-backend": "3.0.2",
"jquery": "4.0.0",
"jquery.fancytree": "2.38.5",
@@ -54,14 +54,14 @@
"leaflet": "1.9.4",
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
"marked": "17.0.1",
"marked": "17.0.2",
"mermaid": "11.12.2",
"mind-elixir": "5.6.1",
"mind-elixir": "5.8.0",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.28.3",
"react-i18next": "16.5.4",
"react-window": "2.2.6",
"react-window": "2.2.7",
"reveal.js": "5.2.1",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",
@@ -78,7 +78,7 @@
"@types/reveal.js": "5.2.2",
"@types/tabulator-tables": "6.3.1",
"copy-webpack-plugin": "13.0.1",
"happy-dom": "20.4.0",
"happy-dom": "20.6.1",
"lightningcss": "1.31.1",
"script-loader": "0.7.2",
"vite-plugin-static-copy": "3.2.0"

View File

@@ -46,10 +46,6 @@ if (utils.isElectron()) {
electronContextMenu.setupContextMenu();
}
if (utils.isPWA()) {
initPWATopbarColor();
}
function initOnElectron() {
const electron: typeof Electron = utils.dynamicRequire("electron");
electron.ipcRenderer.on("globalShortcut", async (event, actionName) => appContext.triggerCommand(actionName));
@@ -134,20 +130,3 @@ function initDarkOrLightMode(style: CSSStyleDeclaration) {
const { nativeTheme } = utils.dynamicRequire("@electron/remote") as typeof ElectronRemote;
nativeTheme.themeSource = themeSource;
}
function initPWATopbarColor() {
const tracker = $("#background-color-tracker");
if (tracker.length) {
const applyThemeColor = () => {
let meta = $("meta[name='theme-color']");
if (!meta.length) {
meta = $(`<meta name="theme-color">`).appendTo($("head"));
}
meta.attr("content", tracker.css("color"));
};
tracker.on("transitionend", applyThemeColor);
applyThemeColor();
}
}

View File

@@ -0,0 +1,76 @@
#background-color-tracker {
color: var(--main-background-color) !important;
}
span.keyboard-shortcut,
kbd {
display: none;
}
.dropdown-menu {
font-size: larger;
}
.action-button {
background: none;
border: none;
cursor: pointer;
font-size: 1.25em;
padding-inline-start: 0.5em;
padding-inline-end: 0.5em;
color: var(--main-text-color);
}
.quick-search {
margin: 0;
}
.quick-search .dropdown-menu {
max-width: 350px;
}
/* #region Tree */
.tree-wrapper {
max-height: 100%;
margin-top: 0px;
overflow-y: auto;
contain: content;
padding-inline-start: 10px;
}
.fancytree-title {
margin-inline-start: 0.6em !important;
}
.fancytree-node {
padding: 5px;
}
span.fancytree-expander {
width: 24px !important;
margin-inline-end: 5px;
}
.fancytree-loading span.fancytree-expander {
width: 24px;
height: 32px;
}
.fancytree-loading span.fancytree-expander:after {
width: 20px;
height: 20px;
margin-top: 4px;
border-width: 2px;
border-style: solid;
}
.tree-wrapper .collapse-tree-button,
.tree-wrapper .scroll-to-active-note-button,
.tree-wrapper .tree-settings-button {
position: fixed;
margin-inline-end: 16px;
display: none;
}
.tree-wrapper .unhoist-button {
font-size: 200%;
}
/* #endregion */

View File

@@ -1,128 +1,41 @@
import "./mobile_layout.css";
import type AppContext from "../components/app_context.js";
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
import CloseZenModeButton from "../widgets/close_zen_button.js";
import NoteList from "../widgets/collections/NoteList.jsx";
import ContentHeader from "../widgets/containers/content_header.js";
import FlexContainer from "../widgets/containers/flex_container.js";
import RootContainer from "../widgets/containers/root_container.js";
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
import FloatingButtons from "../widgets/FloatingButtons.jsx";
import { MOBILE_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
import FindWidget from "../widgets/find.js";
import LauncherContainer from "../widgets/launch_bar/LauncherContainer.jsx";
import InlineTitle from "../widgets/layout/InlineTitle.jsx";
import NoteBadges from "../widgets/layout/NoteBadges.jsx";
import NoteTitleActions from "../widgets/layout/NoteTitleActions.jsx";
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button.jsx";
import NoteIconWidget from "../widgets/note_icon.jsx";
import NoteTitleWidget from "../widgets/note_title.js";
import NoteTreeWidget from "../widgets/note_tree.js";
import NoteWrapperWidget from "../widgets/note_wrapper.js";
import NoteDetail from "../widgets/NoteDetail.jsx";
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
import QuickSearchWidget from "../widgets/quick_search.js";
import { useNoteContext } from "../widgets/react/hooks.jsx";
import ReadOnlyNoteInfoBar from "../widgets/ReadOnlyNoteInfoBar.jsx";
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
import SearchDefinitionTab from "../widgets/ribbon/SearchDefinitionTab.jsx";
import SearchResult from "../widgets/search_result.jsx";
import SharedInfoWidget from "../widgets/shared_info.js";
import TabRowWidget from "../widgets/tab_row.js";
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
import { applyModals } from "./layout_commons.js";
const MOBILE_CSS = `
<style>
span.keyboard-shortcut,
kbd {
display: none;
}
.dropdown-menu {
font-size: larger;
}
.action-button {
background: none;
border: none;
cursor: pointer;
font-size: 1.25em;
padding-inline-start: 0.5em;
padding-inline-end: 0.5em;
color: var(--main-text-color);
}
.quick-search {
margin: 0;
}
.quick-search .dropdown-menu {
max-width: 350px;
}
</style>`;
const FANCYTREE_CSS = `
<style>
.tree-wrapper {
max-height: 100%;
margin-top: 0px;
overflow-y: auto;
contain: content;
padding-inline-start: 10px;
}
.fancytree-custom-icon {
font-size: 2em;
}
.fancytree-title {
font-size: 1.5em;
margin-inline-start: 0.6em !important;
}
.fancytree-node {
padding: 5px;
}
.fancytree-node .fancytree-expander:before {
font-size: 2em !important;
}
span.fancytree-expander {
width: 24px !important;
margin-inline-end: 5px;
}
.fancytree-loading span.fancytree-expander {
width: 24px;
height: 32px;
}
.fancytree-loading span.fancytree-expander:after {
width: 20px;
height: 20px;
margin-top: 4px;
border-width: 2px;
border-style: solid;
}
.tree-wrapper .collapse-tree-button,
.tree-wrapper .scroll-to-active-note-button,
.tree-wrapper .tree-settings-button {
position: fixed;
margin-inline-end: 16px;
display: none;
}
.tree-wrapper .unhoist-button {
font-size: 200%;
}
</style>`;
export default class MobileLayout {
getRootWidget(appContext: typeof AppContext) {
const rootContainer = new RootContainer(true)
.setParent(appContext)
.class("horizontal-layout")
.cssBlock(MOBILE_CSS)
.child(new FlexContainer("column").id("mobile-sidebar-container"))
.child(
new FlexContainer("row")
@@ -136,7 +49,7 @@ export default class MobileLayout {
.css("padding-inline-start", "0")
.css("padding-inline-end", "0")
.css("contain", "content")
.child(new FlexContainer("column").filling().id("mobile-sidebar-wrapper").child(new QuickSearchWidget()).child(new NoteTreeWidget().cssBlock(FANCYTREE_CSS)))
.child(new FlexContainer("column").filling().id("mobile-sidebar-wrapper").child(new QuickSearchWidget()).child(new NoteTreeWidget()))
)
.child(
new ScreenContainer("detail", "row")
@@ -147,30 +60,28 @@ export default class MobileLayout {
new NoteWrapperWidget()
.child(
new FlexContainer("row")
.class("title-row note-split-title")
.contentSized()
.css("font-size", "larger")
.css("align-items", "center")
.child(<ToggleSidebarButton />)
.child(<NoteIconWidget />)
.child(<NoteTitleWidget />)
.child(<NoteBadges />)
.child(<MobileDetailMenu />)
)
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
.child(<PromotedAttributes />)
.child(
new ScrollingContainer()
.filling()
.contentSized()
.child(new ContentHeader()
.child(<ReadOnlyNoteInfoBar />)
.child(<SharedInfoWidget />)
)
.child(<InlineTitle />)
.child(<NoteTitleActions />)
.child(<NoteDetail />)
.child(<NoteList media="screen" />)
.child(<StandaloneRibbonAdapter component={SearchDefinitionTab} />)
.child(<SearchResult />)
.child(<FilePropertiesWrapper />)
)
.child(<MobileEditorToolbar />)
.child(new FindWidget())
)
)
)

View File

@@ -248,7 +248,7 @@ class ContextMenu {
if ("uiIcon" in item || "checked" in item) {
const icon = (item.checked ? "bx bx-check" : item.uiIcon);
if (icon) {
$icon.addClass(icon);
$icon.addClass([icon, "tn-icon"]);
} else {
$icon.append("&nbsp;");
}

View File

@@ -1,12 +1,12 @@
import treeService from "../services/tree.js";
import froca from "../services/froca.js";
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
import dialogService from "../services/dialog.js";
import server from "../services/server.js";
import { t } from "../services/i18n.js";
import type { ContextMenuCommandData,FilteredCommandNames } from "../components/app_context.js";
import type { SelectMenuItemEventListener } from "../components/events.js";
import dialogService from "../services/dialog.js";
import froca from "../services/froca.js";
import { t } from "../services/i18n.js";
import server from "../services/server.js";
import treeService from "../services/tree.js";
import type NoteTreeWidget from "../widgets/note_tree.js";
import type { FilteredCommandNames, ContextMenuCommandData } from "../components/app_context.js";
import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js";
type LauncherCommandNames = FilteredCommandNames<ContextMenuCommandData>;
@@ -32,8 +32,8 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener<
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
const parentNoteId = this.node.getParent().data.noteId;
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers";
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers";
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers" || note?.noteId === "_lbMobileVisibleLaunchers";
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers" || note?.noteId === "_lbMobileAvailableLaunchers";
const isVisibleItem = parentNoteId === "_lbVisibleLaunchers" || parentNoteId === "_lbMobileVisibleLaunchers";
const isAvailableItem = parentNoteId === "_lbAvailableLaunchers" || parentNoteId === "_lbMobileAvailableLaunchers";
const isItem = isVisibleItem || isAvailableItem;

View File

@@ -18,6 +18,10 @@ export type PrintReport = {
} | {
type: "collection";
ignoredNoteIds: string[];
} | {
type: "error";
message: string;
stack?: string;
};
async function main() {

View File

@@ -0,0 +1,9 @@
.rendered-content.no-preview > div {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
height: 100%;
font-size: 500%;
flex-grow: 1;
}

View File

@@ -1,3 +1,5 @@
import "./content_renderer.css";
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
import WheelZoom from 'vanilla-js-wheel-zoom';
@@ -71,18 +73,9 @@ export async function getRenderedContent(this: {} | { ctx: string }, entity: FNo
$renderedContent.append($("<div>").append("<div>This note is protected and to access it you need to enter password.</div>").append("<br/>").append($button));
} else if (entity instanceof FNote) {
$renderedContent
.css("display", "flex")
.css("flex-direction", "column");
$renderedContent.addClass("no-preview");
$renderedContent.append(
$("<div>")
.css("display", "flex")
.css("justify-content", "space-around")
.css("align-items", "center")
.css("height", "100%")
.css("font-size", "500%")
.css("flex-grow", "1")
.append($("<span>").addClass(entity.getIcon()))
$("<div>").append($("<span>").addClass(entity.getIcon()))
);
if (entity.type === "webView" && entity.hasLabel("webViewSrc")) {
@@ -292,10 +285,11 @@ function getRenderingType(entity: FNote | FAttachment) {
}
const mime = "mime" in entity && entity.mime;
const isIconPack = entity instanceof FNote && entity.hasLabel("iconPack");
if (type === "file" && mime === "application/pdf") {
type = "pdf";
} else if ((type === "file" || type === "viewConfig") && mime && CODE_MIME_TYPES.has(mime)) {
} else if ((type === "file" || type === "viewConfig") && mime && CODE_MIME_TYPES.has(mime) && !isIconPack) {
type = "code";
} else if (type === "file" && mime && mime.startsWith("audio/")) {
type = "audio";

View File

@@ -1,5 +1,6 @@
import { t } from "./i18n";
import options from "./options";
import { isMobile } from "./utils";
export interface ExperimentalFeature {
id: string;
@@ -21,7 +22,7 @@ let enabledFeatures: Set<ExperimentalFeatureId> | null = null;
export function isExperimentalFeatureEnabled(featureId: ExperimentalFeatureId): boolean {
if (featureId === "new-layout") {
return options.is("newLayout");
return (isMobile() || options.is("newLayout"));
}
return getEnabledFeatures().has(featureId);
@@ -29,7 +30,7 @@ export function isExperimentalFeatureEnabled(featureId: ExperimentalFeatureId):
export function getEnabledExperimentalFeatureIds() {
const values = [ ...getEnabledFeatures().values() ];
if (options.is("newLayout")) {
if (isMobile() || options.is("newLayout")) {
values.push("new-layout");
}
return values;

View File

@@ -28,7 +28,7 @@
--bs-body-color: var(--main-text-color) !important;
--bs-body-bg: var(--main-background-color) !important;
--ck-mention-list-max-height: 500px;
--tn-modal-max-height: 90vh;
--tn-modal-max-height: 90svh;
--tree-item-light-theme-max-color-lightness: 50;
--tree-item-dark-theme-min-color-lightness: 75;
@@ -111,6 +111,7 @@ body.mobile #root-widget.virtual-keyboard-opened #mobile-bottom-bar {
}
#mobile-bottom-bar {
border-top: 1px solid var(--main-border-color);
padding-bottom: var(--mobile-bottom-offset);
}
@@ -409,6 +410,7 @@ body.desktop .tabulator-popup-container,
.dropdown-menu.static {
box-shadow: unset;
backdrop-filter: unset !important;
}
.dropend .dropdown-toggle::after {
@@ -454,7 +456,7 @@ body.desktop .tabulator-popup-container,
visibility: hidden;
}
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
.dropdown-menu:not(#context-menu-container) .dropdown-item,
body.desktop .dropdown-menu .dropdown-toggle,
body #context-menu-container .dropdown-item > span,
body.mobile .dropdown .dropdown-submenu > span {
@@ -462,6 +464,15 @@ body.mobile .dropdown .dropdown-submenu > span {
align-items: center;
}
body.mobile .dropdown .dropdown-submenu {
flex-wrap: wrap;
& > span {
flex-grow: 1;
}
}
.dropdown-item span.keyboard-shortcut,
.dropdown-item *:not(.keyboard-shortcut) > kbd {
flex-grow: 1;
@@ -1325,15 +1336,12 @@ body.desktop .dropdown-submenu > .dropdown-menu {
max-width: 300px;
}
.dropdown-submenu.dropstart > .dropdown-menu {
.dropdown-submenu.dropstart > .dropdown-menu,
body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
inset-inline-start: auto;
inset-inline-end: calc(100% - 2px);
}
body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
inset-inline-start: calc(-100% + 10px);
}
.right-dropdown-widget {
flex-shrink: 0;
}
@@ -1530,7 +1538,8 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
@media (max-width: 991px) {
body.mobile #launcher-pane .dropdown.global-menu > .dropdown-menu.show,
body.mobile #launcher-container .dropdown > .dropdown-menu.show {
body.mobile #launcher-container .dropdown > .dropdown-menu.show,
body.mobile .dropdown-menu.mobile-bottom-menu.show {
--dropdown-bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size));
position: fixed !important;
bottom: var(--dropdown-bottom) !important;
@@ -1542,6 +1551,16 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
max-height: calc(var(--tn-modal-max-height) - var(--dropdown-bottom));
}
body.mobile #launcher-container .dropdown > .dropdown-menu.show {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
body.mobile .dropdown-menu.mobile-bottom-menu.show {
--dropdown-bottom: 0px;
padding-bottom: calc(max(var(--menu-padding-size), env(safe-area-inset-bottom))) !important;
}
#mobile-sidebar-container {
position: fixed;
top: 0;
@@ -1667,47 +1686,15 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
background: var(--main-background-color);
}
.modal-dialog {
margin: var(--bs-modal-margin);
max-width: 80%;
}
body.mobile {
.modal-dialog {
margin: var(--bs-modal-margin);
max-width: 80%;
}
.modal-content {
height: 100%;
}
}
@media (max-width: 991px) {
body.mobile.force-fixed-tree #mobile-sidebar-wrapper {
padding-top: 0;
position: static;
height: 40vh;
width: 100vw;
transform: none !important;
background-color: var(--left-pane-background-color) !important;
border-bottom: 0.5px solid var(--main-border-color);
}
body.mobile.force-fixed-tree #mobile-sidebar-container {
display: none !important;
}
body.mobile.force-fixed-tree #mobile-sidebar-wrapper .quick-search {
display: none;
}
body.mobile.force-fixed-tree .component > button.bx-sidebar {
visibility: hidden;
padding: 0;
width: 6px;
}
body.mobile.force-fixed-tree #mobile-rest-container {
flex-direction: column !important;
}
body.mobile.force-fixed-tree #detail-container {
flex-grow: 1;
.modal-content {
height: 100%;
}
}
}
@@ -2623,14 +2610,14 @@ iframe.print-iframe {
}
}
#root-widget.virtual-keyboard-opened .note-split:not(:focus-within) {
#root-widget.virtual-keyboard-opened .note-split:not(.active) {
max-height: 80px;
opacity: 0.4;
}
}
}
body.desktop .title-row {
.title-row {
height: 50px;
min-height: 50px;
align-items: center;

View File

@@ -134,6 +134,7 @@
--left-pane-collapsed-border-color: #0009;
--left-pane-background-color: #1f1f1f;
--left-pane-text-color: #aaaaaa;
--left-pane-icon-color: #c5c5c5;
--left-pane-item-hover-background: #ffffff0d;
--left-pane-item-selected-background: #ffffff25;
--left-pane-item-selected-color: #dfdfdf;

View File

@@ -127,6 +127,7 @@
--left-pane-collapsed-border-color: #0000000d;
--left-pane-background-color: #f2f2f2;
--left-pane-text-color: #383838;
--left-pane-icon-color: currentColor;
--left-pane-item-hover-background: rgba(0, 0, 0, 0.032);
--left-pane-item-selected-background: white;
--left-pane-item-selected-color: black;

View File

@@ -800,3 +800,18 @@ li.dropdown-item a.dropdown-item-button:focus-visible {
background: var(--hover-item-background-color);
color: var(--hover-item-text-color);
}
/*
* Alert bars
*/
div.alert {
margin-bottom: 8px;
background: var(--alert-bar-background) !important;
border-radius: 8px;
font-size: .85em;
}
div.alert p + p {
margin-block: 1em 0;
}

View File

@@ -84,6 +84,22 @@ button.btn.btn-success kbd {
letter-spacing: 0.5pt;
}
/*
* Low profile buttons
*/
button.tn-low-profile {
appearance: none;
background: transparent;
border: 0;
border-radius: 8px;
color: inherit;
}
button.tn-low-profile:hover {
background-color: var(--icon-button-hover-background);
}
/*
* Icon buttons
*/
@@ -794,3 +810,35 @@ input[type="range"] {
scrollbar-width: unset;
}
}
/*
* Centered forms
*/
.tn-centered-form {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20vh;
}
.tn-centered-form .form-group {
text-align: center;
color: var(--muted-text-color);
}
.tn-centered-form .form-icon {
font-size: 140px;
color: var(--main-border-color);
}
.tn-centered-form .protected-session-password {
margin-inline: auto;
max-width: 350px;
text-align: center;
}
.tn-centered-form input,
.tn-centered-form button {
margin-top: 12px;
}

View File

@@ -47,9 +47,14 @@
}
/* The toolbar show / hide button for the current text block */
.ck.ck-block-toolbar-button {
:root .ck.ck-block-toolbar-button {
--ck-color-block-toolbar-button: var(--muted-text-color);
--ck-color-button-on-background: transparent;
--ck-color-button-on-color: currentColor;
--ck-color-button-on-color: var(--ck-editor-toolbar-button-on-color);
translate: -40% 0;
min-width: 0;
padding: 0;
z-index: 1600;
}
:root .ck.ck-toolbar .ck-button:not(.ck-disabled):active,
@@ -517,6 +522,10 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
* EDITOR'S CONTENT
*/
.note-detail-editable-text-editor > .ck-placeholder {
opacity: .5;
}
/*
* Code Blocks
*/

View File

@@ -57,12 +57,12 @@
height: 18px;
}
/*
/*
* SEARCH PAGE
*/
/* Button bar */
.search-definition-widget .search-setting-table tbody:last-child div {
.search-definition-widget .search-setting-table .search-actions-container {
justify-content: flex-end;
gap: 8px;
}
@@ -143,7 +143,7 @@
/*
* OPTIONS PAGES
*/
:root {
--options-card-min-width: 500px;
--options-card-max-width: 900px;
@@ -156,6 +156,10 @@
--preferred-max-content-width: var(--options-card-max-width);
}
.note-split.options .collection-properties {
visibility: hidden;
}
/* Create a gap at the top of the option pages */
.note-detail-content-widget-content.options>*:first-child {
margin-top: var(--options-first-item-top-margin, 1em);
@@ -261,13 +265,6 @@ body.desktop .options-section:not(.tn-no-card) {
margin-bottom: 6px;
}
.options-section .alert {
margin-bottom: 8px;
background: var(--alert-bar-background) !important;
border-radius: 8px;
font-size: .85em;
}
nav.options-section-tabs {
min-width: var(--options-card-min-width);
max-width: var(--options-card-max-width);
@@ -331,4 +328,4 @@ nav.options-section-tabs + .options-section {
.etapi-options-section div {
height: auto !important;
}
}

View File

@@ -739,18 +739,12 @@ body[dir=rtl] #left-pane span.fancytree-node.protected > span.fancytree-custom-i
transform: translateX(-25%);
}
body.mobile .fancytree-expander::before,
body.mobile .fancytree-title,
body.mobile .fancytree-node > span {
font-size: 1rem !important;
}
@media (max-width: 991px) {
body.mobile #mobile-sidebar-container {
background-color: rgba(0, 0, 0, 0.5);
}
body.mobile:not(.force-fixed-tree) #mobile-sidebar-wrapper {
body.mobile #mobile-sidebar-wrapper {
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
border-inline-end: 1px solid var(--subtle-border-color);
@@ -769,9 +763,9 @@ body.mobile .fancytree-node > span {
#left-pane .fancytree-custom-icon {
margin-top: 0; /* Use this to align the icon with the tree view item's caption */
color: var(--custom-color, var(--left-pane-icon-color));
}
#left-pane span.fancytree-active .fancytree-title {
font-weight: normal;
}
@@ -1272,7 +1266,7 @@ body.layout-horizontal #rest-pane > .classic-toolbar-widget {
#center-pane .note-split {
padding-top: 2px;
background-color: var(--note-split-background-color, var(--main-background-color));
transition: border-color 250ms ease-in;
transition: border-color 150ms ease-out;
border: 2px solid transparent;
}
@@ -1322,7 +1316,7 @@ body.mobile .note-title {
margin-inline-start: 0;
}
.title-row {
body.desktop .title-row {
/* Aligns the "Create new split" button with the note menu button (the three dots button) */
padding-inline-end: 3px;
}

View File

@@ -206,6 +206,7 @@ span.fancytree-selected .fancytree-title {
}
span.fancytree-selected .fancytree-custom-icon::before {
font-family: "boxicons";
content: "\eb43";
border: 1px solid var(--main-border-color);
border-radius: 3px;

View File

@@ -29,7 +29,9 @@
"widget-render-error": {
"title": "فشل عرض عنصر واجهة مستخدم React مخصص"
},
"widget-missing-parent": "لا تحتوي الأداة المخصصة على خاصية إلزامية '{{property}}'.\n\nإذا كان من المفترض تشغيل هذا البرنامج النصي بدون عنصر واجهة مستخدم، فاستخدم '#run=frontendStartup' بدلاً من ذلك."
"widget-missing-parent": "لا تحتوي الأداة المخصصة على خاصية إلزامية '{{property}}'.\n\nإذا كان من المفترض تشغيل هذا البرنامج النصي بدون عنصر واجهة مستخدم، فاستخدم '#run=frontendStartup' بدلاً من ذلك.",
"open-script-note": "فتح ملاحظة برمجية",
"scripting-error": "خطأ في النص البرمجي المخصص: {{title}}"
},
"add_link": {
"add_link": "أضافة رابط",
@@ -37,14 +39,19 @@
"search_note": "البحث عن الملاحظة بالاسم",
"link_title": "عنوان الرابط",
"button_add_link": "اضافة رابط",
"help_on_links": "مساعدة حول الارتباطات التشعبية"
"help_on_links": "مساعدة حول الارتباطات التشعبية",
"link_title_mirrors": "عنوان الرابط يعكس العنوان الحالي للملاحظة",
"link_title_arbitrary": "يمكن تغيير عنوان الرابط حسب الرغبة"
},
"branch_prefix": {
"edit_branch_prefix": "تعديل بادئة الفرع",
"prefix": "البادئة: ",
"save": "حفظ",
"help_on_tree_prefix": "مساعدة حول بادئة الشجرة",
"branch_prefix_saved": "تم حفظ بادئة الفرع."
"branch_prefix_saved": "تم حفظ بادئة الفرع.",
"edit_branch_prefix_multiple": "تعديل البادئة لـ {{count}} من تفرعات الملاحظات",
"branch_prefix_saved_multiple": "تم حفظ بادئة التفرع لـ {{count}} من التفرعات.",
"affected_branches": "الفروع المتأثرة ({{count}}):"
},
"bulk_actions": {
"bulk_actions": "اجراءات جماعية",
@@ -1173,9 +1180,6 @@
"note_not_found": "الملاحظة {{noteId}} غير موجودة!",
"cannot_match_transform": "تعذر مطابقة التحويل: {{transform}}"
},
"web_view": {
"web_view": "عرض الويب"
},
"consistency_checks": {
"title": "فحوصات التناسق"
},

View File

@@ -662,7 +662,8 @@
"show-cheatsheet": "显示快捷帮助",
"toggle-zen-mode": "禅模式",
"new-version-available": "新更新可用",
"download-update": "取得版本 {{latestVersion}}"
"download-update": "取得版本 {{latestVersion}}",
"search_notes": "搜索笔记"
},
"zen_mode": {
"button_exit": "退出禅模式"
@@ -745,7 +746,7 @@
"button_title": "导出SVG格式图片"
},
"relation_map_buttons": {
"create_child_note_title": "创建新的子笔记并添加到关系图",
"create_child_note_title": "创建子笔记并添加到图",
"reset_pan_zoom_title": "重置平移和缩放到初始坐标和放大倍率",
"zoom_in_title": "放大",
"zoom_out_title": "缩小"
@@ -759,7 +760,9 @@
"delete_this_note": "删除此笔记",
"error_cannot_get_branch_id": "无法获取 notePath '{{notePath}}' 的 branchId",
"error_unrecognized_command": "无法识别的命令 {{command}}",
"note_revisions": "笔记历史版本"
"note_revisions": "笔记历史版本",
"backlinks": "反链",
"content_language_switcher": "内容语言: {{language}}"
},
"note_icon": {
"change_note_icon": "更改笔记图标",
@@ -910,7 +913,8 @@
"unknown_search_option": "未知的搜索选项 {{searchOptionName}}",
"search_note_saved": "搜索笔记已保存到 {{- notePathTitle}}",
"actions_executed": "操作已执行。",
"view_options": "查看选项:"
"view_options": "查看选项:",
"option": "选项"
},
"similar_notes": {
"title": "相似笔记",
@@ -1064,11 +1068,6 @@
"note_detail_render_help_1": "之所以显示此帮助说明,是因为这个类型为渲染 HTML 的笔记没有正常工作所需的关系。",
"note_detail_render_help_2": "渲染 HTML 笔记类型用于<a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">编写脚本</a>。简而言之,您有一份 HTML 代码笔记(可包含一些 JavaScript然后这个笔记会把页面渲染出来。要使其正常工作您需要定义一个名为 \"renderNote\" 的<a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">关系</a>指向要渲染的 HTML 笔记。"
},
"web_view": {
"web_view": "网页视图",
"embed_websites": "网页视图类型的笔记允许您将网站嵌入到 Trilium 中。",
"create_label": "首先,请创建一个带有您要嵌入的 URL 地址的标签,例如 #webViewSrc=\"https://www.bing.com\""
},
"backend_log": {
"refresh": "刷新"
},
@@ -1417,7 +1416,8 @@
"description": "描述",
"reload_app": "重载应用以应用更改",
"set_all_to_default": "将所有快捷键重置为默认值",
"confirm_reset": "您确定要将所有键盘快捷键重置为默认值吗?"
"confirm_reset": "您确定要将所有键盘快捷键重置为默认值吗?",
"no_results": "未找到与“{{filter}}”匹配的快捷方式"
},
"spellcheck": {
"title": "拼写检查",
@@ -1618,7 +1618,9 @@
"print_report_title": "打印报告",
"print_report_collection_content_other": "集合中的 {{count}} 篇笔记无法打印,因为它们不受支持或受到保护。",
"print_report_collection_details_button": "查看详情",
"print_report_collection_details_ignored_notes": "忽略的笔记"
"print_report_collection_details_ignored_notes": "忽略的笔记",
"print_report_error_title": "打印失败",
"print_report_stack_trace": "堆栈跟踪"
},
"note_title": {
"placeholder": "请输入笔记标题...",
@@ -1782,8 +1784,8 @@
"desktop-application": "桌面应用程序",
"native-title-bar": "原生标题栏",
"native-title-bar-description": "对于 Windows 和 macOS关闭原生标题栏可使应用程序看起来更紧凑。在 Linux 上,保留原生标题栏可以更好地与系统集成。",
"background-effects": "启用背景效果(仅适用于 Windows 11",
"background-effects-description": "Mica 效果为应用窗口添加模糊且时尚的背景,营造出深度感和现代外观。「原生标题栏」必須被禁用。",
"background-effects": "启用背景效果",
"background-effects-description": "为应用窗口添加模糊且时尚的背景,营造出深度感和现代外观。「原生标题栏」必須被禁用。",
"restart-app-button": "重启应用程序以查看更改",
"zoom-factor": "缩放系数"
},
@@ -1802,7 +1804,8 @@
"geo-map": {
"create-child-note-title": "创建一个新的子笔记并将其添加到地图中",
"create-child-note-instruction": "单击地图以在该位置创建新笔记,或按 Escape 以取消。",
"unable-to-load-map": "无法加载地图。"
"unable-to-load-map": "无法加载地图。",
"create-child-note-text": "添加标记"
},
"geo-map-context": {
"open-location": "打开位置",
@@ -2117,7 +2120,7 @@
},
"call_to_action": {
"background_effects_title": "背景效果现已推出稳定版本",
"background_effects_message": "在 Windows 装置上,背景效果现在已完全稳定。背景效果通过模糊背后的背景,为使用者界面增添一抹色彩。此技术也用于其他应用程序,例如 Windows 资源管理器。",
"background_effects_message": "在 Windows 和 macOS 设备上,背景效果现在已稳定。背景效果通过模糊背后的背景,为使用者界面增添一抹色彩。",
"background_effects_button": "启用背景效果",
"next_theme_title": "试用新 Trilium 主题",
"next_theme_message": "当前使用旧版主题,要试用新主题吗?",
@@ -2253,5 +2256,22 @@
"pages_alt": "第{{pageNumber}}页",
"pages_loading": "加载中...",
"layers_other": "{{count}} 层"
},
"platform_indicator": {
"available_on": "在 {{platform}} 上可用"
},
"mobile_tab_switcher": {
"title_other": "{{count}} 选项卡",
"more_options": "更多选项"
},
"bookmark_buttons": {
"bookmarks": "书签"
},
"web_view_setup": {
"title": "直接在 Trilium 中创建网页的实时视图",
"url_placeholder": "输入或粘贴网站地址,例如 https://triliumnotes.org",
"create_button": "创建网页视图",
"invalid_url_title": "无效的地址",
"invalid_url_message": "请输入有效的网址,例如 https://triliumnotes.org。"
}
}

View File

@@ -1,6 +1,6 @@
{
"about": {
"title": "Über Trilium Notizen",
"title": "Über Trilium Notes",
"homepage": "Startseite:",
"app_version": "App-Version:",
"db_version": "DB-Version:",
@@ -662,7 +662,8 @@
"show-cheatsheet": "Cheatsheet anzeigen",
"toggle-zen-mode": "Zen Modus",
"new-version-available": "Neues Update verfügbar",
"download-update": "Version {{latestVersion}} herunterladen"
"download-update": "Version {{latestVersion}} herunterladen",
"search_notes": "Notizen durchsuchen"
},
"sync_status": {
"unknown": "<p>Der Synchronisations-Status wird bekannt, sobald der nächste Synchronisierungsversuch gestartet wird.</p><p>Klicke, um eine Synchronisierung jetzt auszulösen.</p>",
@@ -742,7 +743,7 @@
"button_title": "Diagramm als SVG exportieren"
},
"relation_map_buttons": {
"create_child_note_title": "Erstelle eine neue untergeordnete Notiz und füge sie dieser Beziehungskarte hinzu",
"create_child_note_title": "Erstelle eine untergeordnete Notiz und füge sie dieser Karte hinzu",
"reset_pan_zoom_title": "Schwenken und Zoomen auf die ursprünglichen Koordinaten und Vergrößerung zurücksetzen",
"zoom_in_title": "Hineinzoom",
"zoom_out_title": "Herauszoomen"
@@ -757,7 +758,9 @@
"delete_this_note": "Diese Notiz löschen",
"error_cannot_get_branch_id": "BranchId für notePath „{{notePath}}“ kann nicht abgerufen werden",
"error_unrecognized_command": "Unbekannter Befehl {{command}}",
"note_revisions": "Notiz Revisionen"
"note_revisions": "Notiz Revisionen",
"backlinks": "Rücklinks",
"content_language_switcher": "Inhaltssprache: {{language}}"
},
"note_icon": {
"change_note_icon": "Notiz-Icon ändern",
@@ -909,7 +912,8 @@
"unknown_search_option": "Unbekannte Suchoption {{searchOptionName}}",
"search_note_saved": "Suchnotiz wurde in {{-notePathTitle}} gespeichert",
"actions_executed": "Aktionen wurden ausgeführt.",
"view_options": "Optionen anzeigen:"
"view_options": "Optionen anzeigen:",
"option": "Option"
},
"similar_notes": {
"title": "Ähnliche Notizen",
@@ -1063,11 +1067,6 @@
"note_detail_render_help_1": "Diese Hilfesnotiz wird angezeigt, da diese Notiz vom Typ „HTML rendern“ nicht über die erforderliche Beziehung verfügt, um ordnungsgemäß zu funktionieren.",
"note_detail_render_help_2": "Render-HTML-Notiztyp wird benutzt für <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">scripting</a>. Kurzgesagt, du hast ein HTML-Code-Notiz (optional mit JavaScript) und diese Notiz rendert es. Damit es funktioniert, musst du eine a <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">Beziehung</a> namens \"renderNote\" zeigend auf die HTML-Notiz zum rendern definieren."
},
"web_view": {
"web_view": "Webansicht",
"embed_websites": "Notiz vom Typ Web View ermöglicht das Einbetten von Websites in Trilium.",
"create_label": "Um zu beginnen, erstelle bitte ein Label mit einer URL-Adresse, die eingebettet werden soll, z. B. #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Aktualisieren"
},
@@ -1383,7 +1382,8 @@
"description": "Beschreibung",
"reload_app": "Lade die App neu, um die Änderungen zu übernehmen",
"set_all_to_default": "Setze alle Verknüpfungen auf die Standardeinstellungen",
"confirm_reset": "Möchtest du wirklich alle Tastaturkürzel auf die Standardeinstellungen zurücksetzen?"
"confirm_reset": "Möchtest du wirklich alle Tastaturkürzel auf die Standardeinstellungen zurücksetzen?",
"no_results": "Keine Tastenkürzel für '{{filter}}' gefunden"
},
"spellcheck": {
"title": "Rechtschreibprüfung",
@@ -1587,7 +1587,9 @@
"print_report_collection_details_button": "Details anzeigen",
"print_report_collection_details_ignored_notes": "Ignorierte Notizen",
"print_report_collection_content_one": "{{count}} Notiz in der Sammlung konnte nicht gedruckt werden, weil sie nicht unterstützt oder geschützt ist.",
"print_report_collection_content_other": "{{count}} Notizen in der Sammlung konnten nicht gedruckt werden, weil sie nicht unterstützt oder geschützt sind."
"print_report_collection_content_other": "{{count}} Notizen in der Sammlung konnten nicht gedruckt werden, weil sie nicht unterstützt oder geschützt sind.",
"print_report_error_title": "Druck fehlgeschlagen",
"print_report_stack_trace": "Stapelzurückverfolgung"
},
"note_title": {
"placeholder": "Titel der Notiz hier eingeben…",
@@ -1771,7 +1773,8 @@
"geo-map": {
"create-child-note-title": "Neue Unternotiz anlegen und zur Karte hinzufügen",
"create-child-note-instruction": "Auf die Karte klicken, um eine neue Notiz an der Stelle zu erstellen oder Escape drücken um abzubrechen.",
"unable-to-load-map": "Karte konnte nicht geladen werden."
"unable-to-load-map": "Karte konnte nicht geladen werden.",
"create-child-note-text": "Marker hinzufügen"
},
"geo-map-context": {
"open-location": "Ort öffnen",
@@ -2270,5 +2273,20 @@
},
"platform_indicator": {
"available_on": "Verfügbar auf {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} Tab",
"title_other": "{{count}} Tabs",
"more_options": "Weitere Optionen"
},
"bookmark_buttons": {
"bookmarks": "Lesezeichen"
},
"web_view_setup": {
"title": "Erstelle eine Live-Ansicht einer Webseite direkt in Trilium",
"url_placeholder": "Gib oder füge die Adresse der Webseite ein, zum Beispiel https://triliumnotes.org",
"create_button": "Erstelle Web Ansicht",
"invalid_url_title": "Ungültige Adresse",
"invalid_url_message": "Füge eine valide Webadresse ein, zum Beispiel https://triliumnotes.org."
}
}

View File

@@ -13,6 +13,46 @@
"critical-error": {
"title": "Κρίσιμο σφάλμα",
"message": "Συνέβη κάποιο κρίσιμο σφάλμα, το οποίο δεν επιτρέπει στην εφαρμογή χρήστη να ξεκινήσει:\n\n{{message}}\n\nΤο πιθανότερο είναι να προκλήθηκε από κάποιο script που απέτυχε απρόοπτα. Δοκιμάστε να ξεκινήσετε την εφαρμογή σε ασφαλή λειτουργία για να λύσετε το πρόβλημα."
}
},
"widget-error": {
"title": "Δεν ήταν δυνατή η αρχικοποίηση του widget",
"message-custom": "Προσαρμοσμένο widget της σημείωσης με ID \"{{id}}\", με τίτλο \"{{title}}\", δεν ήταν δυνατό να αρχικοποιηθεί λόγω:\n\n{{message}}",
"message-unknown": "Άγνωστο widget δεν ήταν δυνατό να αρχικοποιηθεί λόγω:\n\n{{message}}"
},
"bundle-error": {
"title": "Δεν ήταν δυνατή η φόρτωση προσαρμοσμένου script",
"message": "Το script δεν ήταν δυνατό να εκτελεστεί λόγω:\n\n{{message}}"
},
"widget-list-error": {
"title": "Δεν ήταν δυνατή η λήψη της λίστας των widgets από τον server"
},
"widget-render-error": {
"title": "Δεν ήταν δυνατή η απόδοση προσαρμοσμένου React widget"
},
"widget-missing-parent": "Το προσαρμοσμένο widget δεν έχει ορισμένη την υποχρεωτική ιδιότητα '{{property}}'.\n\nΕάν το script προορίζεται για εκτέλεση χωρίς UI element, χρησιμοποιήστε '#run=frontendStartup' αντί για αυτό.",
"open-script-note": "Άνοιγμα σημείωσης script",
"scripting-error": "Σφάλμα προσαρμοσμένου script: {{title}}"
},
"bookmark_buttons": {
"bookmarks": "Σελιδοδείκτες"
},
"add_link": {
"add_link": "Προσθήκη συνδέσμου",
"help_on_links": "Βοήθεια για συνδέσμους",
"note": "Σημείωση",
"search_note": "Αναζήτηση σημείωσης με βάση το όνομά της",
"link_title_mirrors": "Ο τίτλος του συνδέσμου αντικατοπτρίζει τον τρέχοντα τίτλο της σημείωσης",
"link_title_arbitrary": "Ο τίτλος του συνδέσμου μπορεί να τροποποιηθεί ελεύθερα",
"link_title": "Τίτλος συνδέσμου",
"button_add_link": "Προσθήκη συνδέσμου"
},
"branch_prefix": {
"edit_branch_prefix": "Επεξεργασία προθέματος κλάδου",
"edit_branch_prefix_multiple": "Επεξεργασία προθέματος κλάδου για {{count}} κλάδους",
"help_on_tree_prefix": "Βοήθεια για πρόθεμα δέντρου",
"prefix": "Πρόθεμα: ",
"save": "Αποθήκευση",
"branch_prefix_saved": "Το πρόθεμα κλάδου αποθηκεύτηκε.",
"branch_prefix_saved_multiple": "Το πρόθεμα κλάδου αποθηκεύτηκε για {{count}} κλάδους."
}
}

View File

@@ -662,7 +662,8 @@
"show-cheatsheet": "Show Cheatsheet",
"toggle-zen-mode": "Zen Mode",
"new-version-available": "New Update Available",
"download-update": "Get Version {{latestVersion}}"
"download-update": "Get Version {{latestVersion}}",
"search_notes": "Search notes"
},
"zen_mode": {
"button_exit": "Exit Zen Mode"
@@ -745,7 +746,7 @@
"button_title": "Export diagram as SVG"
},
"relation_map_buttons": {
"create_child_note_title": "Create new child note and add it into this relation map",
"create_child_note_title": "Create child note and add it to map",
"reset_pan_zoom_title": "Reset pan & zoom to initial coordinates and magnification",
"zoom_in_title": "Zoom In",
"zoom_out_title": "Zoom Out"
@@ -760,7 +761,9 @@
"delete_this_note": "Delete this note",
"note_revisions": "Note revisions",
"error_cannot_get_branch_id": "Cannot get branchId for notePath '{{notePath}}'",
"error_unrecognized_command": "Unrecognized command {{command}}"
"error_unrecognized_command": "Unrecognized command {{command}}",
"backlinks": "Backlinks",
"content_language_switcher": "Content language: {{language}}"
},
"note_icon": {
"change_note_icon": "Change note icon",
@@ -905,6 +908,7 @@
"debug": "debug",
"debug_description": "Debug will print extra debugging information into the console to aid in debugging complex queries",
"action": "action",
"option": "option",
"search_button": "Search",
"search_execute": "Search & Execute actions",
"save_to_note": "Save to note",
@@ -1066,10 +1070,12 @@
"note_detail_render_help_1": "This help note is shown because this note of type Render HTML doesn't have required relation to function properly.",
"note_detail_render_help_2": "Render HTML note type is used for <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">scripting</a>. In short, you have a HTML code note (optionally with some JavaScript) and this note will render it. To make it work, you need to define a <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">relation</a> called \"renderNote\" pointing to the HTML note to render."
},
"web_view": {
"web_view": "Web View",
"embed_websites": "Note of type Web View allows you to embed websites into Trilium.",
"create_label": "To start, please create a label with a URL address you want to embed, e.g. #webViewSrc=\"https://www.google.com\""
"web_view_setup": {
"title": "Create a live view of a webpage directly into Trilium",
"url_placeholder": "Enter or paste the website address, for example https://triliumnotes.org",
"create_button": "Create Web View",
"invalid_url_title": "Invalid address",
"invalid_url_message": "Insert a valid web address, for example https://triliumnotes.org."
},
"backend_log": {
"refresh": "Refresh"
@@ -1585,7 +1591,8 @@
"description": "Description",
"reload_app": "Reload app to apply changes",
"set_all_to_default": "Set all shortcuts to the default",
"confirm_reset": "Do you really want to reset all keyboard shortcuts to the default?"
"confirm_reset": "Do you really want to reset all keyboard shortcuts to the default?",
"no_results": "No shortcuts found matching '{{filter}}'"
},
"spellcheck": {
"title": "Spell Check",
@@ -1791,6 +1798,8 @@
"printing": "Printing in progress...",
"printing_pdf": "Exporting to PDF in progress...",
"print_report_title": "Print report",
"print_report_error_title": "Failed to print",
"print_report_stack_trace": "Stack trace",
"print_report_collection_content_one": "{{count}} note in the collection could not be printed because they are not supported or they are protected.",
"print_report_collection_content_other": "{{count}} notes in the collection could not be printed because they are not supported or they are protected.",
"print_report_collection_details_button": "See details",
@@ -2276,5 +2285,8 @@
"title_one": "{{count}} tab",
"title_other": "{{count}} tabs",
"more_options": "More options"
},
"bookmark_buttons": {
"bookmarks": "Bookmarks"
}
}

View File

@@ -662,7 +662,8 @@
"show-cheatsheet": "Mostrar hoja de trucos",
"toggle-zen-mode": "Modo Zen",
"new-version-available": "Nueva actualización disponible",
"download-update": "Obtener versión {{latestVersion}}"
"download-update": "Obtener versión {{latestVersion}}",
"search_notes": "Buscar notas"
},
"zen_mode": {
"button_exit": "Salir del modo Zen"
@@ -745,7 +746,7 @@
"button_title": "Exportar diagrama como SVG"
},
"relation_map_buttons": {
"create_child_note_title": "Crear una nueva subnota y agregarla a este mapa de relaciones",
"create_child_note_title": "Crear una subnota y agregarla al mapa",
"reset_pan_zoom_title": "Restablecer la panorámica y el zoom a las coordenadas y ampliación iniciales",
"zoom_in_title": "Acercar",
"zoom_out_title": "Alejar"
@@ -754,14 +755,16 @@
"relation": "relación",
"backlink_one": "{{count}} Vínculo de retroceso",
"backlink_many": "{{count}} Vínculos de retroceso",
"backlink_other": "{{count}} vínculos de retroceso"
"backlink_other": "{{count}} Vínculos de retroceso"
},
"mobile_detail_menu": {
"insert_child_note": "Insertar subnota",
"delete_this_note": "Eliminar esta nota",
"error_cannot_get_branch_id": "No se puede obtener el branchID del notePath '{{notePath}}'",
"error_unrecognized_command": "Comando no reconocido {{command}}",
"note_revisions": "Revisiones de notas"
"note_revisions": "Revisiones de notas",
"backlinks": "Vínculos de retroceso",
"content_language_switcher": "Idioma de contenido: {{language}}"
},
"note_icon": {
"change_note_icon": "Cambiar icono de nota",
@@ -914,7 +917,8 @@
"unknown_search_option": "Opción de búsqueda desconocida {{searchOptionName}}",
"search_note_saved": "La nota de búsqueda se ha guardado en {{- notePathTitle}}",
"actions_executed": "Las acciones han sido ejecutadas.",
"view_options": "Ver opciones:"
"view_options": "Ver opciones:",
"option": "opción"
},
"similar_notes": {
"title": "Notas similares",
@@ -1068,11 +1072,6 @@
"note_detail_render_help_1": "Esta nota de ayuda se muestra porque esta nota de tipo Renderizar HTML no tiene la relación requerida para funcionar correctamente.",
"note_detail_render_help_2": "El tipo de nota Render HTML es usado para <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">scripting</a>. De forma resumida, tiene una nota con código HTML (opcionalmente con algo de JavaScript) y esta nota la renderizará. Para que funcione, es necesario definir una <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">relación</a> llamada \"renderNote\" apuntando a la nota HTML nota a renderizar."
},
"web_view": {
"web_view": "Vista web",
"embed_websites": "La nota de tipo Web View le permite insertar sitios web en Trilium.",
"create_label": "Para comenzar, por favor cree una etiqueta con una dirección URL que desee empotrar, e.g. #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Refrescar"
},
@@ -1573,7 +1572,8 @@
"description": "Descripción",
"reload_app": "Vuelva a cargar la aplicación para aplicar los cambios",
"set_all_to_default": "Establecer todos los accesos directos al valor predeterminado",
"confirm_reset": "¿Realmente desea restablecer todos los atajos de teclado a sus valores predeterminados?"
"confirm_reset": "¿Realmente desea restablecer todos los atajos de teclado a sus valores predeterminados?",
"no_results": "No se encontraron atajos que coincidan con '{{filter}} '"
},
"spellcheck": {
"title": "Revisión ortográfica",
@@ -1780,7 +1780,9 @@
"print_report_collection_content_other": "{{count}} notas en la colección no se pueden imprimir porque no son compatibles o están protegidas.",
"print_report_title": "Imprimir informe",
"print_report_collection_details_button": "Ver detalles",
"print_report_collection_details_ignored_notes": "Notas ignoradas"
"print_report_collection_details_ignored_notes": "Notas ignoradas",
"print_report_stack_trace": "Rastreo de pila",
"print_report_error_title": "Fallo al imprimir"
},
"note_title": {
"placeholder": "escriba el título de la nota aquí...",
@@ -2285,5 +2287,21 @@
},
"platform_indicator": {
"available_on": "Disponible en {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} pestaña",
"title_many": "{{count}} pestañas",
"title_other": "{{count}} pestañas",
"more_options": "Más opciones"
},
"bookmark_buttons": {
"bookmarks": "Marcadores"
},
"web_view_setup": {
"title": "Crear una vista en vivo de una página web directamente en Trilium",
"url_placeholder": "Ingresar o pegar la dirección del sitio web, por ejemplo https://triliumnotes.org",
"create_button": "Crear Vista Web",
"invalid_url_title": "Dirección inválida",
"invalid_url_message": "Ingrese una dirección web válida, por ejemplo https://triliumnotes.org."
}
}

View File

@@ -1057,11 +1057,6 @@
"note_detail_render_help_1": "Cette note d'aide s'affiche car cette note de type Rendu HTML n'a pas la relation requise pour fonctionner correctement.",
"note_detail_render_help_2": "Le type de note Rendu HTML est utilisé pour les <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">scripts</a>. En résumé, vous disposez d'une note de code HTML (éventuellement contenant JavaScript) et cette note affichera le rendu. Pour que cela fonctionne, vous devez définir une <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">relation</a> appelée \"renderNote\" pointant vers la note HTML à rendre."
},
"web_view": {
"web_view": "Affichage Web",
"embed_websites": "Les notes de type Affichage Web vous permet d'intégrer des sites Web dans Trilium.",
"create_label": "Pour commencer, veuillez créer un label avec l'adresse URL que vous souhaitez intégrer, par ex. #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Rafraîchir"
},

File diff suppressed because it is too large Load Diff

View File

@@ -50,7 +50,8 @@
"save": "Simpan",
"branch_prefix_saved": "Prefiks cabang telah disimpan.",
"branch_prefix_saved_multiple": "Prefix cabang telah disimpan pada {{count}} cabang.",
"affected_branches": "Cabang terdampak ({{count}}):"
"affected_branches": "Cabang terdampak ({{count}}):",
"edit_branch_prefix": "Sunting awalan cabang"
},
"bulk_actions": {
"bulk_actions": "Aksi borongan",
@@ -61,7 +62,10 @@
"execute_bulk_actions": "Eksekusi aksi borongan",
"bulk_actions_executed": "Aksi borongan telah di eksekusi dengan sukses.",
"none_yet": "Belum ada... tambahkan aksi dengan memilih salah satu dari aksi di atas.",
"labels": "Label-label"
"labels": "Label-label",
"relations": "Hubungan",
"notes": "Catatan",
"other": "Lainnya"
},
"confirm": {
"cancel": "Batal",
@@ -80,6 +84,8 @@
"no_note_to_delete": "Tidak ada Catatan yang akan dihapus (hanya duplikat)."
},
"clone_to": {
"clone_notes_to": "Duplikat catatan ke…"
"clone_notes_to": "Duplikat catatan ke…",
"help_on_links": "Bantuan pada tautan",
"notes_to_clone": "Catatan untuk kloning"
}
}

View File

@@ -186,7 +186,8 @@
"geo-map": {
"create-child-note-title": "Crea una nota figlia e aggiungila alla mappa",
"create-child-note-instruction": "Clicca sulla mappa per creare una nuova nota qui o premi Escape per uscire.",
"unable-to-load-map": "Impossibile caricare la mappa."
"unable-to-load-map": "Impossibile caricare la mappa.",
"create-child-note-text": "Aggiungi indicatore"
},
"geo-map-context": {
"open-location": "Apri la posizione",
@@ -422,7 +423,8 @@
"unknown_search_option": "Opzione di ricerca sconosciuta {{searchOptionName}}",
"search_note_saved": "La nota di ricerca è stata salvata in {{- notePathTitle}}",
"actions_executed": "Le azioni sono state eseguite.",
"view_options": "Opzioni di visualizzazione:"
"view_options": "Opzioni di visualizzazione:",
"option": "opzione"
},
"modal": {
"close": "Chiudi",
@@ -1241,7 +1243,8 @@
"show-cheatsheet": "Mostra il foglietto illustrativo",
"toggle-zen-mode": "Modalità Zen",
"new-version-available": "Nuovo aggiornamento disponibile",
"download-update": "Ottieni la versione {{latestVersion}}"
"download-update": "Ottieni la versione {{latestVersion}}",
"search_notes": "Cerca note"
},
"zen_mode": {
"button_exit": "Esci dalla modalità Zen"
@@ -1340,7 +1343,9 @@
"delete_this_note": "Elimina questa nota",
"note_revisions": "Revisioni delle note",
"error_cannot_get_branch_id": "Impossibile ottenere branchId per notePath '{{notePath}}'",
"error_unrecognized_command": "Comando non riconosciuto {{command}}"
"error_unrecognized_command": "Comando non riconosciuto {{command}}",
"backlinks": "Backlinks",
"content_language_switcher": "Lingua dei contenuti: {{language}}"
},
"note_icon": {
"change_note_icon": "Cambia icona nota",
@@ -1581,11 +1586,6 @@
"note_detail_render_help_1": "Questa nota di aiuto viene visualizzata perché questa nota di tipo Render HTML non ha la relazione richiesta per funzionare correttamente.",
"note_detail_render_help_2": "Il tipo di nota HTML Render viene utilizzato per lo <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">scripting</a>. In breve, si ottiene una nota in codice HTML (opzionalmente con un po' di JavaScript) che verrà visualizzata. Per farla funzionare, è necessario definire una <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">relazione</a> denominata \"renderNote\" che punti alla nota HTML da visualizzare."
},
"web_view": {
"web_view": "Visualizzazione Web",
"embed_websites": "La nota di tipo Web View consente di incorporare siti web in Trilium.",
"create_label": "Per iniziare, crea un'etichetta con l'indirizzo URL che desideri incorporare, ad esempio #webViewSrc=\"https://www.google.com\""
},
"vacuum_database": {
"title": "Pulizia del database",
"description": "Questa operazione ricostruirà il database, generando in genere un file di dimensioni inferiori. In realtà, nessun dato verrà modificato.",
@@ -2145,7 +2145,7 @@
"background_effects_title": "Gli effetti di sfondo sono ora stabili",
"background_effects_message": "Sui dispositivi Windows, gli effetti di sfondo sono ora completamente stabili. Gli effetti di sfondo aggiungono un tocco di colore all'interfaccia utente sfocando lo sfondo retrostante. Questa tecnica è utilizzata anche in altre applicazioni come Esplora risorse di Windows.",
"background_effects_button": "Abilita gli effetti di sfondo",
"dismiss": "Congedare",
"dismiss": "Chiudi",
"new_layout_title": "Nuovo layout",
"new_layout_message": "Abbiamo introdotto un layout modernizzato per Trilium. La barra multifunzione è stata rimossa e integrata perfettamente nell'interfaccia principale, con una nuova barra di stato e sezioni espandibili (come gli attributi promossi) che assumono le funzioni chiave.\n\nIl nuovo layout è abilitato di default e può essere temporaneamente disabilitato tramite Opzioni → Aspetto.",
"new_layout_button": "Maggiori informazioni"
@@ -2281,5 +2281,24 @@
"pages_other": "{{count}} pagine",
"pages_alt": "Pagina {{pageNumber}}",
"pages_loading": "Caricamento in corso..."
},
"web_view_setup": {
"title": "Crea una visualizzazione live di una pagina web direttamente in Trilium",
"url_placeholder": "Inserisci o incolla l'indirizzo del sito web, ad esempio https://triliumnotes.org",
"create_button": "Crea vista Web",
"invalid_url_title": "Indirizzo non valido",
"invalid_url_message": "Inserisci un indirizzo web valido, ad esempio https://triliumnotes.org."
},
"platform_indicator": {
"available_on": "Disponibile su {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "Scheda {{count}}",
"title_many": "Schede {{count}}",
"title_other": "Schede {{count}}",
"more_options": "Altre opzioni"
},
"bookmark_buttons": {
"bookmarks": "Segnalibri"
}
}

View File

@@ -81,7 +81,8 @@
"configure_launchbar": "ランチャーバーの設定",
"show_shared_notes_subtree": "共有ノートのサブツリーを表示",
"new-version-available": "新しいアップデートが利用可能",
"download-update": "{{latestVersion}} をバージョンを入手"
"download-update": "{{latestVersion}} をバージョンを入手",
"search_notes": "検索ノート"
},
"left_pane_toggle": {
"show_panel": "パネルを表示",
@@ -234,7 +235,8 @@
"search_note_saved": "検索ノートが {{- notePathTitle}} に保存されました",
"actions_executed": "アクションが実行されました。",
"ancestor": "祖先:",
"view_options": "表示オプション:"
"view_options": "表示オプション:",
"option": "オプション"
},
"shortcuts": {
"multiple_shortcuts": "同じアクションに対して複数のショートカットを設定する場合、カンマで区切ることができます。",
@@ -247,7 +249,8 @@
"reload_app": "リロードして変更を適用する",
"set_all_to_default": "すべてのショートカットをデフォルトに戻す",
"confirm_reset": "キーボードショートカットをすべてデフォルトにリセットしますか?",
"keyboard_shortcuts": "キーボードショートカット"
"keyboard_shortcuts": "キーボードショートカット",
"no_results": "'{{filter}}' に一致するショートカットが見つかりません"
},
"confirm": {
"confirmation": "確認",
@@ -407,7 +410,7 @@
"relation_map_buttons": {
"zoom_out_title": "ズームアウト",
"zoom_in_title": "ズームイン",
"create_child_note_title": "新しい子ノートを作成し、関連マップに追加",
"create_child_note_title": "子ノートを作成し、マップに追加",
"reset_pan_zoom_title": "パンとズームを初期座標と倍率にリセット"
},
"tree-context-menu": {
@@ -824,11 +827,6 @@
"error_no_path": "移動するパスがありません。",
"move_success_message": "選択したノートは以下に移動されました "
},
"web_view": {
"web_view": "Web ビュー",
"embed_websites": "Web ビュータイプでは、web サイトを Trilium に埋め込むことができます。",
"create_label": "まず始めに、埋め込みたいURLアドレスのラベルを作成してください。例: #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "リフレッシュ"
},
@@ -1648,7 +1646,9 @@
"error_unrecognized_command": "認識されないコマンド {{command}}",
"insert_child_note": "子ノートを挿入",
"error_cannot_get_branch_id": "ノートパス 「{{notePath}} のbranchIdを取得できません",
"note_revisions": "ノートの変更履歴"
"note_revisions": "ノートの変更履歴",
"backlinks": "バックリンク",
"content_language_switcher": "コンテンツの言語: {{language}}"
},
"inherited_attribute_list": {
"title": "継承属性",
@@ -1955,7 +1955,9 @@
"print_report_title": "レポートを印刷",
"print_report_collection_content_other": "コレクション内の {{count}} 件のノートは、サポートされていないか保護されているため、印刷できませんでした。",
"print_report_collection_details_button": "詳細を見る",
"print_report_collection_details_ignored_notes": "無視されたノート"
"print_report_collection_details_ignored_notes": "無視されたノート",
"print_report_error_title": "印刷に失敗しました",
"print_report_stack_trace": "スタックトレース"
},
"watched_file_update_status": {
"ignore_this_change": "この変更を無視する",
@@ -2008,7 +2010,8 @@
"geo-map": {
"create-child-note-title": "新しい子ノートを作成し、マップに追加する",
"create-child-note-instruction": "地図をクリックしてその場所に新しいートを作成するか、Esc キーを押して閉じます。",
"unable-to-load-map": "マップを読み込めません。"
"unable-to-load-map": "マップを読み込めません。",
"create-child-note-text": "マーカーを追加"
},
"geo-map-context": {
"open-location": "現在位置を表示",
@@ -2256,5 +2259,19 @@
},
"platform_indicator": {
"available_on": "{{platform}} で利用可能"
},
"mobile_tab_switcher": {
"title_other": "{{count}} タブ",
"more_options": "その他のオプション"
},
"bookmark_buttons": {
"bookmarks": "ブックマーク"
},
"web_view_setup": {
"title": "Trilium に直接 Web ページのライブビューを作成",
"url_placeholder": "Web サイトのアドレスを入力または貼り付けて下さい。 例: https://triliumnotes.org",
"create_button": "Web ビューを作成",
"invalid_url_title": "無効なアドレス",
"invalid_url_message": "有効な Web アドレスを入力してください。 例: https://triliumnotes.org"
}
}

View File

@@ -21,7 +21,7 @@
},
"bundle-error": {
"title": "Nie udało się załadować niestandardowego skryptu",
"message": "Skrypt z notatki o ID \"{{id}}\", zatytułowany \"{{title}}\", nie mógł zostać wykonany z powodu:\n\n{{message}}"
"message": "Skrypt nie mógł zostać wykonany z powodu:\n\n{{message}}"
},
"widget-list-error": {
"title": "Nie udało się pobrać listy widżetów z serwera"
@@ -29,8 +29,9 @@
"widget-render-error": {
"title": "Nie udało się wyrenderować niestandardowego widżetu React"
},
"widget-missing-parent": "Niestandardowy widżet nie ma zdefiniowanej obowiązkowej właściwości „{{property}}”.",
"open-script-note": "Otwórz notatkę ze skryptem"
"widget-missing-parent": "Niestandardowy widżet nie ma zdefiniowanej obowiązkowej właściwości „{{property}}”.\nJeśli skrypt ma działać bez interfejsu użytkownika (UI) wyłącz go: '#run=frontendStartup'.",
"open-script-note": "Otwórz notatkę ze skryptem",
"scripting-error": "Błąd skryptu użytkownika: {{title}}"
},
"add_link": {
"add_link": "Dodaj link",
@@ -191,7 +192,8 @@
"expand_tooltip": "Rozwija bezpośrednie elementy podrzędne tej kolekcji (o jeden poziom). Aby uzyskać więcej opcji, naciśnij strzałkę po prawej.",
"expand_first_level": "Rozwiń bezpośrednie elementy podrzędne",
"expand_nth_level": "Rozwiń {{depth}} poziomów",
"expand_all_levels": "Rozwiń wszystkie poziomy"
"expand_all_levels": "Rozwiń wszystkie poziomy",
"hide_child_notes": "Ukryj notatki podrzędne w derzwie"
},
"board_view": {
"move-to": "Przenieś do",
@@ -240,7 +242,7 @@
"background_effects_title": "Efekty tła są teraz stabilne",
"dismiss": "Odrzuć",
"background_effects_button": "Włącz efekty tła",
"background_effects_message": "Na urządzeniach z systemem Windows efekty tła są teraz w pełni stabilne. Efekty tła dodają odrobinę koloru do interfejsu użytkownika poprzez rozmycie tła za nim. Ta technika jest również stosowana w innych aplikacjach, takich jak Eksplorator Windows.",
"background_effects_message": "Na urządzeniach z systemem Windows i macOS efekty tła są stabilne. Efekty tła dodają odrobinę koloru do interfejsu użytkownika poprzez rozmycie tła za nim.",
"new_layout_title": "Nowy układ",
"new_layout_message": "Wprowadziliśmy zmodernizowany układ interfejsu dla Trilium. Wstążka została usunięta i płynnie zintegrowana z głównym interfejsem, a jej kluczowe funkcje przejęły nowy pasek stanu i rozwijane sekcje (takie jak promowane atrybuty).\n\nNowy układ jest domyślnie włączony i można go tymczasowo wyłączyć w Ustawienia → Wygląd.",
"new_layout_button": "Szczegóły"
@@ -520,7 +522,8 @@
"action": "akcja",
"search_button": "Szukaj",
"search_execute": "Szukaj i wykonaj akcje",
"view_options": "Ustawienia widoku:"
"view_options": "Ustawienia widoku:",
"option": "opcja"
},
"similar_notes": {
"title": "Podobne notatki",
@@ -602,8 +605,8 @@
"desktop-application": "Aplikacja desktopowa",
"native-title-bar": "Natywny pasek tytułu",
"native-title-bar-description": "Dla Windows i macOS, wyłączenie natywnego paska tytułu sprawia, że aplikacja wygląda bardziej kompaktowo. Na Linuxie, włączenie natywnego paska tytułu lepiej integruje się z resztą systemu.",
"background-effects": "Włącz efekty tła (tylko Windows 11)",
"background-effects-description": "Efekt Mica dodaje rozmyte, stylowe tło do okien aplikacji, tworząc głębię i nowoczesny wygląd. \"Natywny pasek tytułu\" musi być wyłączony.",
"background-effects": "Włącz efekty tła",
"background-effects-description": "Dodaje rozmyte, stylowe tło do okien aplikacji, tworząc głębię i nowoczesny wygląd. \"Natywny pasek tytułu\" musi być wyłączony.",
"restart-app-button": "Zrestartuj aplikację, aby zobaczyć zmiany",
"zoom-factor": "Współczynnik powiększenia"
},
@@ -1182,7 +1185,8 @@
"show-cheatsheet": "Pokaż ściągawkę",
"toggle-zen-mode": "Tryb Zen",
"new-version-available": "Dostępna nowa aktualizacja",
"download-update": "Pobierz wersję {{latestVersion}}"
"download-update": "Pobierz wersję {{latestVersion}}",
"search_notes": "Przeszukaj notatki"
},
"zen_mode": {
"button_exit": "Wyjdź z trybu Zen"
@@ -1265,7 +1269,7 @@
"button_title": "Eksportuj diagram jako SVG"
},
"relation_map_buttons": {
"create_child_note_title": "Utwórz nową notatkę podrzędną i dodaj ją do tej mapy relacji",
"create_child_note_title": "Utwórz notatkę podrzędną i dodaj ją do mapy",
"reset_pan_zoom_title": "Zresetuj przesunięcie i powiększenie do początkowych współrzędnych i powiększenia",
"zoom_in_title": "Powiększ",
"zoom_out_title": "Pomniejsz"
@@ -1281,12 +1285,23 @@
"delete_this_note": "Usuń tę notatkę",
"note_revisions": "Wersje notatki",
"error_cannot_get_branch_id": "Nie można pobrać branchId dla ścieżki notatki '{{notePath}}'",
"error_unrecognized_command": "Nierozpoznane polecenie {{command}}"
"error_unrecognized_command": "Nierozpoznane polecenie {{command}}",
"backlinks": "Linki zwrotne",
"content_language_switcher": "Język treści: {{language}}"
},
"note_icon": {
"change_note_icon": "Zmień ikonę notatki",
"search": "Szukaj:",
"reset-default": "Przywróć domyślną ikonę"
"reset-default": "Przywróć domyślną ikonę",
"search_placeholder_one": "Znaleziono {{number}} ikonę w {{count}} pakietach",
"search_placeholder_few": "Znaleziono {{number}} ikon w {{count}} pakietach",
"search_placeholder_many": "Znaleziono {{number}} ikon w {{count}} pakietach",
"search_placeholder_filtered": "Wyszukaj {{number}} ikon w {{name}}",
"filter": "Filtr",
"filter-none": "Wszystkie ikony",
"filter-default": "Domyślne ikony",
"icon_tooltip": "{{name}}\npakiet ikon: {{iconPack}}",
"no_results": "Nie znaleziono ikon."
},
"basic_properties": {
"note_type": "Typ notatki",
@@ -1421,11 +1436,6 @@
"note_detail_render_help_1": "Ta notatka pomocy jest wyświetlana, ponieważ ta notatka typu Render HTML nie ma wymaganej relacji do poprawnego działania.",
"note_detail_render_help_2": "Typ notatki Render HTML jest używany do <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">skryptowania</a>. W skrócie, masz notatkę kodu HTML (opcjonalnie z JavaScript) i ta notatka ją wyrenderuje. Aby to zadziałało, musisz zdefiniować <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">relację</a> o nazwie \"renderNote\" wskazującą na notatkę HTML do wyrenderowania."
},
"web_view": {
"web_view": "Widok WWW",
"embed_websites": "Notatka typu Widok WWW pozwala na osadzanie stron internetowych w Trilium.",
"create_label": "Aby rozpocząć, utwórz etykietę z adresem URL, który chcesz osadzić, np. #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Odśwież"
},
@@ -1826,7 +1836,7 @@
"will_be_deleted_in": "Ten załącznik zostanie automatycznie usunięty za {{time}}",
"will_be_deleted_soon": "Ten załącznik zostanie wkrótce automatycznie usunięty",
"deletion_reason": ", ponieważ załącznik nie jest podlinkowany w treści notatki. Aby zapobiec usunięciu, dodaj link do załącznika z powrotem do treści lub przekonwertuj załącznik na notatkę.",
"role_and_size": "Rola: {{role}}, Rozmiar: {{size}}",
"role_and_size": "Rola: {{role}}, Rozmiar: {{size}}, MIME: {{- mimeType}}",
"link_copied": "Link do załącznika skopiowany do schowka.",
"unrecognized_role": "Nierozpoznana rola załącznika '{{role}}'."
},
@@ -1880,7 +1890,10 @@
"apply-bulk-actions": "Zastosuj akcje masowe",
"converted-to-attachments": "{{count}} notatek zostało przekonwertowanych na załączniki.",
"convert-to-attachment-confirm": "Czy na pewno chcesz przekonwertować wybrane notatki na załączniki ich notatek nadrzędnych? Ta operacja dotyczy tylko notatek Obrazów, inne notatki zostaną pominięte.",
"open-in-popup": "Szybka edycja"
"open-in-popup": "Szybka edycja",
"open-in-a-new-window": "Otwórz w nowym oknie",
"hide-subtree": "Ukryj gałąź",
"show-subtree": "Rozwiń gałąź"
},
"shared_info": {
"shared_publicly": "Ta notatka jest udostępniona publicznie pod adresem {{- link}}.",
@@ -1971,7 +1984,17 @@
"create-child-note": "Utwórz notatkę podrzędną",
"unhoist": "Cofnij zawężenie",
"toggle-sidebar": "Przełącz pasek boczny",
"dropping-not-allowed": "Upuszczanie notatek w tej lokalizacji jest niedozwolone."
"dropping-not-allowed": "Upuszczanie notatek w tej lokalizacji jest niedozwolone.",
"clone-indicator-tooltip": "Ta notatka ma {{- count}} notatek nadrzędnych: {{- parents}}",
"clone-indicator-tooltip-single": "Ta notatka jest sklonowana (1 dodatkowa notatka nadrzędna: {{- parent}})",
"shared-indicator-tooltip": "Ta notatka jest udostępniona publicznie",
"shared-indicator-tooltip-with-url": "Ta notatka jest udostępniana publicznie jako: {{- url}}",
"subtree-hidden-tooltip_one": "{{count}} notatka podrzędna ukryta w drzewie",
"subtree-hidden-tooltip_few": "{{count}} notatek podrzędnych ukrytych w drzewie",
"subtree-hidden-tooltip_many": "{{count}} notatek podrzędnych ukrytych w drzewie",
"subtree-hidden-moved-title": "Dodano do {{title}}",
"subtree-hidden-moved-description-collection": "Ta kolekcja ukrywa swoje notatki podrzędne w drzewie.",
"subtree-hidden-moved-description-other": "Notatki podrzędne są ukryte w drzewie tej notatki."
},
"title_bar_buttons": {
"window-on-top": "Utrzymuj okno na wierzchu"
@@ -1979,7 +2002,13 @@
"note_detail": {
"could_not_find_typewidget": "Nie można znaleźć widżetu typu dla typu '{{type}}'",
"printing": "Drukowanie w toku...",
"printing_pdf": "Eksportowanie do PDF w toku..."
"printing_pdf": "Eksportowanie do PDF w toku...",
"print_report_title": "Wydrukuj raport",
"print_report_collection_content_one": "Nie można wydrukować {{count}} notatki w kolekcji, ponieważ nie jest ona obsługiwana lub jest chroniona.",
"print_report_collection_content_few": "Nie można wydrukować {{count}} notatek w kolekcji, ponieważ nie są one obsługiwane lub są chronione.",
"print_report_collection_content_many": "Nie można wydrukować {{count}} notatek w kolekcji, ponieważ nie są one obsługiwane lub są chronione.",
"print_report_collection_details_button": "Zobacz szczegóły",
"print_report_collection_details_ignored_notes": "Zignorowane notatki"
},
"note_title": {
"placeholder": "wpisz tytuł notatki tutaj...",
@@ -1989,7 +2018,8 @@
"note_type_switcher_others": "Inny typ notatki",
"note_type_switcher_templates": "Szablon",
"note_type_switcher_collection": "Kolekcja",
"edited_notes": "Edytowane notatki"
"edited_notes": "Notatki edytowane dzisiaj",
"promoted_attributes": "Sugerowane atrybuty"
},
"search_result": {
"no_notes_found": "Nie znaleziono notatek dla podanych parametrów wyszukiwania.",
@@ -1999,7 +2029,11 @@
"configure_launchbar": "Konfiguruj pasek szybkiego dostępu"
},
"sql_result": {
"no_rows": "Dla tego zapytania nie zwrócono żadnych wierszy"
"no_rows": "Dla tego zapytania nie zwrócono żadnych wierszy",
"not_executed": "Zapytanie nie zostało jeszcze wykonane.",
"failed": "Wykonanie zapytania SQL nie powiodło się",
"statement_result": "Wynik wyrażenia",
"execute_now": "Wykonaj teraz"
},
"sql_table_schemas": {
"tables": "Tabele"
@@ -2116,7 +2150,8 @@
"geo-map": {
"create-child-note-title": "Utwórz nową notatkę podrzędną i dodaj ją do mapy",
"create-child-note-instruction": "Kliknij na mapie, aby utworzyć nową notatkę w tej lokalizacji lub naciśnij Escape, aby anulować.",
"unable-to-load-map": "Nie można załadować mapy."
"unable-to-load-map": "Nie można załadować mapy.",
"create-child-note-text": "Dodaj zaznaczenie"
},
"geo-map-context": {
"open-location": "Otwórz lokalizację",
@@ -2183,7 +2218,14 @@
"execute_sql_description": "Ta notatka jest notatką SQL. Kliknij, aby wykonać zapytanie SQL.",
"shared_copy_to_clipboard": "Kopiuj link do schowka",
"shared_open_in_browser": "Otwórz link w przeglądarce",
"shared_unshare": "Usuń udostępnienie"
"shared_unshare": "Usuń udostępnienie",
"save_status_saved": "Zapisane",
"save_status_saving": "Zapisywanie...",
"save_status_unsaved": "Niezapisane",
"save_status_error": "Zapis nie powiódł się",
"save_status_saving_tooltip": "Zmiany zostały zapisane.",
"save_status_unsaved_tooltip": "Są niezapisane zmiany. Zostaną one zapisane automatycznie za chwilę.",
"save_status_error_tooltip": "Wystąpił błąd podczas zapisywania notatki. Spróbuj skopiować treść notatki w inne miejsce i ponownie załadować aplikację."
},
"status_bar": {
"language_title": "Zmień język treści",
@@ -2226,5 +2268,30 @@
"empty_button": "Ukryj panel",
"toggle": "Pokaż/ukryj prawy panel",
"custom_widget_go_to_source": "Przejdź do kodu źródłowego"
},
"pdf": {
"attachments_one": "{{count}} załącznik",
"attachments_few": "{{count}} załączniki",
"attachments_many": "{{count}} załączników",
"layers_one": "{{count}} warstwa",
"layers_few": "{{count}} warstw",
"layers_many": "{{count}} warstw",
"pages_one": "{{count}} strona",
"pages_few": "{{count}} stron",
"pages_many": "{{count}} stron",
"pages_alt": "Strona {{pageNumber}}",
"pages_loading": "Wczytuję..."
},
"platform_indicator": {
"available_on": "Dostępne na {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} zakładka",
"title_few": "{{count}} zakładki",
"title_many": "{{count}} zakładek",
"more_options": "Więcej opcji"
},
"bookmark_buttons": {
"bookmarks": "Zakładki"
}
}

View File

@@ -1068,11 +1068,6 @@
"note_detail_render_help_1": "Esta nota de ajuda é mostrada porque esta nota do tipo Renderizar HTML não possui a relação necessária para funcionar corretamente.",
"note_detail_render_help_2": "O tipo de nota Renderizar HTML é usado para <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">automação</a>. Em suma, tem uma nota de código HTML (opcionalmente com algum JavaScript) e esta nota irá renderizá-la. Para fazê-lo funcionar, deve definir uma <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">relação</a> chamada \"renderNote\" que aponta para a nota HTML a ser renderizada."
},
"web_view": {
"web_view": "Web View",
"embed_websites": "Nota do tipo Visualização Web permite que incorpore sites no Trilium.",
"create_label": "Para começar, crie uma etiqueta com um endereço URL que deseja incorporar, por exemplo, #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Recarregar"
},

View File

@@ -1271,11 +1271,6 @@
"start_dragging_relations": "Comece arrastando as relações daqui e solte-as em outra nota.",
"cannot_match_transform": "Não foi possível combinar a transformação: {{transform}}"
},
"web_view": {
"web_view": "Web View",
"embed_websites": "Nota do tipo Visualização Web permite que você incorpore sites dentro do Trilium.",
"create_label": "Para começar, crie uma etiqueta com um endereço URL que deseja incorporar, por exemplo, #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Recarregar"
},

View File

@@ -1376,11 +1376,6 @@
"enable_vim_keybindings": "Permite utilizarea combinațiilor de taste în stil Vim pentru notițele de tip cod (fără modul ex)",
"use_vim_keybindings_in_code_notes": "Combinații de taste Vim"
},
"web_view": {
"create_label": "Pentru a începe, creați o etichetă cu adresa URL de încorporat, e.g. #webViewSrc=\"https://www.google.com\"",
"embed_websites": "Notițele de tip „Vizualizare web” permit încorporarea site-urilor web în Trilium.",
"web_view": "Vizualizare web"
},
"wrap_lines": {
"enable_line_wrap": "Activează trecerea automată pe rândul următor (poate necesita o reîncărcare a interfeței pentru a avea efect)",
"wrap_lines_in_code_notes": "Trecerea automată pe rândul următor în notițe de cod"
@@ -1761,8 +1756,8 @@
"show-recent-notes": "Afișează notițele recente"
},
"electron_integration": {
"background-effects": "Activează efectele de fundal (doar pentru Windows 11)",
"background-effects-description": "Efectul Mica adaugă un fundal estompat și elegant ferestrelor aplicațiilor, creând profunzime și un aspect modern. Opțiunea „Bară de titlu nativă” trebuie să fie dezactivată.",
"background-effects": "Activează efectele de fundal",
"background-effects-description": "Adaugă un fundal estompat și elegant ferestrelor aplicațiilor, creând profunzime și un aspect modern. Opțiunea „Bară de titlu nativă” trebuie să fie dezactivată.",
"desktop-application": "Aplicația desktop",
"native-title-bar": "Bară de titlu nativă",
"native-title-bar-description": "Pentru Windows și macOS, dezactivarea bării de titlu native face aplicația să pară mai compactă. Pe Linux, păstrarea bării integrează mai bine aplicația cu restul sistemului de operare.",
@@ -1781,7 +1776,8 @@
"geo-map": {
"create-child-note-title": "Crează o notiță nouă și adaug-o pe hartă",
"unable-to-load-map": "Nu s-a putut încărca harta.",
"create-child-note-instruction": "Click pe hartă pentru a crea o nouă notiță la acea poziție sau apăsați Escape pentru a anula."
"create-child-note-instruction": "Click pe hartă pentru a crea o nouă notiță la acea poziție sau apăsați Escape pentru a anula.",
"create-child-note-text": "Adaugă marcaj"
},
"duration": {
"days": "zile",
@@ -2127,7 +2123,7 @@
},
"call_to_action": {
"background_effects_title": "Efectele de fundal sunt acum stabile",
"background_effects_message": "Pe dispozitive cu Windows, efectele de fundal sunt complet stabile. Acestea adaugă un strop de culoare interfeței grafice prin estomparea fundalului din spatele ferestrei. Această tehnică este folosită și în alte aplicații precum Windows Explorer.",
"background_effects_message": "Pe dispozitive cu Windows și macOS, efectele de fundal sunt stabile. Acestea adaugă un strop de culoare interfeței grafice prin estomparea fundalului din spatele ferestrei.",
"background_effects_button": "Activează efectele de fundal",
"next_theme_title": "Încercați noua temă Trilium",
"next_theme_message": "Utilizați tema clasică, doriți să încercați noua temă?",
@@ -2281,5 +2277,14 @@
"pages_other": "{{count}} de pagini",
"pages_alt": "Pagina {{pageNumber}}",
"pages_loading": "Încărcare..."
},
"platform_indicator": {
"available_on": "Disponibil pe {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} tab",
"title_few": "{{count}} taburi",
"title_other": "{{count}} de taburi",
"more_options": "Mai multe opțiuni"
}
}

View File

@@ -668,7 +668,8 @@
"geo-map": {
"unable-to-load-map": "Не удалось загрузить карту.",
"create-child-note-instruction": "Щелкните по карте, чтобы создать новую заметку в этом месте, или нажмите Escape, чтобы закрыть ее.",
"create-child-note-title": "Создать новую дочернюю заметку и добавить ее на карту"
"create-child-note-title": "Создать новую дочернюю заметку и добавить ее на карту",
"create-child-note-text": "Добавить маркер"
},
"note_tooltip": {
"quick-edit": "Быстрое редактирование",
@@ -685,8 +686,8 @@
"electron_integration": {
"zoom-factor": "Коэффициент масштабирования",
"restart-app-button": "Применить изменения и перезапустить приложение",
"background-effects-description": "Эффект Mica добавляет размытый, стильный фон окнам приложений, создавая глубину и современный вид. Опция \"Системная строка заголовка\" должна быть отключена.",
"background-effects": "Включить фоновые эффекты (только Windows 11)",
"background-effects-description": "Добавляет размытый, стильный фон окнам приложений, создавая глубину и современный вид. Опция \"Системная строка заголовка\" должна быть отключена.",
"background-effects": "Включить фоновые эффекты",
"native-title-bar-description": "В Windows и macOS отключение системной строки заголовка делает приложение более компактным. В Linux включение системной строки заголовка улучшает интеграцию с остальной частью системы.",
"native-title-bar": "Системная панель заголовка",
"desktop-application": "Десктопное приложение"
@@ -776,7 +777,11 @@
"refresh-saved-search-results": "Обновить сохраненные результаты поиска",
"automatically-collapse-notes-title": "Заметки будут свернуты после определенного периода бездействия, чтобы навести порядок в дереве.",
"toggle-sidebar": "Переключить боковую панель",
"dropping-not-allowed": "Перетаскивание заметок в эту область не разрешено."
"dropping-not-allowed": "Перетаскивание заметок в эту область не разрешено.",
"shared-indicator-tooltip": "Эта заметка опубликована",
"shared-indicator-tooltip-with-url": "Эта заметка доступно публично по адресу: {{- url}}",
"subtree-hidden-moved-description-other": "В дереве, к которому относится эта заметка, скрыты дочерние заметки.",
"subtree-hidden-moved-description-collection": "Эта коллекция скрывает свои дочерние заметки в дереве."
},
"quick-search": {
"no-results": "Результаты не найдены",
@@ -856,7 +861,10 @@
"convert-to-attachment-confirm": "Вы уверены, что хотите преобразовать выбранные заметки во вложения их родительских заметок? Эта операция применяется только к заметкам в виде изображений; другие заметки будут пропущены.",
"converted-to-attachments": "{{count}} заметок были преобразованы во вложения.",
"archive": "Архивировать",
"unarchive": "Разархивировать"
"unarchive": "Разархивировать",
"open-in-a-new-window": "Открыть в новом окне",
"hide-subtree": "Скрыть поддерево",
"show-subtree": "Показать поддерево"
},
"info": {
"closeButton": "Закрыть",
@@ -1000,7 +1008,8 @@
"switch_to_mobile_version": "Перейти на мобильную версию",
"switch_to_desktop_version": "Переключиться на версию для ПК",
"new-version-available": "Доступно обновление",
"download-update": "Обновить до {{latestVersion}}"
"download-update": "Обновить до {{latestVersion}}",
"search_notes": "Поиск заметок"
},
"zpetne_odkazy": {
"relation": "отношение",
@@ -1047,7 +1056,8 @@
"expand_all_levels": "Развернуть все вложенные уровни",
"expand_nth_level": "Развернуть уровни: {{depth}} шт.",
"expand_first_level": "Развернуть прямые дочерние уровни",
"expand_tooltip": "Разщвернуть дочерние элементы этой коллекции (на один уровень вложенности). Для получения дополнительных параметров нажмите стрелку справа."
"expand_tooltip": "Разщвернуть дочерние элементы этой коллекции (на один уровень вложенности). Для получения дополнительных параметров нажмите стрелку справа.",
"hide_child_notes": "Скрыть дочерние заметки в дереве"
},
"edited_notes": {
"deleted": "(удалено)",
@@ -1692,7 +1702,7 @@
"zoom_in_title": "Увеличить масштаб",
"zoom_out_title": "Уменьшить масштаб",
"reset_pan_zoom_title": "Сбросить панорамирование и масштабирование",
"create_child_note_title": "Создать новую дочернюю заметку и добавить ее в эту карту связей"
"create_child_note_title": "Создать дочернюю заметку и добавить ее в карту"
},
"code_auto_read_only_size": {
"unit": "символов",
@@ -1845,7 +1855,8 @@
"error_cannot_get_branch_id": "Невозможно получить branchId для notePath '{{notePath}}'",
"delete_this_note": "Удалить эту заметку",
"insert_child_note": "Вставить дочернюю заметку",
"note_revisions": "История изменений"
"note_revisions": "История изменений",
"content_language_switcher": "Язык содержимого: {{language}}"
},
"svg_export_button": {
"button_title": "Экспортировать диаграмму как SVG"
@@ -1900,7 +1911,7 @@
"dismiss": "Отклонить",
"background_effects_button": "Включить эффекты фона",
"next_theme_button": "Попробовать новую тему",
"background_effects_message": "На устройствах Windows фоновые эффекты теперь полностью стабильны. Они добавляют цвет в пользовательский интерфейс, размывая фон за ним. Этот приём также используется в других приложениях, например, в проводнике Windows.",
"background_effects_message": "На устройствах с ОС Windows или macOS, фоновые эффекты теперь полностью стабильны. Они добавляют цвета в пользовательский интерфейс, размывая фон за ним.",
"background_effects_title": "Фоновые эффекты теперь стабильны",
"next_theme_title": "Попробуйте новую тему Trilium",
"new_layout_button": "Подробнее",
@@ -1988,11 +1999,6 @@
"attachment_deleted": "Это вложение было удалено.",
"you_can_also_open": ", вы также можете открыть "
},
"web_view": {
"web_view": "Веб-страница",
"create_label": "Для начала создайте метку с URL-адресом, который вы хотите встроить, например, #webViewSrc=\"https://www.google.com\"",
"embed_websites": "Заметки типа \"Веб-страница\" позволяет встраивать веб-сайты в Trilium."
},
"ribbon": {
"widgets": "Виджеты ленты",
"promoted_attributes_message": "Вкладка \"Продвигаемые атрибуты\" будет автоматически открыта, если таковые атрибуты установлены у заметки",
@@ -2094,7 +2100,11 @@
"ui": "Пользовательский интерфейс"
},
"sql_result": {
"no_rows": "По этому запросу не возвращено ни одной строки"
"no_rows": "По этому запросу не возвращено ни одной строки",
"not_executed": "Запрос еще не выполнен.",
"failed": "Выполнение SQL-запроса завершилось с ошибкой",
"statement_result": "Результат заявления",
"execute_now": "Выполнить сейчас"
},
"editable_code": {
"placeholder": "Введите содержимое для заметки с кодом..."
@@ -2189,7 +2199,14 @@
"read_only_auto_description": "Эта заметка была автоматически переведена в режим только для чтения по соображениям производительности. Это автоматическое ограничение можно изменить в настройках.\n\nНажмите, чтобы временно отредактировать её.",
"read_only_auto": "Автоматический режим \"только для чтения\"",
"read_only_explicit_description": "Эта заметка была вручную установлена в режим «только для чтения».\nНажмите, чтобы временно отредактировать её.",
"read_only_explicit": "Только для чтения"
"read_only_explicit": "Только для чтения",
"save_status_saving": "Сохранение...",
"save_status_saved": "Сохранение",
"save_status_unsaved": "Не сохранено",
"save_status_error": "Ошибка сохранения",
"save_status_saving_tooltip": "Изменения сохраняются.",
"save_status_unsaved_tooltip": "Есть несохраненные изменения. Они будут сохранены автоматически через некоторое время.",
"save_status_error_tooltip": "Произошла ошибка при сохранении заметки. Если возможно, попробуйте скопировать содержимое заметки в другое место и перезагрузить приложение."
},
"breadcrumb": {
"hoisted_badge_title": "Снять фокус",
@@ -2243,5 +2260,30 @@
},
"attributes_panel": {
"title": "Атрибуты заметки"
},
"bookmark_buttons": {
"bookmarks": "Закладки"
},
"mobile_tab_switcher": {
"more_options": "Показать больше",
"title_one": "{{count}} вкладка",
"title_few": "{{count}} вкладки",
"title_many": "{{count}} вкладок"
},
"pdf": {
"pages_loading": "Загрузка...",
"pages_alt": "Страница {{pageNumber}}",
"pages_one": "{{count}} страница",
"pages_few": "{{count}} страницы",
"pages_many": "{{count}} страниц",
"layers_one": "{{count}} слой",
"layers_few": "{{count}} слоя",
"layers_many": "{{count}} слоев",
"attachments_one": "{{count}} вложение",
"attachments_few": "{{count}} вложения",
"attachments_many": "{{count}} вложений"
},
"platform_indicator": {
"available_on": "Доступно для {{platform}}"
}
}

View File

@@ -662,7 +662,8 @@
"show-cheatsheet": "顯示快捷鍵說明",
"toggle-zen-mode": "禪模式",
"new-version-available": "發現新更新",
"download-update": "取得版本 {{latestVersion}}"
"download-update": "取得版本 {{latestVersion}}",
"search_notes": "搜尋筆記"
},
"sync_status": {
"unknown": "<p>同步狀態將在下一次同步嘗試開始後顯示。</p><p>點擊以立即觸發同步。</p>",
@@ -757,7 +758,9 @@
"delete_this_note": "刪除此筆記",
"error_cannot_get_branch_id": "無法獲取 notePath '{{notePath}}' 的 branchId",
"error_unrecognized_command": "無法識別的命令 {{command}}",
"note_revisions": "筆記歷史版本"
"note_revisions": "筆記歷史版本",
"backlinks": "反向連結",
"content_language_switcher": "內文語言:{{language}}"
},
"note_icon": {
"change_note_icon": "更改筆記圖標",
@@ -909,7 +912,8 @@
"unknown_search_option": "未知的搜尋選項 {{searchOptionName}}",
"search_note_saved": "搜尋筆記已儲存至 {{- notePathTitle}}",
"actions_executed": "已執行操作。",
"view_options": "查看選項:"
"view_options": "查看選項:",
"option": "選項"
},
"similar_notes": {
"title": "相似筆記",
@@ -1063,11 +1067,6 @@
"note_detail_render_help_1": "之所以顯示此說明筆記,是因為該類型的渲染 HTML 沒有設定好必須的關聯。",
"note_detail_render_help_2": "渲染筆記類型用於編寫 <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">腳本</a>。簡單說就是您可以寫HTML程式碼或者加上一些JavaScript程式碼 然後這個筆記會把頁面渲染出來。要使其正常工作,您需要定義一個名為 \"renderNote\" 的 <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">關聯</a> 指向要呈現的 HTML 筆記。"
},
"web_view": {
"web_view": "網頁顯示",
"embed_websites": "網頁顯示類型的筆記允許您將網站嵌入至 Trilium 中。",
"create_label": "首先,請新增一個帶有您要嵌入的 URL 地址的標籤,例如 #webViewSrc=\"https://www.bing.com\""
},
"backend_log": {
"refresh": "重新整理"
},
@@ -1939,8 +1938,8 @@
"desktop-application": "桌面版應用程式",
"native-title-bar": "原生標題列",
"native-title-bar-description": "對於 Windows 和 macOS關閉原生標題列會讓應用程式看起來更緊湊。在 Linux 上,開啟原生標題列可以與系統的其他部分整合得更好。",
"background-effects": "啟用背景效果(僅適用於 Windows 11",
"background-effects-description": "Mica 效果為程式視窗新增模糊且時尚的背景,營造出深度感和現代化外觀。「原生標題列」必須被禁用。",
"background-effects": "啟用背景效果",
"background-effects-description": "為程式視窗新增模糊且時尚的背景,營造立體感和現代化外觀。「原生標題列」必須被禁用。",
"restart-app-button": "重新啟動應用程式以查看更改",
"zoom-factor": "縮放係數"
},
@@ -1959,7 +1958,8 @@
"geo-map": {
"create-child-note-title": "建立一個新的子筆記並將其加至地圖中",
"create-child-note-instruction": "點擊地圖以在該位置建立新筆記,或按 Escape 以取消。",
"unable-to-load-map": "無法載入地圖。"
"unable-to-load-map": "無法載入地圖。",
"create-child-note-text": "新增地圖標記"
},
"geo-map-context": {
"open-location": "打開位置",
@@ -2122,7 +2122,7 @@
},
"call_to_action": {
"background_effects_title": "背景特效已推出穩定版本",
"background_effects_message": "在 Windows 裝置上,背景特效現在已完全穩定。背景特效透過模糊背後的背景,為使用者介面增添一抹色彩。此技術也用於其他應用程式,例如 Windows 檔案總管。",
"background_effects_message": "在 Windows 和macOS裝置上,背景特效現在已穩定。背景特效透過模糊背後的背景,為使用者介面增添一抹色彩。",
"background_effects_button": "啟用背景特效",
"next_theme_title": "試用新 Trilium 主題",
"next_theme_message": "您正在使用舊版主題,要試用新主題嗎?",
@@ -2267,5 +2267,16 @@
"pages_other": "",
"pages_alt": "第 {{pageNumber}} 頁",
"pages_loading": "正在載入…"
},
"mobile_tab_switcher": {
"more_options": "更多選項",
"title_one": "{{count}} 個分頁",
"title_other": ""
},
"platform_indicator": {
"available_on": "可於 {{platform}} 使用"
},
"bookmark_buttons": {
"bookmarks": "書籤"
}
}

View File

@@ -1134,11 +1134,6 @@
"note_detail_render_help_1": "Ця довідка відображається, оскільки ця нотатка типу Render HTML не має необхідного зв'язку для належного функціонування.",
"note_detail_render_help_2": "Тип нотатки Render HTML використовується для <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/scripts.html\">скриптів</a>. Коротше кажучи, у вас є нотатка з HTML-кодом (за бажанням з деяким JavaScript), і ця нотатка її відобразить. Щоб це запрацювало, вам потрібно визначити <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/attributes.html\">відношення</a> під назвою \"renderNote\", яке вказує на нотатку HTML для відображення."
},
"web_view": {
"web_view": "Веб-перегляд",
"embed_websites": "Нотатка типу Веб-перегляд дозволяє вбудовувати веб-сайти в Trilium.",
"create_label": "Для початку створіть мітку з URL-адресою, яку ви хочете вбудувати, наприклад, #webViewSrc=\"https://www.google.com\""
},
"backend_log": {
"refresh": "Оновити"
},

View File

@@ -0,0 +1,83 @@
.tn-backlinks-widget .backlinks-items {
list-style-type: none;
margin: 0;
padding: 0;
position: static;
width: unset;
> li {
--border-radius: 8px;
max-width: 600px;
padding: 10px 20px;
background: var(--card-background-color);
& + li {
margin-top: 2px;
}
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
/* Card header */
& > span:first-child {
display: block;
> span {
display: flex;
flex-wrap: wrap;
align-items: center;
/* Note path */
> small {
flex: 100%;
order: -1;
font-size: .65rem;
.note-path {
padding: 0;
}
}
/* Note icon */
> .tn-icon {
color: var(--menu-item-icon-color);
}
/* Note title */
> a {
margin-inline-start: 4px;
color: currentColor;
font-weight: 500;
}
}
}
/* Card content - excerpt */
.backlink-excerpt {
all: unset; /* TODO: Remove after disposing the old style from FloatingButtons.css */
display: block;
margin: 8px 0;
border-radius: 4px;
background: var(--quick-search-result-content-background);
padding: 8px;
font-size: .75rem;
a {
background: transparent;
color: var(--quick-search-result-highlight-color);
text-decoration: underline;
}
p {
margin: 0;
}
}
}
}

View File

@@ -1,3 +1,5 @@
import "./Backlinks.css";
import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons";
import { VNode } from "preact";
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
@@ -60,14 +62,6 @@ export const DESKTOP_FLOATING_BUTTONS: FloatingButtonsList = [
Backlinks
];
export const MOBILE_FLOATING_BUTTONS: FloatingButtonsList = [
RefreshBackendLogButton,
EditButton,
RelationMapButtons,
ExportImageButtons,
Backlinks
];
/**
* Floating buttons that should be hidden in popup editor (Quick edit).
*/

View File

@@ -3,6 +3,30 @@
font-family: var(--detail-font-family);
font-size: var(--detail-font-size);
contain: none;
&.fixed-tree {
display: flex;
flex-direction: column;
height: 100%;
.fixed-note-tree-container {
height: 60%;
border-bottom: 1px solid var(--main-border-color);
overflow: auto;
.tree-wrapper {
padding: 0;
}
.tree {
padding: 0;
}
ul {
margin: 0;
}
}
}
}
body.prefers-centered-content .note-detail {
@@ -12,4 +36,4 @@ body.prefers-centered-content .note-detail {
.note-detail > * {
contain: none;
}
}

View File

@@ -1,5 +1,6 @@
import "./NoteDetail.css";
import clsx from "clsx";
import { isValidElement, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
@@ -12,8 +13,9 @@ import { t } from "../services/i18n";
import protected_session_holder from "../services/protected_session_holder";
import toast from "../services/toast.js";
import { dynamicRequire, isElectron, isMobile } from "../services/utils";
import NoteTreeWidget from "./note_tree";
import { ExtendedNoteType, TYPE_MAPPINGS, TypeWidget } from "./note_types";
import { useNoteContext, useTriliumEvent } from "./react/hooks";
import { useLegacyWidget, useNoteContext, useTriliumEvent } from "./react/hooks";
import { NoteListWithLinks } from "./react/NoteList";
import { TypeWidgetProps } from "./type_widgets/type_widget";
@@ -36,6 +38,7 @@ export default function NoteDetail() {
const [ noteTypesToRender, setNoteTypesToRender ] = useState<{ [ key in ExtendedNoteType ]?: (props: TypeWidgetProps) => VNode }>({});
const [ activeNoteType, setActiveNoteType ] = useState<ExtendedNoteType>();
const widgetRequestId = useRef(0);
const hasFixedTree = note && noteContext?.hoistedNoteId === "_lbMobileRoot" && isMobile() && note.noteId.startsWith("_lbMobile");
const props: TypeWidgetProps = {
note: note!,
@@ -119,13 +122,6 @@ export default function NoteDetail() {
}
});
// Fixed tree for launch bar config on mobile.
useEffect(() => {
if (!isMobile) return;
const hasFixedTree = noteContext?.hoistedNoteId === "_lbMobileRoot";
document.body.classList.toggle("force-fixed-tree", hasFixedTree);
}, [ note ]);
// Handle toast notifications.
useEffect(() => {
if (!isElectron()) return;
@@ -215,8 +211,13 @@ export default function NoteDetail() {
return (
<div
ref={containerRef}
class={`component note-detail ${isFullHeight ? "full-height" : ""}`}
class={clsx("component note-detail", {
"full-height": isFullHeight,
"fixed-tree": hasFixedTree
})}
>
{hasFixedTree && <FixedTree noteContext={noteContext} />}
{Object.entries(noteTypesToRender).map(([ itemType, Element ]) => {
return <NoteDetailWrapper
Element={Element}
@@ -231,6 +232,11 @@ export default function NoteDetail() {
);
}
function FixedTree({ noteContext }: { noteContext: NoteContext }) {
const [ treeEl ] = useLegacyWidget(() => new NoteTreeWidget(), { noteContext });
return <div class="fixed-note-tree-container">{treeEl}</div>;
}
/**
* Wraps a single note type widget, in order to keep it in the DOM even after the user has switched away to another note type. This allows faster loading of the same note type again. The properties are cached, so that they are updated only
* while the widget is visible, to avoid rendering in the background. When not visible, the DOM element is simply hidden.
@@ -364,7 +370,33 @@ function showToast(type: "printing" | "exporting_pdf", progress: number = 0) {
}
function handlePrintReport(printReport?: PrintReport) {
if (printReport?.type === "collection" && printReport.ignoredNoteIds.length > 0) {
if (!printReport) return;
if (printReport.type === "error") {
toast.showPersistent({
id: "print-error",
icon: "bx bx-error-circle",
title: t("note_detail.print_report_error_title"),
message: printReport.message,
buttons: printReport.stack ? [
{
text: t("note_detail.print_report_collection_details_button"),
onClick(api) {
api.dismissToast();
dialog.info(<>
<p>{printReport.message}</p>
<details>
<summary>{t("note_detail.print_report_stack_trace")}</summary>
<pre style="font-size: 0.85em; overflow-x: auto;">{printReport.stack}</pre>
</details>
</>, {
title: t("note_detail.print_report_error_title")
});
}
}
] : undefined
});
} else if (printReport.type === "collection" && printReport.ignoredNoteIds.length > 0) {
toast.showPersistent({
id: "print-report",
icon: "bx bx-collection",

View File

@@ -5,6 +5,7 @@ import clsx from "clsx";
import { ComponentChild, HTMLInputTypeAttribute, InputHTMLAttributes, MouseEventHandler, TargetedEvent, TargetedInputEvent } from "preact";
import { Dispatch, StateUpdater, useEffect, useRef, useState } from "preact/hooks";
import NoteContext from "../components/note_context";
import FAttribute from "../entities/fattribute";
import FNote from "../entities/fnote";
import { Attribute } from "../services/attribute_parser";
@@ -40,8 +41,8 @@ type OnChangeEventData = TargetedEvent<HTMLInputElement, Event> | InputEvent | J
type OnChangeListener = (e: OnChangeEventData) => Promise<void>;
export default function PromotedAttributes() {
const { note, componentId } = useNoteContext();
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
const { note, componentId, noteContext } = useNoteContext();
const [ cells, setCells ] = usePromotedAttributeData(note, componentId, noteContext);
return <PromotedAttributesContent note={note} componentId={componentId} cells={cells} setCells={setCells} />;
}
@@ -74,12 +75,12 @@ export function PromotedAttributesContent({ note, componentId, cells, setCells }
*
* The cells are returned as a state since they can also be altered internally if needed, for example to add a new empty cell.
*/
export function usePromotedAttributeData(note: FNote | null | undefined, componentId: string): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
export function usePromotedAttributeData(note: FNote | null | undefined, componentId: string, noteContext: NoteContext | undefined): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
const [ viewType ] = useNoteLabel(note, "viewType");
const [ cells, setCells ] = useState<Cell[]>();
function refresh() {
if (!note || viewType === "table") {
if (!note || viewType === "table" || noteContext?.viewScope?.viewMode !== "default") {
setCells([]);
return;
}
@@ -124,7 +125,7 @@ export function usePromotedAttributeData(note: FNote | null | undefined, compone
setCells(cells);
}
useEffect(refresh, [ note, viewType ]);
useEffect(refresh, [ note, viewType, noteContext ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows(componentId).find((attr) => attributes.isAffecting(attr, note))) {
refresh();

View File

@@ -29,7 +29,6 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
const isVerticalLayout = !isHorizontalLayout;
const parentComponent = useContext(ParentComponent);
const { isUpdateAvailable, latestVersion } = useTriliumUpdateStatus();
const isMobileLocal = isMobile();
const logoRef = useRef<SVGSVGElement>(null);
useStaticTooltip(logoRef);
@@ -44,9 +43,12 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
</div>}
</>}
noDropdownListStyle
onShown={isMobileLocal ? () => document.getElementById("context-menu-cover")?.classList.add("show", "global-menu-cover") : undefined}
onHidden={isMobileLocal ? () => document.getElementById("context-menu-cover")?.classList.remove("show", "global-menu-cover") : undefined}
mobileBackdrop
>
{isMobile() && <>
<MenuItem command="searchNotes" icon="bx bx-search" text={t("global_menu.search_notes")} />
<FormDropdownDivider />
</>}
<MenuItem command="openNewWindow" icon="bx bx-window-open" text={t("global_menu.open_new_window")} />
<MenuItem command="showShareSubtree" icon="bx bx-share-alt" text={t("global_menu.show_shared_notes_subtree")} />
@@ -107,8 +109,7 @@ function BrowserOnlyOptions() {
function DevelopmentOptions({ dropStart }: { dropStart: boolean }) {
return <>
<FormDropdownDivider />
<FormListItem disabled>Development Options</FormListItem>
<FormListHeader text="Development Options" />
<FormDropdownSubmenu icon="bx bx-test-tube" title="Experimental features" dropStart={dropStart}>
{experimentalFeatures.map((feature) => (
<ExperimentalFeatureToggle key={feature.id} experimentalFeature={feature as ExperimentalFeature} />

View File

@@ -10,12 +10,6 @@
--card-padding: 0.6em;
}
body.mobile .board-view {
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
}
.board-view-container {
height: 100%;
display: flex;
@@ -26,6 +20,12 @@ body.mobile .board-view {
overflow-x: auto;
}
body.mobile .board-view-container {
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
}
.board-view-container .board-column {
width: 250px;
flex-shrink: 0;

View File

@@ -79,6 +79,7 @@ export const LOCALE_MAPPINGS: Record<DISPLAYABLE_LOCALE_IDS, (() => Promise<{ de
es: () => import("@fullcalendar/core/locales/es"),
fr: () => import("@fullcalendar/core/locales/fr"),
it: () => import("@fullcalendar/core/locales/it"),
ga: null,
cn: () => import("@fullcalendar/core/locales/zh-cn"),
tw: () => import("@fullcalendar/core/locales/zh-tw"),
ro: () => import("@fullcalendar/core/locales/ro"),

View File

@@ -7,7 +7,7 @@
> .collection-properties {
position: relative;
z-index: 2000;
z-index: 998;
}
}
@@ -22,7 +22,7 @@
.leaflet-top,
.leaflet-bottom {
z-index: 997;
z-index: 997 !important;
}
.geo-view.placing-note .geo-map-container {

View File

@@ -1,11 +1,12 @@
import { EventData } from "../../components/app_context.js";
import { LOCALES } from "@triliumnext/commons";
import { readCssVar } from "../../utils/css-var.js";
import FlexContainer from "./flex_container.js";
import options from "../../services/options.js";
import type BasicWidget from "../basic_widget.js";
import utils from "../../services/utils.js";
import { EventData } from "../../components/app_context.js";
import { getEnabledExperimentalFeatureIds } from "../../services/experimental_features.js";
import options from "../../services/options.js";
import utils, { isMobile } from "../../services/utils.js";
import { readCssVar } from "../../utils/css-var.js";
import type BasicWidget from "../basic_widget.js";
import FlexContainer from "./flex_container.js";
/**
* The root container is the top-most widget/container, from which the entire layout derives.
@@ -17,14 +18,12 @@ import { getEnabledExperimentalFeatureIds } from "../../services/experimental_fe
* - `#root-container.vertical-layout`, if the current layout is horizontal.
*/
export default class RootContainer extends FlexContainer<BasicWidget> {
private originalViewportHeight: number;
constructor(isHorizontalLayout: boolean) {
super(isHorizontalLayout ? "column" : "row");
this.id("root-widget");
this.css("height", "100dvh");
this.originalViewportHeight = getViewportHeight();
}
render(): JQuery<HTMLElement> {
@@ -39,6 +38,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
this.#setThemeCapabilities();
this.#setLocaleAndDirection(options.get("locale"));
this.#setExperimentalFeatures();
this.#initPWATopbarColor();
return super.render();
}
@@ -64,8 +64,12 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
}
#onMobileResize() {
const currentViewportHeight = getViewportHeight();
const isKeyboardOpened = (currentViewportHeight < this.originalViewportHeight);
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
const windowHeight = window.innerHeight;
// If viewport is significantly smaller, keyboard is likely open
const isKeyboardOpened = windowHeight - viewportHeight > 150;
this.$widget.toggleClass("virtual-keyboard-opened", isKeyboardOpened);
}
@@ -88,7 +92,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
}
#setBackdropEffects() {
const enabled = options.is("backdropEffectsEnabled");
const enabled = options.is("backdropEffectsEnabled") && !isMobile();
document.body.classList.toggle("backdrop-effects-disabled", !enabled);
}
@@ -96,7 +100,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
// Supports background effects
const useBgfx = readCssVar(document.documentElement, "allow-background-effects")
.asBoolean(false);
.asBoolean(false);
document.body.classList.toggle("theme-supports-background-effects", useBgfx);
}
@@ -112,8 +116,23 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
document.body.lang = locale;
document.body.dir = correspondingLocale?.rtl ? "rtl" : "ltr";
}
#initPWATopbarColor() {
if (!utils.isPWA()) return;
const tracker = $("#background-color-tracker");
if (tracker.length) {
const applyThemeColor = () => {
let meta = $("meta[name='theme-color']");
if (!meta.length) {
meta = $(`<meta name="theme-color">`).appendTo($("head"));
}
meta.attr("content", tracker.css("color"));
};
tracker.on("transitionend", applyThemeColor);
applyThemeColor();
}
}
}
function getViewportHeight() {
return window.visualViewport?.height ?? window.innerHeight;
}

View File

@@ -1,14 +1,22 @@
.scrolling-container {
--content-margin-inline: 24px;
overflow: auto;
scroll-behavior: smooth;
position: relative;
> .inline-title,
> .note-detail > .note-detail-editable-text,
> .note-detail > .note-detail-editable-text > .note-detail-editable-text-editor,
> .note-list-widget:not(.full-height) .note-list-wrapper {
padding-inline: 24px;
margin-inline: var(--content-margin-inline);
}
> .inline-title {
padding-inline: var(--content-margin-inline);
}
> .note-detail > .note-detail-editable-text > .note-detail-editable-text-editor {
overflow: unset;
}
}
.note-split.type-code:not(.mime-text-x-sqlite) {

View File

@@ -91,8 +91,9 @@ body.mobile .modal.popup-editor-dialog .modal-dialog {
height: 100%;
}
.modal.popup-editor-dialog .note-detail-editable-text {
padding: 0 1em;
.modal.popup-editor-dialog .note-detail-editable-text-editor {
margin: 0 28px;
overflow: visible; /* Allow selection rectangle to go outside of the editor area */
}
.modal.popup-editor-dialog .note-detail-file {

View File

@@ -5,13 +5,15 @@ import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import appContext from "../../components/app_context";
import NoteContext from "../../components/note_context";
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
import froca from "../../services/froca";
import { t } from "../../services/i18n";
import tree from "../../services/tree";
import utils from "../../services/utils";
import NoteList from "../collections/NoteList";
import FloatingButtons from "../FloatingButtons";
import { DESKTOP_FLOATING_BUTTONS, MOBILE_FLOATING_BUTTONS, POPUP_HIDDEN_FLOATING_BUTTONS } from "../FloatingButtonsDefinitions";
import { DESKTOP_FLOATING_BUTTONS, POPUP_HIDDEN_FLOATING_BUTTONS } from "../FloatingButtonsDefinitions";
import NoteBadges from "../layout/NoteBadges";
import NoteIcon from "../note_icon";
import NoteTitleWidget from "../note_title";
import NoteDetail from "../NoteDetail";
@@ -23,8 +25,6 @@ import ReadOnlyNoteInfoBar from "../ReadOnlyNoteInfoBar";
import StandaloneRibbonAdapter from "../ribbon/components/StandaloneRibbonAdapter";
import FormattingToolbar from "../ribbon/FormattingToolbar";
import MobileEditorToolbar from "../type_widgets/text/mobile_editor_toolbar";
import NoteBadges from "../layout/NoteBadges";
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
@@ -34,7 +34,7 @@ export default function PopupEditor() {
const [ noteContext, setNoteContext ] = useState(new NoteContext("_popup-editor"));
const isMobile = utils.isMobile();
const items = useMemo(() => {
const baseItems = isMobile ? MOBILE_FLOATING_BUTTONS : DESKTOP_FLOATING_BUTTONS;
const baseItems = isMobile ? [] : DESKTOP_FLOATING_BUTTONS;
return baseItems.filter(item => !POPUP_HIDDEN_FLOATING_BUTTONS.includes(item));
}, [ isMobile ]);

View File

@@ -1,11 +1,16 @@
import { useContext, useMemo } from "preact/hooks";
import { LaunchBarContext, LaunchBarDropdownButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
import { CSSProperties } from "preact";
import type FNote from "../../entities/fnote";
import { useChildNotes, useNoteLabelBoolean } from "../react/hooks";
import "./BookmarkButtons.css";
import { CSSProperties } from "preact";
import { useContext, useMemo } from "preact/hooks";
import type FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
import { FormDropdownSubmenu, FormListItem } from "../react/FormList";
import { useChildNotes, useNote, useNoteIcon, useNoteLabelBoolean } from "../react/hooks";
import NoteLink from "../react/NoteLink";
import { CustomNoteLauncher } from "./GenericButtons";
import ResponsiveContainer from "../react/ResponsiveContainer";
import { CustomNoteLauncher, launchCustomNoteLauncher } from "./GenericButtons";
import { LaunchBarContext, LaunchBarDropdownButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
const PARENT_NOTE_ID = "_lbBookmarks";
@@ -19,17 +24,64 @@ export default function BookmarkButtons() {
const childNotes = useChildNotes(PARENT_NOTE_ID);
return (
<div style={style}>
{childNotes?.map(childNote => <SingleBookmark key={childNote.noteId} note={childNote} />)}
</div>
)
<ResponsiveContainer
desktop={
<div style={style}>
{childNotes?.map(childNote => <SingleBookmark key={childNote.noteId} note={childNote} />)}
</div>
}
mobile={
<LaunchBarDropdownButton
icon="bx bx-bookmark"
title={t("bookmark_buttons.bookmarks")}
>
{childNotes?.map(childNote => <SingleBookmark key={childNote.noteId} note={childNote} />)}
</LaunchBarDropdownButton>
}
/>
);
}
function SingleBookmark({ note }: { note: FNote }) {
const [ bookmarkFolder ] = useNoteLabelBoolean(note, "bookmarkFolder");
return bookmarkFolder
? <BookmarkFolder note={note} />
: <CustomNoteLauncher launcherNote={note} getTargetNoteId={() => note.noteId} />
return <ResponsiveContainer
desktop={
bookmarkFolder
? <BookmarkFolder note={note} />
: <CustomNoteLauncher launcherNote={note} getTargetNoteId={() => note.noteId} />
}
mobile={<MobileBookmarkItem noteId={note.noteId} bookmarkFolder={bookmarkFolder} />}
/>;
}
function MobileBookmarkItem({ noteId, bookmarkFolder }: { noteId: string, bookmarkFolder: boolean }) {
const note = useNote(noteId);
const noteIcon = useNoteIcon(note);
if (!note) return null;
return (
!bookmarkFolder
? <FormListItem icon={noteIcon} onClick={(e) => launchCustomNoteLauncher(e, { launcherNote: note, getTargetNoteId: () => note.noteId })}>{note.title}</FormListItem>
: <MobileBookmarkFolder note={note} />
);
}
function MobileBookmarkFolder({ note }: { note: FNote }) {
const childNotes = useChildNotes(note.noteId);
return (
<FormDropdownSubmenu icon="bx bx-folder" title={note.title}>
{childNotes.map(childNote => (
<FormListItem
key={childNote.noteId}
icon={childNote.getIcon()}
onClick={(e) => launchCustomNoteLauncher(e, { launcherNote: childNote, getTargetNoteId: () => childNote.noteId })}
>
{childNote.title}
</FormListItem>
))}
</FormDropdownSubmenu>
);
}
function BookmarkFolder({ note }: { note: FNote }) {
@@ -55,5 +107,5 @@ function BookmarkFolder({ note }: { note: FNote }) {
</ul>
</div>
</LaunchBarDropdownButton>
)
);
}

View File

@@ -3,7 +3,7 @@ import clsx from "clsx";
import server from "../../services/server";
import { TargetedMouseEvent, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Dayjs, getWeekInfo, WeekSettings } from "@triliumnext/commons";
import { Dayjs } from "@triliumnext/commons";
import { t } from "../../services/i18n";
interface DateNotesForMonth {
@@ -22,7 +22,6 @@ const DAYS_OF_WEEK = [
interface DateRangeInfo {
weekNumbers: number[];
weekYears: number[];
dates: Dayjs[];
}
@@ -37,27 +36,19 @@ export interface CalendarArgs {
export default function Calendar(args: CalendarArgs) {
const [ rawFirstDayOfWeek ] = useTriliumOptionInt("firstDayOfWeek");
const [ firstWeekOfYear ] = useTriliumOptionInt("firstWeekOfYear");
const [ minDaysInFirstWeek ] = useTriliumOptionInt("minDaysInFirstWeek");
const firstDayOfWeekISO = (rawFirstDayOfWeek === 0 ? 7 : rawFirstDayOfWeek);
const weekSettings = {
firstDayOfWeek: firstDayOfWeekISO,
firstWeekOfYear: firstWeekOfYear ?? 0,
minDaysInFirstWeek: minDaysInFirstWeek ?? 4
};
const date = args.date;
const firstDay = date.startOf('month');
const firstDayISO = firstDay.isoWeekday();
const monthInfo = getMonthInformation(date, firstDayISO, weekSettings);
const monthInfo = getMonthInformation(date, firstDayISO, firstDayOfWeekISO);
return (
<>
<CalendarWeekHeader rawFirstDayOfWeek={rawFirstDayOfWeek} />
<div className="calendar-body" data-calendar-area="month">
{firstDayISO !== firstDayOfWeekISO && <PreviousMonthDays info={monthInfo.prevMonth} weekSettings={weekSettings} {...args} />}
<CurrentMonthDays weekSettings={weekSettings} {...args} />
{firstDayISO !== firstDayOfWeekISO && <PreviousMonthDays info={monthInfo.prevMonth} {...args} />}
<CurrentMonthDays firstDayOfWeekISO={firstDayOfWeekISO} {...args} />
<NextMonthDays dates={monthInfo.nextMonth.dates} {...args} />
</div>
</>
@@ -76,7 +67,7 @@ function CalendarWeekHeader({ rawFirstDayOfWeek }: { rawFirstDayOfWeek: number }
)
}
function PreviousMonthDays({ date, info: { dates, weekNumbers, weekYears }, weekSettings, ...args }: { date: Dayjs, info: DateRangeInfo, weekSettings: WeekSettings } & CalendarArgs) {
function PreviousMonthDays({ date, info: { dates, weekNumbers }, ...args }: { date: Dayjs, info: DateRangeInfo } & CalendarArgs) {
const prevMonth = date.subtract(1, 'month').format('YYYY-MM');
const [ dateNotesForPrevMonth, setDateNotesForPrevMonth ] = useState<DateNotesForMonth>();
@@ -86,28 +77,27 @@ function PreviousMonthDays({ date, info: { dates, weekNumbers, weekYears }, week
return (
<>
<CalendarWeek date={date} weekNumber={weekNumbers[0]} weekYear={weekYears[0]} {...args} />
<CalendarWeek date={date} weekNumber={weekNumbers[0]} {...args} />
{dates.map(date => <CalendarDay key={date.toISOString()} date={date} dateNotesForMonth={dateNotesForPrevMonth} className="calendar-date-prev-month" {...args} />)}
</>
)
}
function CurrentMonthDays({ date, weekSettings, ...args }: { date: Dayjs, weekSettings: WeekSettings } & CalendarArgs) {
function CurrentMonthDays({ date, firstDayOfWeekISO, ...args }: { date: Dayjs, firstDayOfWeekISO: number } & CalendarArgs) {
let dateCursor = date;
const currentMonth = date.month();
const items: VNode[] = [];
const curMonthString = date.format('YYYY-MM');
const [ dateNotesForCurMonth, setDateNotesForCurMonth ] = useState<DateNotesForMonth>();
const { firstDayOfWeek, firstWeekOfYear, minDaysInFirstWeek } = weekSettings;
useEffect(() => {
server.get<DateNotesForMonth>(`special-notes/notes-for-month/${curMonthString}`).then(setDateNotesForCurMonth);
}, [ date ]);
while (dateCursor.month() === currentMonth) {
const { weekYear, weekNumber } = getWeekInfo(dateCursor, weekSettings);
if (dateCursor.isoWeekday() === firstDayOfWeek) {
items.push(<CalendarWeek key={`${weekYear}-W${weekNumber}`} date={dateCursor} weekNumber={weekNumber} weekYear={weekYear} {...args}/>)
const weekNumber = getWeekNumber(dateCursor, firstDayOfWeekISO);
if (dateCursor.isoWeekday() === firstDayOfWeekISO) {
items.push(<CalendarWeek key={`${dateCursor.year()}-W${weekNumber}`} date={dateCursor} weekNumber={weekNumber} {...args}/>)
}
items.push(<CalendarDay key={dateCursor.toISOString()} date={dateCursor} dateNotesForMonth={dateNotesForCurMonth} {...args} />)
@@ -151,8 +141,14 @@ function CalendarDay({ date, dateNotesForMonth, className, activeDate, todaysDat
);
}
function CalendarWeek({ date, weekNumber, weekYear, weekNotes, onWeekClicked }: { weekNumber: number, weekYear: number, weekNotes: string[] } & Pick<CalendarArgs, "date" | "onWeekClicked">) {
const weekString = `${weekYear}-W${String(weekNumber).padStart(2, '0')}`;
function CalendarWeek({ date, weekNumber, weekNotes, onWeekClicked }: { weekNumber: number, weekNotes: string[] } & Pick<CalendarArgs, "date" | "onWeekClicked">) {
const localDate = date.local();
// Handle case where week is in between years.
let year = localDate.year();
if (localDate.month() === 11 && weekNumber === 1) year++;
const weekString = `${year}-W${String(weekNumber).padStart(2, '0')}`;
if (onWeekClicked) {
return (
@@ -173,33 +169,33 @@ function CalendarWeek({ date, weekNumber, weekYear, weekNotes, onWeekClicked }:
>{weekNumber}</span>);
}
export function getMonthInformation(date: Dayjs, firstDayISO: number, weekSettings: WeekSettings) {
export function getMonthInformation(date: Dayjs, firstDayISO: number, firstDayOfWeekISO: number) {
return {
prevMonth: getPrevMonthDays(date, firstDayISO, weekSettings),
nextMonth: getNextMonthDays(date, weekSettings.firstDayOfWeek)
prevMonth: getPrevMonthDays(date, firstDayISO, firstDayOfWeekISO),
nextMonth: getNextMonthDays(date, firstDayOfWeekISO)
}
}
function getPrevMonthDays(date: Dayjs, firstDayISO: number, weekSettings: WeekSettings): DateRangeInfo {
function getPrevMonthDays(date: Dayjs, firstDayISO: number, firstDayOfWeekISO: number): DateRangeInfo {
const prevMonthLastDay = date.subtract(1, 'month').endOf('month');
const daysToAdd = (firstDayISO - weekSettings.firstDayOfWeek + 7) % 7;
const daysToAdd = (firstDayISO - firstDayOfWeekISO + 7) % 7;
const dates: Dayjs[] = [];
const firstDay = date.startOf('month');
const { weekYear, weekNumber } = getWeekInfo(firstDay, weekSettings);
const weekNumber = getWeekNumber(firstDay, firstDayOfWeekISO);
// Get dates from previous month
for (let i = daysToAdd - 1; i >= 0; i--) {
dates.push(prevMonthLastDay.subtract(i, 'day'));
}
return { weekNumbers: [ weekNumber ], weekYears: [ weekYear ], dates };
return { weekNumbers: [ weekNumber ], dates };
}
function getNextMonthDays(date: Dayjs, firstDayOfWeek: number): DateRangeInfo {
function getNextMonthDays(date: Dayjs, firstDayOfWeekISO: number): DateRangeInfo {
const lastDayOfMonth = date.endOf('month');
const lastDayISO = lastDayOfMonth.isoWeekday();
const lastDayOfUserWeek = ((firstDayOfWeek + 6 - 1) % 7) + 1;
const lastDayOfUserWeek = ((firstDayOfWeekISO + 6 - 1) % 7) + 1;
const nextMonthFirstDay = date.add(1, 'month').startOf('month');
const dates: Dayjs[] = [];
@@ -210,5 +206,16 @@ function getNextMonthDays(date: Dayjs, firstDayOfWeek: number): DateRangeInfo {
dates.push(nextMonthFirstDay.add(i, 'day'));
}
}
return { weekNumbers: [], weekYears: [], dates };
return { weekNumbers: [], dates };
}
export function getWeekNumber(date: Dayjs, firstDayOfWeekISO: number): number {
const weekStart = getWeekStartDate(date, firstDayOfWeekISO);
return weekStart.isoWeek();
}
function getWeekStartDate(date: Dayjs, firstDayOfWeekISO: number): Dayjs {
const currentISO = date.isoWeekday();
const diff = (currentISO - firstDayOfWeekISO + 7) % 7;
return date.clone().subtract(diff, "day").startOf("day");
}

View File

@@ -7,32 +7,18 @@ import { isCtrlKey } from "../../services/utils";
import { useGlobalShortcut, useNoteLabel } from "../react/hooks";
import { LaunchBarActionButton, useLauncherIconAndTitle } from "./launch_bar_widgets";
export function CustomNoteLauncher({ launcherNote, getTargetNoteId, getHoistedNoteId }: {
export function CustomNoteLauncher(props: {
launcherNote: FNote;
getTargetNoteId: (launcherNote: FNote) => string | null | Promise<string | null>;
getHoistedNoteId?: (launcherNote: FNote) => string | null;
keyboardShortcut?: string;
}) {
const { launcherNote, getTargetNoteId } = props;
const { icon, title } = useLauncherIconAndTitle(launcherNote);
const launch = useCallback(async (evt: MouseEvent | KeyboardEvent) => {
if (evt.which === 3) {
return;
}
const targetNoteId = await getTargetNoteId(launcherNote);
if (!targetNoteId) return;
const hoistedNoteIdWithDefault = getHoistedNoteId?.(launcherNote) || appContext.tabManager.getActiveContext()?.hoistedNoteId;
const ctrlKey = isCtrlKey(evt);
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
const activate = !!evt.shiftKey;
await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteIdWithDefault, activate);
} else {
await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteIdWithDefault);
}
}, [ launcherNote, getTargetNoteId, getHoistedNoteId ]);
await launchCustomNoteLauncher(evt, props);
}, [ props ]);
// Keyboard shortcut.
const [ shortcut ] = useNoteLabel(launcherNote, "keyboardShortcut");
@@ -54,3 +40,24 @@ export function CustomNoteLauncher({ launcherNote, getTargetNoteId, getHoistedNo
/>
);
}
export async function launchCustomNoteLauncher(evt: MouseEvent | KeyboardEvent, { launcherNote, getTargetNoteId, getHoistedNoteId }: {
launcherNote: FNote;
getTargetNoteId: (launcherNote: FNote) => string | null | Promise<string | null>;
getHoistedNoteId?: (launcherNote: FNote) => string | null;
}) {
if (evt.which === 3) return;
const targetNoteId = await getTargetNoteId(launcherNote);
if (!targetNoteId) return;
const hoistedNoteIdWithDefault = getHoistedNoteId?.(launcherNote) || appContext.tabManager.getActiveContext()?.hoistedNoteId;
const ctrlKey = isCtrlKey(evt);
if ((evt.which === 1 && ctrlKey) || evt.which === 2) {
const activate = !!evt.shiftKey;
await appContext.tabManager.openInNewTab(targetNoteId, hoistedNoteIdWithDefault, activate);
} else {
await appContext.tabManager.openInSameTab(targetNoteId, hoistedNoteIdWithDefault);
}
}

View File

@@ -49,6 +49,7 @@ export function LaunchBarDropdownButton({ children, icon, dropdownOptions, ...pr
placement: isHorizontalLayout ? "bottom" : "right"
}
}}
mobileBackdrop
{...props}
>{children}</Dropdown>
);

View File

@@ -50,6 +50,9 @@ body.experimental-feature-new-layout {
}
}
}
body.desktop .title-actions {
> .collapsible,
> .note-type-switcher {
padding-inline-start: calc(24px - var(--title-actions-padding-start));
@@ -57,3 +60,4 @@ body.experimental-feature-new-layout {
}
}
}

View File

@@ -1,25 +1,21 @@
import "./NoteTitleActions.css";
import clsx from "clsx";
import { useEffect, useState } from "preact/hooks";
import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
import CollectionProperties from "../note_bars/CollectionProperties";
import { checkFullHeight, getExtendedWidgetType } from "../NoteDetail";
import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes";
import SimpleBadge from "../react/Badge";
import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible";
import { useNoteContext, useNoteLabel, useNoteProperty, useTriliumEvent, useTriliumOptionBool } from "../react/hooks";
import NoteLink, { NewNoteLink } from "../react/NoteLink";
import { NewNoteLink } from "../react/NoteLink";
import { useEditedNotes } from "../ribbon/EditedNotesTab";
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
import NoteTypeSwitcher from "./NoteTypeSwitcher";
export default function NoteTitleActions() {
const { note, ntxId, componentId, noteContext } = useNoteContext();
const isHiddenNote = note && note.noteId !== "_search" && note.noteId.startsWith("_");
const { note, ntxId, componentId, noteContext, viewScope } = useNoteContext();
const noteType = useNoteProperty(note, "type");
return (
@@ -27,7 +23,7 @@ export default function NoteTitleActions() {
<PromotedAttributes note={note} componentId={componentId} noteContext={noteContext} />
{noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />}
<EditedNotes />
<NoteTypeSwitcher />
{(!viewScope?.viewMode || viewScope.viewMode === "default") && <NoteTypeSwitcher />}
</div>
);
}
@@ -48,7 +44,7 @@ function PromotedAttributes({ note, componentId, noteContext }: {
componentId: string,
noteContext: NoteContext | undefined
}) {
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
const [ cells, setCells ] = usePromotedAttributeData(note, componentId, noteContext);
const [ expanded, setExpanded ] = useState(false);
useEffect(() => {

View File

@@ -37,6 +37,10 @@
&:hover {
background: var(--input-background-color);
}
.text {
white-space: nowrap;
}
}
.status-bar-dropdown-button {
@@ -58,101 +62,12 @@
.dropdown-note-info {
padding: 1em !important;
ul {
--row-block-margin: .2em;
list-style-type: none;
padding: 0;
margin: 0;
margin-top: calc(0px - var(--row-block-margin));
margin-bottom: 12px;
display: table;
li {
display: table-row;
> strong {
display: table-cell;
padding: var(--row-block-margin) 0;
opacity: .5;
}
> span {
display: table-cell;
user-select: text;
padding-left: 2em;
}
}
}
}
.dropdown-note-paths {
.note-paths-widget {
padding: 0.5em;
}
.note-path-intro {
color: var(--muted-text-color);
}
.note-path-list {
margin: 12px 0;
padding: 0;
list-style: none;
/* Note path card */
li {
--border-radius: 6px;
position: relative;
background: var(--card-background-color);
padding: 8px 20px 8px 25px;
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
& + li {
margin-top: 2px;
}
/* Current path arrow */
&.path-current::before {
position: absolute;
display: flex;
justify-content: flex-end;
align-items: center;
content: "\ee8f";
top: 0;
left: 0;
width: 20px;
bottom: 0;
font-family: "boxicons";
font-size: .75em;
color: var(--menu-item-icon-color);
}
}
/* Note path segment */
a {
margin-inline: 2px;
padding-inline: 2px;
color: currentColor;
font-weight: normal;
text-decoration: none;
/* The last segment of the note path */
&.basename {
color: var(--muted-text-color);
}
}
}
}
.backlinks-widget > .dropdown-menu {
@@ -160,84 +75,6 @@
max-height: 60vh;
overflow-y: scroll;
/* Backlink card */
li {
--border-radius: 8px;
max-width: 600px;
padding: 10px 20px;
background: var(--card-background-color);
& + li {
margin-top: 2px;
}
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
/* Card header */
& > span:first-child {
display: block;
> span {
display: flex;
flex-wrap: wrap;
align-items: center;
/* Note path */
> small {
flex: 100%;
order: -1;
font-size: .65rem;
.note-path {
padding: 0;
}
}
/* Note icon */
> .tn-icon {
color: var(--menu-item-icon-color);
}
/* Note title */
> a {
margin-inline-start: 4px;
color: currentColor;
font-weight: 500;
}
}
}
/* Card content - excerpt */
& > span:nth-child(2) > div {
all: unset; /* TODO: Remove after disposing the old style from FloatingButtons.css */
display: block;
margin: 8px 0;
border-radius: 4px;
background: var(--quick-search-result-content-background);
padding: 8px;
font-size: .75rem;
a {
background: transparent;
color: var(--quick-search-result-highlight-color);
text-decoration: underline;
}
p {
margin: 0;
}
}
}
}
}
@@ -284,16 +121,50 @@
}
div.similar-notes-widget div.similar-notes-wrapper {
max-height: unset;
}
button.select-button:not(:focus-visible) {
outline: none;
}
}
body.experimental-feature-new-layout .note-info-content {
ul {
--row-block-margin: .2em;
list-style-type: none;
padding: 0;
margin: 0;
margin-top: calc(0px - var(--row-block-margin));
margin-bottom: 12px;
display: table;
li {
display: table-row;
> strong {
display: table-cell;
padding: var(--row-block-margin) 0;
opacity: .5;
}
> span {
display: table-cell;
user-select: text;
padding-left: 2em;
}
}
}
}
body.experimental-feature-new-layout div.similar-notes-widget div.similar-notes-wrapper {
max-height: unset;
}
body.experimental-feature-new-layout.mobile div.similar-notes-widget div.similar-notes-wrapper {
max-height: unset;
padding: 0;
}
.bottom-panel {
margin: 0 !important;
padding: 0;

View File

@@ -56,7 +56,7 @@ export default function StatusBar() {
similarNotesShown: activePane === "similar-notes",
setSimilarNotesShown: (shown) => setActivePane(shown && "similar-notes")
};
const isHiddenNote = note?.isInHiddenSubtree();
const isHiddenNote = note?.isHiddenCompletely();
return (
<div className="status-bar">
@@ -212,8 +212,8 @@ export function getLocaleName(locale: Locale | null | undefined) {
//#region Note info & Similar
interface NoteInfoContext extends StatusBarContext {
similarNotesShown: boolean;
setSimilarNotesShown: (value: boolean) => void;
similarNotesShown?: boolean;
setSimilarNotesShown?: (value: boolean) => void;
}
export function NoteInfoBadge(context: NoteInfoContext) {
@@ -225,7 +225,7 @@ export function NoteInfoBadge(context: NoteInfoContext) {
// Keyboard shortcut.
useTriliumEvent("toggleRibbonTabNoteInfo", () => enabled && dropdownRef.current?.show());
useTriliumEvent("toggleRibbonTabSimilarNotes", () => setSimilarNotesShown(!similarNotesShown));
useTriliumEvent("toggleRibbonTabSimilarNotes", () => setSimilarNotesShown && setSimilarNotesShown(!similarNotesShown));
return (enabled &&
<StatusBarDropdown
@@ -242,8 +242,8 @@ export function NoteInfoBadge(context: NoteInfoContext) {
);
}
function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }: NoteInfoContext & {
dropdownRef: RefObject<BootstrapDropdown>;
export function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }: Pick<NoteInfoContext, "note" | "setSimilarNotesShown"> & {
dropdownRef?: RefObject<BootstrapDropdown>;
noteType: NoteType;
}) {
const { metadata, ...sizeProps } = useNoteMetadata(note);
@@ -251,7 +251,7 @@ function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }:
const noteTypeMapping = useMemo(() => NOTE_TYPES.find(t => t.type === noteType), [ noteType ]);
return (
<>
<div className="note-info-content">
<ul>
{originalFileName && <NoteInfoValue text={t("file_properties.original_file_name")} value={originalFileName} />}
<NoteInfoValue text={t("note_info_widget.created")} value={formatDateTime(metadata?.dateCreated)} />
@@ -262,14 +262,14 @@ function NoteInfoContent({ note, setSimilarNotesShown, noteType, dropdownRef }:
<NoteInfoValue text={t("note_info_widget.note_size")} title={t("note_info_widget.note_size_info")} value={<NoteSizeWidget {...sizeProps} />} />
</ul>
<LinkButton
{setSimilarNotesShown && <LinkButton
text={t("note_info_widget.show_similar_notes")}
onClick={() => {
dropdownRef.current?.hide();
dropdownRef?.current?.hide();
setSimilarNotesShown(true);
}}
/>
</>
/>}
</div>
);
}
@@ -300,7 +300,7 @@ function BacklinksBadge({ note, viewScope }: StatusBarContext) {
const count = useBacklinkCount(note, viewScope?.viewMode === "default");
return (note && count > 0 &&
<StatusBarDropdown
className="backlinks-badge backlinks-widget"
className="backlinks-badge backlinks-widget tn-backlinks-widget"
icon="bx bx-link"
text={t("status_bar.backlinks", { count })}
title={t("status_bar.backlinks_title", { count })}

View File

@@ -89,12 +89,25 @@
&.type-text {
padding: 10px;
--ck-content-todo-list-checkmark-size: 8px;
p { margin-bottom: 0.2em;}
hr { margin-block: 0.1em; height: 1px; }
h2 { font-size: 1.20em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.10em; }
h5 { font-size: 1.05em}
h6 { font-size: 1em; }
ul, ol { margin: 0 }
}
&.type-book,
&.type-contentWidget,
&.type-search,
&.type-empty {
&.type-empty,
&.type-relationMap,
&.type-launcher,
&.tab-preview-placeholder {
display: flex;
align-items: center;
justify-content: center;
@@ -105,13 +118,6 @@
.preview-placeholder {
font-size: 500%;
}
p { margin-bottom: 0.2em;}
h2 { font-size: 1.20em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.10em; }
h5 { font-size: 1.05em}
h6 { font-size: 1em; }
}
&.with-split {

View File

@@ -1,6 +1,7 @@
import "./TabSwitcher.css";
import clsx from "clsx";
import { ComponentChild } from "preact";
import { createPortal, Fragment } from "preact/compat";
import { useCallback, useEffect, useRef, useState } from "preact/hooks";
@@ -11,6 +12,7 @@ import contextMenu from "../../menus/context_menu";
import { getHue, parseColor } from "../../services/css_class_manager";
import froca from "../../services/froca";
import { t } from "../../services/i18n";
import type { ViewMode, ViewScope } from "../../services/link";
import { NoteContent } from "../collections/legacy/ListOrGridView";
import { LaunchBarActionButton } from "../launch_bar/launch_bar_widgets";
import { ICON_MAPPINGS } from "../note_bars/CollectionProperties";
@@ -20,6 +22,13 @@ import Icon from "../react/Icon";
import LinkButton from "../react/LinkButton";
import Modal from "../react/Modal";
const VIEW_MODE_ICON_MAPPINGS: Record<Exclude<ViewMode, "default">, string> = {
source: "bx bx-code",
"contextual-help": "bx bx-help-circle",
"note-map": "bx bxs-network-chart",
attachments: "bx bx-paperclip",
};
export default function TabSwitcher() {
const [ shown, setShown ] = useState(false);
const mainNoteContexts = useMainNoteContexts();
@@ -138,7 +147,6 @@ function Tab({ noteContext, containerRef, selectTab, activeNtxId }: {
activeNtxId: string | null | undefined;
}) {
const { note } = noteContext;
const iconClass = useNoteIcon(note);
const colorClass = note?.getColorClass() || '';
const workspaceTabBackgroundColorHue = getWorkspaceTabBackgroundColorHue(noteContext);
const subContexts = noteContext.getSubContexts();
@@ -158,46 +166,65 @@ function Tab({ noteContext, containerRef, selectTab, activeNtxId }: {
>
{subContexts.map(subContext => (
<Fragment key={subContext.ntxId}>
<header className={colorClass}>
{subContext.note && <Icon icon={iconClass} />}
<span className="title">{subContext.note?.title ?? t("tab_row.new_tab")}</span>
{subContext.isMainContext() && <ActionButton
icon="bx bx-x"
text={t("tab_row.close_tab")}
onClick={(e) => {
// We are closing a tab, so we need to prevent propagation for click (activate tab).
e.stopPropagation();
appContext.tabManager.removeNoteContext(subContext.ntxId);
}}
/>}
</header>
<div className={clsx("tab-preview", `type-${subContext.note?.type ?? "empty"}`)}>
<TabPreviewContent note={subContext.note} />
</div>
<TabHeader noteContext={subContext} colorClass={colorClass} />
<TabPreviewContent note={subContext.note} viewScope={subContext.viewScope} />
</Fragment>
))}
</div>
);
}
function TabPreviewContent({ note }: {
note: FNote | null
}) {
if (!note) {
return <PreviewPlaceholder icon="bx bx-plus" />;
}
function TabHeader({ noteContext, colorClass }: { noteContext: NoteContext, colorClass: string }) {
const iconClass = useNoteIcon(noteContext.note);
const [ navigationTitle, setNavigationTitle ] = useState<string | null>(null);
if (note.type === "book") {
return <PreviewPlaceholder icon={ICON_MAPPINGS[note.getLabelValue("viewType") ?? ""] ?? "bx bx-book"} />;
}
// Manage the title for read-only notes
useEffect(() => {
noteContext?.getNavigationTitle().then(setNavigationTitle);
}, [noteContext]);
return (
<NoteContent
<header className={colorClass}>
{noteContext.note && <Icon icon={iconClass} />}
<span className="title">{navigationTitle ?? noteContext.note?.title ?? t("tab_row.new_tab")}</span>
{noteContext.isMainContext() && <ActionButton
icon="bx bx-x"
text={t("tab_row.close_tab")}
onClick={(e) => {
// We are closing a tab, so we need to prevent propagation for click (activate tab).
e.stopPropagation();
appContext.tabManager.removeNoteContext(noteContext.ntxId);
}}
/>}
</header>
);
}
function TabPreviewContent({ note, viewScope }: {
note: FNote | null,
viewScope: ViewScope | undefined
}) {
let el: ComponentChild;
let isPlaceholder = true;
if (!note) {
el = <PreviewPlaceholder icon="bx bx-plus" />;
} else if (note.type === "book") {
el = <PreviewPlaceholder icon={ICON_MAPPINGS[note.getLabelValue("viewType") ?? ""] ?? "bx bx-book"} />;
} else if (viewScope?.viewMode && viewScope.viewMode !== "default") {
el = <PreviewPlaceholder icon={VIEW_MODE_ICON_MAPPINGS[viewScope?.viewMode ?? ""] ?? "bx bx-empty"} />;
} else {
el = <NoteContent
note={note}
highlightedTokens={undefined}
trim
includeArchivedNotes={false}
/>
/>;
isPlaceholder = false;
}
return (
<div className={clsx("tab-preview", `type-${note?.type ?? "empty"}`, { "tab-preview-placeholder": isPlaceholder })}>{el}</div>
);
}

View File

@@ -0,0 +1,3 @@
.code-note-switcher-modal .dropdown-menu {
background: none !important;
}

View File

@@ -1,84 +1,240 @@
import { useContext } from "preact/hooks";
import "./mobile_detail_menu.css";
import appContext, { CommandMappings } from "../../components/app_context";
import contextMenu, { MenuItem } from "../../menus/context_menu";
import branches from "../../services/branches";
import { Dropdown as BootstrapDropdown } from "bootstrap";
import { createPortal, useRef, useState } from "preact/compat";
import FNote, { NotePathRecord } from "../../entities/fnote";
import { t } from "../../services/i18n";
import { getHelpUrlForNote } from "../../services/in_app_help";
import note_create from "../../services/note_create";
import tree from "../../services/tree";
import { openInAppHelpFromUrl } from "../../services/utils";
import BasicWidget from "../basic_widget";
import server from "../../services/server";
import { BacklinksList, useBacklinkCount } from "../FloatingButtonsDefinitions";
import { getLocaleName, NoteInfoContent } from "../layout/StatusBar";
import ActionButton from "../react/ActionButton";
import { ParentComponent } from "../react/react_utils";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import { useNoteContext, useNoteProperty } from "../react/hooks";
import Modal from "../react/Modal";
import { NoteTypeCodeNoteList, useLanguageSwitcher, useMimeTypes } from "../ribbon/BasicPropertiesTab";
import { NoteContextMenu } from "../ribbon/NoteActions";
import NoteActionsCustom from "../ribbon/NoteActionsCustom";
import { NotePathsWidget, useSortedNotePaths } from "../ribbon/NotePathsTab";
import SimilarNotesTab from "../ribbon/SimilarNotesTab";
import { useProcessedLocales } from "../type_widgets/options/components/LocaleSelector";
export default function MobileDetailMenu() {
const parentComponent = useContext(ParentComponent);
const dropdownRef = useRef<BootstrapDropdown | null>(null);
const { note, noteContext, parentComponent, ntxId, viewScope, hoistedNoteId } = useNoteContext();
const subContexts = noteContext?.getMainContext().getSubContexts() ?? [];
const isMainContext = noteContext?.isMainContext();
const [ backlinksModalShown, setBacklinksModalShown ] = useState(false);
const [ notePathsModalShown, setNotePathsModalShown ] = useState(false);
const [ noteInfoModalShown, setNoteInfoModalShown ] = useState(false);
const [ similarNotesModalShown, setSimilarNotesModalShown ] = useState(false);
const [ codeNoteSwitcherModalShown, setCodeNoteSwitcherModalShown ] = useState(false);
const sortedNotePaths = useSortedNotePaths(note, hoistedNoteId);
const backlinksCount = useBacklinkCount(note, viewScope?.viewMode === "default");
function closePane() {
// Wait first for the context menu to be dismissed, otherwise the backdrop stays on.
requestAnimationFrame(() => {
parentComponent.triggerCommand("closeThisNoteSplit", { ntxId });
});
}
return (
<ActionButton
icon="bx bx-dots-vertical-rounded"
text=""
onClick={(e) => {
const ntxId = (parentComponent as BasicWidget | null)?.getClosestNtxId();
if (!ntxId) return;
<div style={{ contain: "none" }}>
{note ? (
<NoteContextMenu
dropdownRef={dropdownRef}
note={note} noteContext={noteContext}
itemsAtStart={<>
<div className="form-list-row">
<div className="form-list-col">
<FormListItem
icon="bx bx-link"
onClick={() => setBacklinksModalShown(true)}
disabled={backlinksCount === 0}
>{t("status_bar.backlinks", { count: backlinksCount })}</FormListItem>
</div>
<div className="form-list-col">
<FormListItem
icon="bx bx-directions"
onClick={() => setNotePathsModalShown(true)}
disabled={(sortedNotePaths?.length ?? 0) <= 1}
>{t("status_bar.note_paths", { count: sortedNotePaths?.length })}</FormListItem>
</div>
</div>
<FormDropdownDivider />
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
const subContexts = noteContext.getMainContext().getSubContexts();
const isMainContext = noteContext?.isMainContext();
const note = noteContext.note;
const helpUrl = getHelpUrlForNote(note);
{noteContext && ntxId && <NoteActionsCustom note={note} noteContext={noteContext} ntxId={ntxId} />}
<FormListItem
onClick={() => noteContext?.notePath && note_create.createNote(noteContext.notePath)}
icon="bx bx-plus"
>{t("mobile_detail_menu.insert_child_note")}</FormListItem>
{subContexts.length < 2 && <>
<FormDropdownDivider />
<FormListItem
onClick={(e) => {
// We have to manually manage the hide because otherwise the old note context gets activated.
e.stopPropagation();
dropdownRef.current?.hide();
parentComponent.triggerCommand("openNewNoteSplit", { ntxId });
}}
icon="bx bx-dock-right"
>{t("create_pane_button.create_new_split")}</FormListItem>
</>}
{!isMainContext && <>
<FormDropdownDivider />
<FormListItem
icon="bx bx-x"
onClick={closePane}
>{t("close_pane_button.close_this_pane")}</FormListItem>
</>}
<FormDropdownDivider />
</>}
itemsNearNoteSettings={<>
{note.type === "text" && <ContentLanguageSelector note={note} />}
{note.type === "code" && <FormListItem icon={"bx bx-code"} onClick={() => setCodeNoteSwitcherModalShown(true)}>{t("status_bar.code_note_switcher")}</FormListItem>}
<FormListItem icon="bx bx-info-circle" onClick={() => setNoteInfoModalShown(true)}>{t("note_info_widget.title")}</FormListItem>
<FormListItem icon="bx bx-bar-chart" onClick={() => setSimilarNotesModalShown(true)}>{t("similar_notes.title")}</FormListItem>
<FormDropdownDivider />
</>}
/>
) : (
<ActionButton
icon="bx bx-x"
onClick={closePane}
text={t("close_pane_button.close_this_pane")}
/>
)}
const items: (MenuItem<keyof CommandMappings>)[] = [
{ title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note?.type !== "search" },
{ title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note?.noteId !== "root" },
{ kind: "separator" },
{ title: t("mobile_detail_menu.note_revisions"), command: "showRevisions", uiIcon: "bx bx-history" },
{ kind: "separator" },
helpUrl && {
title: t("help-button.title"),
uiIcon: "bx bx-help-circle",
handler: () => openInAppHelpFromUrl(helpUrl)
},
{ kind: "separator" },
subContexts.length < 2 && { title: t("create_pane_button.create_new_split"), command: "openNewNoteSplit", uiIcon: "bx bx-dock-right" },
!isMainContext && { title: t("close_pane_button.close_this_pane"), command: "closeThisNoteSplit", uiIcon: "bx bx-x" }
].filter(i => !!i) as MenuItem<keyof CommandMappings>[];
const lastItem = items.at(-1);
if (lastItem && "kind" in lastItem && lastItem.kind === "separator") {
items.pop();
}
contextMenu.show<keyof CommandMappings>({
x: e.pageX,
y: e.pageY,
items,
selectMenuItemHandler: async ({ command }) => {
if (command === "insertChildNote") {
note_create.createNote(appContext.tabManager.getActiveContextNotePath() ?? undefined);
} else if (command === "delete") {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (!notePath) {
throw new Error("Cannot get note path to delete.");
}
const branchId = await tree.getBranchIdFromUrl(notePath);
if (!branchId) {
throw new Error(t("mobile_detail_menu.error_cannot_get_branch_id", { notePath }));
}
if (await branches.deleteNotes([branchId]) && parentComponent) {
parentComponent.triggerCommand("setActiveScreen", { screen: "tree" });
}
} else if (command && parentComponent) {
parentComponent.triggerCommand(command, { ntxId });
}
},
forcePositionOnMobile: true
});
}}
/>
{createPortal((
<>
<BacklinksModal note={note} modalShown={backlinksModalShown} setModalShown={setBacklinksModalShown} />
<NotePathsModal note={note} modalShown={notePathsModalShown} notePath={noteContext?.notePath} sortedNotePaths={sortedNotePaths} setModalShown={setNotePathsModalShown} />
<NoteInfoModal note={note} modalShown={noteInfoModalShown} setModalShown={setNoteInfoModalShown} />
<SimilarNotesModal note={note} modalShown={similarNotesModalShown} setModalShown={setSimilarNotesModalShown} />
<CodeNoteSwitcherModal note={note} modalShown={codeNoteSwitcherModalShown} setModalShown={setCodeNoteSwitcherModalShown} />
</>
), document.body)}
</div>
);
}
function ContentLanguageSelector({ note }: { note: FNote | null | undefined }) {
const { locales, DEFAULT_LOCALE, currentNoteLanguage, setCurrentNoteLanguage } = useLanguageSwitcher(note);
const { activeLocale, processedLocales } = useProcessedLocales(locales, DEFAULT_LOCALE, currentNoteLanguage ?? DEFAULT_LOCALE.id);
return (
<FormDropdownSubmenu
icon="bx bx-globe"
title={t("mobile_detail_menu.content_language_switcher", { language: getLocaleName(activeLocale ?? DEFAULT_LOCALE) })}
>
{processedLocales.map((locale, index) =>
(typeof locale === "object") ? (
<FormListItem
key={locale.id}
rtl={locale.rtl}
checked={locale.id === currentNoteLanguage}
onClick={() => setCurrentNoteLanguage(locale.id)}
>{locale.name}</FormListItem>
) : (
<FormDropdownDivider key={`divider-${index}`} />
)
)}
</FormDropdownSubmenu>
);
}
interface WithModal {
modalShown: boolean;
setModalShown: (shown: boolean) => void;
}
function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
return (
<Modal
className="backlinks-modal tn-backlinks-widget"
size="md"
title={t("mobile_detail_menu.backlinks")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
<ul className="backlinks-items">
{note && <BacklinksList note={note} />}
</ul>
</Modal>
);
}
function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalShown }: { note: FNote | null | undefined, sortedNotePaths: NotePathRecord[] | undefined, notePath: string | null | undefined } & WithModal) {
return (
<Modal
className="note-paths-modal"
size="md"
title={t("note_paths.title")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
{note && (
<NotePathsWidget
sortedNotePaths={sortedNotePaths}
currentNotePath={notePath}
/>
)}
</Modal>
);
}
function NoteInfoModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
return (
<Modal
className="note-info-modal"
size="md"
title={t("note_info_widget.title")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
{note && <NoteInfoContent note={note} noteType={note.type} />}
</Modal>
);
}
function SimilarNotesModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
return (
<Modal
className="similar-notes-modal"
size="md"
title={t("similar_notes.title")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
<SimilarNotesTab note={note} />
</Modal>
);
}
function CodeNoteSwitcherModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined } & WithModal) {
const currentNoteMime = useNoteProperty(note, "mime");
const mimeTypes = useMimeTypes();
return (
<Modal
className="code-note-switcher-modal"
size="md"
title={t("status_bar.code_note_switcher")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
<div className="dropdown-menu static show">
{note && <NoteTypeCodeNoteList
currentMimeType={currentNoteMime}
mimeTypes={mimeTypes}
changeNoteType={(type, mime) => {
server.put(`notes/${note.noteId}/type`, { type, mime });
setModalShown(false);
}}
/>}
</div>
</Modal>
);
}

View File

@@ -117,3 +117,35 @@ body.experimental-feature-new-layout {
}
}
}
body.mobile .modal.icon-switcher {
.modal-dialog {
left: 0;
right: 0;
margin: unset;
transform: unset;
max-width: 100%;
height: 100%;
}
.modal-body {
padding: 0;
display: flex;
flex-direction: column;
> .filter-row {
padding: 0.25em var(--bs-modal-padding) 0.5em var(--bs-modal-padding);
border-bottom: 1px solid var(--main-border-color);
}
}
.icon-list {
margin: auto;
flex-grow: 1;
height: 100%;
span {
padding: 12px;
}
}
}

View File

@@ -4,7 +4,8 @@ import { IconRegistry } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap";
import clsx from "clsx";
import { t } from "i18next";
import { CSSProperties, RefObject } from "preact";
import { CSSProperties } from "preact";
import { createPortal } from "preact/compat";
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
import type React from "react";
import { CellComponentProps, Grid } from "react-window";
@@ -12,11 +13,15 @@ import { CellComponentProps, Grid } from "react-window";
import FNote from "../entities/fnote";
import attributes from "../services/attributes";
import server from "../services/server";
import { isDesktop, isMobile } from "../services/utils";
import ActionButton from "./react/ActionButton";
import Dropdown from "./react/Dropdown";
import { FormDropdownDivider, FormListItem } from "./react/FormList";
import FormTextBox from "./react/FormTextBox";
import { useNoteContext, useNoteLabel, useStaticTooltip } from "./react/hooks";
import { useNoteContext, useNoteLabel, useStaticTooltip, useWindowSize } from "./react/hooks";
import Modal from "./react/Modal";
const ICON_SIZE = isMobile() ? 56 : 48;
interface IconToCountCache {
iconClassToCountMap: Record<string, number>;
@@ -37,6 +42,10 @@ export default function NoteIcon() {
setIcon(note?.getIcon());
}, [ note, iconClass, workspaceIconClass ]);
if (isMobile()) {
return <MobileNoteIconSwitcher note={note} icon={icon} />;
}
return (
<Dropdown
className="note-icon-widget"
@@ -48,16 +57,47 @@ export default function NoteIcon() {
hideToggleArrow
disabled={viewScope?.viewMode !== "default"}
>
{ note && <NoteIconList note={note} dropdownRef={dropdownRef} /> }
{ note && <NoteIconList note={note} onHide={() => dropdownRef?.current?.hide()} columnCount={12} /> }
</Dropdown>
);
}
function NoteIconList({ note, dropdownRef }: {
note: FNote,
dropdownRef: RefObject<BootstrapDropdown>;
function MobileNoteIconSwitcher({ note, icon }: {
note: FNote | null | undefined;
icon: string | null | undefined;
}) {
const [ modalShown, setModalShown ] = useState(false);
const { windowWidth } = useWindowSize();
return (note &&
<div className="note-icon-widget">
<ActionButton
className="note-icon"
icon={icon ?? "bx bx-empty"}
text={t("note_icon.change_note_icon")}
onClick={() => setModalShown(true)}
/>
{createPortal((
<Modal
title={t("note_icon.change_note_icon")}
size="xl"
show={modalShown} onHidden={() => setModalShown(false)}
className="icon-switcher note-icon-widget"
scrollable
>
<NoteIconList note={note} onHide={() => setModalShown(false)} columnCount={Math.max(1, Math.floor(windowWidth / ICON_SIZE))} />
</Modal>
), document.body)}
</div>
);
}
function NoteIconList({ note, onHide, columnCount }: {
note: FNote;
onHide: () => void;
columnCount: number;
}) {
const searchBoxRef = useRef<HTMLInputElement>(null);
const iconListRef = useRef<HTMLDivElement>(null);
const [ search, setSearch ] = useState<string>();
const [ filterByPrefix, setFilterByPrefix ] = useState<string | null>(null);
@@ -73,53 +113,22 @@ function NoteIconList({ note, dropdownRef }: {
return (
<>
<div class="filter-row">
<span>{t("note_icon.search")}</span>
<FormTextBox
inputRef={searchBoxRef}
type="text"
name="icon-search"
placeholder={ filterByPrefix
? t("note_icon.search_placeholder_filtered", {
number: filteredIcons.length ?? 0,
name: glob.iconRegistry.sources.find(s => s.prefix === filterByPrefix)?.name ?? ""
})
: t("note_icon.search_placeholder", { number: filteredIcons.length ?? 0, count: glob.iconRegistry.sources.length })}
currentValue={search} onChange={setSearch}
autoFocus
/>
{getIconLabels(note).length > 0 && (
<div style={{ textAlign: "center" }}>
<ActionButton
icon="bx bx-reset"
text={t("note_icon.reset-default")}
onClick={() => {
if (!note) return;
for (const label of getIconLabels(note)) {
attributes.removeAttributeById(note.noteId, label.attributeId);
}
dropdownRef?.current?.hide();
}}
/>
</div>
)}
{glob.iconRegistry.sources.length > 0 && <Dropdown
buttonClassName="bx bx-filter-alt"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
title={t("note_icon.filter")}
>
<IconFilterContent filterByPrefix={filterByPrefix} setFilterByPrefix={setFilterByPrefix} />
</Dropdown>}
</div>
<FilterRow
note={note}
filterByPrefix={filterByPrefix}
search={search}
setSearch={setSearch}
setFilterByPrefix={setFilterByPrefix}
filteredIcons={filteredIcons}
onHide={onHide}
/>
<div
class="icon-list"
ref={iconListRef}
style={{
width: (columnCount * ICON_SIZE + 10),
}}
onClick={(e) => {
// Make sure we are not clicking on something else than a button.
const clickedTarget = e.target as HTMLElement;
@@ -130,18 +139,19 @@ function NoteIconList({ note, dropdownRef }: {
const attributeToSet = note.hasOwnedLabel("workspace") ? "workspaceIconClass" : "iconClass";
attributes.setLabel(note.noteId, attributeToSet, iconClass);
}
dropdownRef?.current?.hide();
onHide();
}}
>
{filteredIcons.length ? (
<Grid
columnCount={12}
columnWidth={48}
rowCount={Math.ceil(filteredIcons.length / 12)}
rowHeight={48}
columnCount={columnCount}
columnWidth={ICON_SIZE}
rowCount={Math.ceil(filteredIcons.length / columnCount)}
rowHeight={ICON_SIZE}
cellComponent={IconItemCell}
cellProps={{
filteredIcons
filteredIcons,
columnCount
}}
/>
) : (
@@ -152,10 +162,95 @@ function NoteIconList({ note, dropdownRef }: {
);
}
function IconItemCell({ rowIndex, columnIndex, style, filteredIcons }: CellComponentProps<{
function FilterRow({ note, filterByPrefix, search, setSearch, setFilterByPrefix, filteredIcons, onHide }: {
note: FNote;
filterByPrefix: string | null;
search: string | undefined;
setSearch: (value: string | undefined) => void;
setFilterByPrefix: (value: string | null) => void;
filteredIcons: IconWithName[];
onHide: () => void;
}) {
const searchBoxRef = useRef<HTMLInputElement>(null);
const hasCustomIcon = getIconLabels(note).length > 0;
function resetToDefaultIcon() {
if (!note) return;
for (const label of getIconLabels(note)) {
attributes.removeAttributeById(note.noteId, label.attributeId);
}
onHide();
}
return (
<div class="filter-row">
<span>{t("note_icon.search")}</span>
<FormTextBox
inputRef={searchBoxRef}
type="text"
name="icon-search"
placeholder={ filterByPrefix
? t("note_icon.search_placeholder_filtered", {
number: filteredIcons.length ?? 0,
name: glob.iconRegistry.sources.find(s => s.prefix === filterByPrefix)?.name ?? ""
})
: t("note_icon.search_placeholder", { number: filteredIcons.length ?? 0, count: glob.iconRegistry.sources.length })}
currentValue={search} onChange={setSearch}
autoFocus
/>
{isDesktop()
? <>
{hasCustomIcon && (
<div style={{ textAlign: "center" }}>
<ActionButton
icon="bx bx-reset"
text={t("note_icon.reset-default")}
onClick={resetToDefaultIcon}
/>
</div>
)}
{<Dropdown
buttonClassName="bx bx-filter-alt"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
title={t("note_icon.filter")}
>
<IconFilterContent filterByPrefix={filterByPrefix} setFilterByPrefix={setFilterByPrefix} />
</Dropdown>}
</> : (
<Dropdown
buttonClassName="bx bx-dots-vertical-rounded"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
dropdownContainerClassName="mobile-bottom-menu"
>
{hasCustomIcon && <>
<FormListItem
icon="bx bx-reset"
onClick={resetToDefaultIcon}
disabled={!hasCustomIcon}
>{t("note_icon.reset-default")}</FormListItem>
<FormDropdownDivider />
</>}
<IconFilterContent filterByPrefix={filterByPrefix} setFilterByPrefix={setFilterByPrefix} />
</Dropdown>
)}
</div>
);
}
function IconItemCell({ rowIndex, columnIndex, style, filteredIcons, columnCount }: CellComponentProps<{
filteredIcons: IconWithName[];
columnCount: number;
}>) {
const iconIndex = rowIndex * 12 + columnIndex;
const iconIndex = rowIndex * columnCount + columnIndex;
const iconData = filteredIcons[iconIndex] as IconWithName | undefined;
if (!iconData) return <></> as React.ReactElement;
@@ -184,7 +279,7 @@ function IconFilterContent({ filterByPrefix, setFilterByPrefix }: {
checked={filterByPrefix === "bx"}
onClick={() => setFilterByPrefix("bx")}
>{t("note_icon.filter-default")}</FormListItem>
<FormDropdownDivider />
{glob.iconRegistry.sources.length > 1 && <FormDropdownDivider />}
{glob.iconRegistry.sources.map(({ prefix, name, icon }) => (
prefix !== "bx" && <FormListItem

View File

@@ -109,4 +109,29 @@ body.experimental-feature-new-layout {
--input-focus-color: initial;
}
}
&.mobile .title-row {
.icon-action:not(.note-icon) {
--icon-button-size: 45px;
--icon-button-icon-ratio: 0.5;
flex-shrink: 0;
}
.note-actions {
width: auto;
}
.note-badges {
margin-inline: 0.5em;
flex-shrink: 0;
}
.note-icon-widget {
margin-inline: 0.5em;
.note-icon {
--icon-button-size: 24px;
}
}
}
}

View File

@@ -1,4 +1,4 @@
#left-pane .tree-wrapper {
.tree-wrapper {
.note-indicator-icon.subtree-hidden-badge {
font-family: inherit !important;
margin-inline: 0.5em;
@@ -9,7 +9,6 @@
border-radius: 0.5em;
font-size: 0.7rem;
font-weight: normal;
float: right;
vertical-align: middle;
}

View File

@@ -1992,7 +1992,7 @@ function buildEnhanceTitle() {
if (isSubtreeHidden && count > 0) {
const $badge = $(`<span class="note-indicator-icon subtree-hidden-badge">${count}</span>`);
$badge.attr("title", t("note_tree.subtree-hidden-tooltip", { count }));
$span.find(".fancytree-title").append($badge);
$span.append($badge);
}
};
}

View File

@@ -43,7 +43,8 @@ export const TYPE_MAPPINGS: Record<ExtendedNoteType, NoteTypeMapping> = {
},
protectedSession: {
view: () => import("./type_widgets/ProtectedSession"),
className: "protected-session-password-component"
className: "protected-session-password-component",
isFullHeight: true
},
book: {
view: () => import("./type_widgets/Book"),

View File

@@ -3,10 +3,7 @@
line-height: 1em;
display: flex;
align-items: center;
appearance: none;
background: transparent;
border: 0;
color: inherit;
padding-inline-end: 12px;
.arrow {
font-size: 1.3em;
@@ -17,21 +14,34 @@
.collapsible-body {
height: 0;
overflow: hidden;
&.fully-expanded {
overflow: visible;
}
}
.collapsible-inner-body {
padding-top: 0.5em;
opacity: 0;
}
&.expanded {
.collapsible-title .arrow {
transform: rotate(90deg);
}
.collapsible-inner-body {
opacity: 1;
}
}
&.with-transition {
.collapsible-body {
transition: height 250ms ease-in;
}
.collapsible-inner-body {
transition: opacity 250ms ease-in;
}
}
}

View File

@@ -27,6 +27,7 @@ export function ExternallyControlledCollapsible({ title, children, className, ex
const { height } = useElementSize(innerRef) ?? {};
const contentId = useUniqueName();
const [ transitionEnabled, setTransitionEnabled ] = useState(false);
const [ fullyExpanded, setFullyExpanded ] = useState(false);
useEffect(() => {
const timeout = setTimeout(() => {
@@ -35,13 +36,28 @@ export function ExternallyControlledCollapsible({ title, children, className, ex
return () => clearTimeout(timeout);
}, []);
useEffect(() => {
if (expanded) {
if (transitionEnabled) {
const timeout = setTimeout(() => {
setFullyExpanded(true);
}, 250);
return () => clearTimeout(timeout);
} else {
setFullyExpanded(true);
}
} else {
setFullyExpanded(false);
}
}, [expanded, transitionEnabled])
return (
<div className={clsx("collapsible", className, {
expanded,
"with-transition": transitionEnabled
})}>
<button
className="collapsible-title"
className="collapsible-title tn-low-profile"
onClick={() => setExpanded(!expanded)}
aria-expanded={expanded}
aria-controls={contentId}
@@ -53,7 +69,7 @@ export function ExternallyControlledCollapsible({ title, children, className, ex
<div
id={contentId}
ref={bodyRef}
className="collapsible-body"
className={clsx("collapsible-body", {"fully-expanded": fullyExpanded})}
style={{ height: expanded ? height : "0" }}
aria-hidden={!expanded}
>

View File

@@ -3,6 +3,7 @@ import { ComponentChildren, HTMLAttributes } from "preact";
import { CSSProperties, HTMLProps } from "preact/compat";
import { MutableRef, useCallback, useEffect, useRef, useState } from "preact/hooks";
import { isMobile } from "../../services/utils";
import { useTooltip, useUniqueName } from "./hooks";
type DataAttributes = {
@@ -32,9 +33,10 @@ export interface DropdownProps extends Pick<HTMLProps<HTMLDivElement>, "id" | "c
dropdownRef?: MutableRef<BootstrapDropdown | null>;
titlePosition?: "top" | "right" | "bottom" | "left";
titleOptions?: Partial<Tooltip.Options>;
mobileBackdrop?: boolean;
}
export default function Dropdown({ id, className, buttonClassName, isStatic, children, title, text, dropdownContainerStyle, dropdownContainerClassName, dropdownContainerRef: externalContainerRef, hideToggleArrow, iconAction, disabled, noSelectButtonStyle, noDropdownListStyle, forceShown, onShown: externalOnShown, onHidden: externalOnHidden, dropdownOptions, buttonProps, dropdownRef, titlePosition, titleOptions }: DropdownProps) {
export default function Dropdown({ id, className, buttonClassName, isStatic, children, title, text, dropdownContainerStyle, dropdownContainerClassName, dropdownContainerRef: externalContainerRef, hideToggleArrow, iconAction, disabled, noSelectButtonStyle, noDropdownListStyle, forceShown, onShown: externalOnShown, onHidden: externalOnHidden, dropdownOptions, buttonProps, dropdownRef, titlePosition, titleOptions, mobileBackdrop }: DropdownProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
const triggerRef = useRef<HTMLButtonElement | null>(null);
const dropdownContainerRef = useRef<HTMLUListElement | null>(null);
@@ -74,12 +76,18 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
setShown(true);
externalOnShown?.();
hideTooltip();
}, [ hideTooltip ]);
if (mobileBackdrop && isMobile()) {
document.getElementById("context-menu-cover")?.classList.add("show", "global-menu-cover");
}
}, [ hideTooltip, mobileBackdrop ]);
const onHidden = useCallback(() => {
setShown(false);
externalOnHidden?.();
}, []);
if (mobileBackdrop && isMobile()) {
document.getElementById("context-menu-cover")?.classList.remove("show", "global-menu-cover");
}
}, [ mobileBackdrop ]);
useEffect(() => {
if (!containerRef.current) return;

View File

@@ -3,8 +3,9 @@ import { useEffect, useRef } from "preact/hooks";
import ActionButton, { ActionButtonProps } from "./ActionButton";
import Button, { ButtonProps } from "./Button";
import { FormListItem, FormListItemOpts } from "./FormList";
interface FormFileUploadProps {
export interface FormFileUploadProps {
name?: string;
onChange: (files: FileList | null) => void;
multiple?: boolean;
@@ -75,3 +76,25 @@ export function FormFileUploadActionButton({ onChange, ...buttonProps }: Omit<Ac
</>
);
}
/**
* Similar to {@link FormFileUploadButton}, but uses an {@link FormListItem} instead of a normal {@link Button}.
* @param param the change listener for the file upload and the properties for the button.
*/
export function FormFileUploadFormListItem({ onChange, children, ...buttonProps }: Omit<FormListItemOpts, "onClick"> & Pick<FormFileUploadProps, "onChange">) {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<FormListItem
{...buttonProps}
onClick={() => inputRef.current?.click()}
>{children}</FormListItem>
<FormFileUpload
inputRef={inputRef}
hidden
onChange={onChange}
/>
</>
);
}

View File

@@ -27,3 +27,13 @@
}
}
}
.form-list-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1em;
.form-list-col {
flex-grow: 1;
}
}

View File

@@ -78,7 +78,7 @@ export interface FormListBadge {
text: string;
}
interface FormListItemOpts {
export interface FormListItemOpts {
children: ComponentChildren;
icon?: string;
value?: string;

View File

@@ -0,0 +1,12 @@
import { ComponentChildren } from "preact";
import { isMobile } from "../../services/utils";
interface ResponsiveContainerProps {
mobile?: ComponentChildren;
desktop?: ComponentChildren;
}
export default function ResponsiveContainer({ mobile, desktop }: ResponsiveContainerProps) {
return (isMobile() ? mobile : desktop);
}

View File

@@ -1,5 +1,4 @@
import { MimeType, NoteType, ToggleInParentResponse } from "@triliumnext/commons";
import { ComponentChildren } from "preact";
import { createPortal } from "preact/compat";
import { Dispatch, StateUpdater, useCallback, useEffect, useMemo, useState } from "preact/hooks";
@@ -117,19 +116,18 @@ export function NoteTypeDropdownContent({ currentNoteType, currentNoteMime, note
onClick={() => changeNoteType(type, mime)}
>{title}</FormListItem>
);
} else {
return (
<>
<FormDropdownDivider />
<FormListItem
checked={checked}
disabled
>
<strong>{title}</strong>
</FormListItem>
</>
);
}
return (
<>
<FormDropdownDivider />
<FormListItem
checked={checked}
disabled
>
<strong>{title}</strong>
</FormListItem>
</>
);
})}
{!noCodeNotes && <NoteTypeCodeNoteList mimeTypes={mimeTypes} changeNoteType={changeNoteType} setModalShown={setModalShown} />}
@@ -141,7 +139,7 @@ export function NoteTypeCodeNoteList({ currentMimeType, mimeTypes, changeNoteTyp
currentMimeType?: string;
mimeTypes: MimeType[];
changeNoteType(type: NoteType, mime: string): void;
setModalShown(shown: boolean): void;
setModalShown?(shown: boolean): void;
}) {
return (
<>
@@ -155,8 +153,10 @@ export function NoteTypeCodeNoteList({ currentMimeType, mimeTypes, changeNoteTyp
</FormListItem>
))}
<FormDropdownDivider />
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
{setModalShown && <>
<FormDropdownDivider />
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
</>}
</>
);
}
@@ -195,7 +195,7 @@ function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
onChange={(shouldProtect) => note && protected_session.protectNote(note.noteId, shouldProtect, false)}
/>
</div>
)
);
}
function EditabilitySelect({ note }: { note?: FNote | null }) {
@@ -417,9 +417,9 @@ function findTypeTitle(type?: NoteType, mime?: string | null) {
const found = mimeTypes.find((mt) => mt.mime === mime);
return found ? found.title : mime;
} else {
const noteType = NOTE_TYPES.find((nt) => nt.type === type);
return noteType ? noteType.title : type;
}
const noteType = NOTE_TYPES.find((nt) => nt.type === type);
return noteType ? noteType.title : type;
}

View File

@@ -1,6 +1,6 @@
import { ConvertToAttachmentResponse } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap";
import { RefObject } from "preact";
import { ComponentChildren, RefObject } from "preact";
import { useContext, useEffect, useRef } from "preact/hooks";
import appContext, { CommandNames } from "../../components/app_context";
@@ -22,7 +22,7 @@ import MovePaneButton from "../buttons/move_pane_button";
import ActionButton from "../react/ActionButton";
import Dropdown from "../react/Dropdown";
import { FormDropdownDivider, FormDropdownSubmenu, FormListHeader, FormListItem, FormListToggleableItem } from "../react/FormList";
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumOption } from "../react/hooks";
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useSyncedRef, useTriliumEvent, useTriliumOption } from "../react/hooks";
import { ParentComponent } from "../react/react_utils";
import { NoteTypeDropdownContent, useNoteBookmarkState, useShareState } from "./BasicPropertiesTab";
import NoteActionsCustom from "./NoteActionsCustom";
@@ -63,8 +63,14 @@ function RevisionsButton({ note }: { note: FNote }) {
type ItemToFocus = "basic-properties";
function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
const dropdownRef = useRef<BootstrapDropdown>(null);
export function NoteContextMenu({ note, noteContext, itemsAtStart, itemsNearNoteSettings, dropdownRef: externalDropdownRef }: {
note: FNote,
noteContext?: NoteContext,
itemsAtStart?: ComponentChildren;
itemsNearNoteSettings?: ComponentChildren;
dropdownRef?: RefObject<BootstrapDropdown>;
}) {
const dropdownRef = useSyncedRef<BootstrapDropdown>(externalDropdownRef, null);
const parentComponent = useContext(ParentComponent);
const noteType = useNoteProperty(note, "type") ?? "";
const [viewType] = useNoteLabel(note, "viewType");
@@ -99,12 +105,15 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
dropdownRef={dropdownRef}
buttonClassName={ isNewLayout ? "bx bx-dots-horizontal-rounded" : "bx bx-dots-vertical-rounded" }
className="note-actions"
dropdownContainerClassName="mobile-bottom-menu"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
onHidden={() => itemToFocusRef.current = null }
mobileBackdrop
>
{itemsAtStart}
{isReadOnly && <>
<CommandItem icon="bx bx-pencil" text={t("read-only-info.edit-note")}
@@ -123,6 +132,8 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
<FormDropdownDivider />
</>}
{itemsNearNoteSettings}
<CommandItem icon="bx bx-import" text={t("note_actions.import_files")}
disabled={isInOptionsOrHelp || note.type === "search"}
command={() => parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} />
@@ -277,7 +288,7 @@ function DevelopmentActions({ note, noteContext }: { note: FNote, noteContext?:
);
}
function CommandItem({ icon, text, title, command, disabled }: { icon: string, text: string, title?: string, command: CommandNames | (() => void), disabled?: boolean, destructive?: boolean }) {
export function CommandItem({ icon, text, title, command, disabled }: { icon: string, text: string, title?: string, command: CommandNames | (() => void), disabled?: boolean, destructive?: boolean }) {
return <FormListItem
icon={icon}
title={title}

View File

@@ -0,0 +1,3 @@
body.mobile .note-actions-custom:not(:empty) {
margin-bottom: calc(var(--bs-dropdown-divider-margin-y) * 2);
}

View File

@@ -1,3 +1,5 @@
import "./NoteActionsCustom.css";
import { NoteType } from "@triliumnext/commons";
import { useContext, useEffect, useRef, useState } from "preact/hooks";
@@ -7,11 +9,12 @@ import FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
import { getHelpUrlForNote } from "../../services/in_app_help";
import { downloadFileNote, openNoteExternally } from "../../services/open";
import { openInAppHelpFromUrl } from "../../services/utils";
import { isMobile, openInAppHelpFromUrl } from "../../services/utils";
import { ViewTypeOptions } from "../collections/interface";
import { buildSaveSqlToNoteHandler } from "../FloatingButtonsDefinitions";
import ActionButton from "../react/ActionButton";
import { FormFileUploadActionButton } from "../react/FormFileUpload";
import ActionButton, { ActionButtonProps } from "../react/ActionButton";
import { FormFileUploadActionButton, FormFileUploadFormListItem, FormFileUploadProps } from "../react/FormFileUpload";
import { FormListItem } from "../react/FormList";
import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumEvents, useTriliumOption } from "../react/hooks";
import { ParentComponent } from "../react/react_utils";
import { buildUploadNewFileRevisionListener } from "./FilePropertiesTab";
@@ -32,6 +35,8 @@ interface NoteActionsCustomInnerProps extends NoteActionsCustomProps {
viewType: ViewTypeOptions | null | undefined;
}
const cachedIsMobile = isMobile();
/**
* Part of {@link NoteActions} on the new layout, but are rendered with a slight spacing
* from the rest of the note items and the buttons differ based on the note type.
@@ -115,7 +120,7 @@ function UploadNewRevisionButton({ note, onChange }: NoteActionsCustomInnerProps
onChange: (files: FileList | null) => void;
}) {
return (
<FormFileUploadActionButton
<NoteActionWithFileUpload
icon="bx bx-folder-open"
text={t("image_properties.upload_new_revision")}
disabled={!note.isContentAvailable()}
@@ -125,8 +130,8 @@ function UploadNewRevisionButton({ note, onChange }: NoteActionsCustomInnerProps
}
function OpenExternallyButton({ note, noteMime }: NoteActionsCustomInnerProps) {
return (
<ActionButton
return (!cachedIsMobile &&
<NoteAction
icon="bx bx-link-external"
text={t("file_properties.open")}
disabled={note.isProtected}
@@ -137,7 +142,7 @@ function OpenExternallyButton({ note, noteMime }: NoteActionsCustomInnerProps) {
function DownloadFileButton({ note, parentComponent, ntxId }: NoteActionsCustomInnerProps) {
return (
<ActionButton
<NoteAction
icon="bx bx-download"
text={t("file_properties.download")}
disabled={!note.isContentAvailable()}
@@ -149,7 +154,7 @@ function DownloadFileButton({ note, parentComponent, ntxId }: NoteActionsCustomI
//#region Floating buttons
function CopyReferenceToClipboardButton({ ntxId, noteType, parentComponent }: NoteActionsCustomInnerProps) {
return (["mermaid", "canvas", "mindMap", "image"].includes(noteType) &&
<ActionButton
<NoteAction
text={t("image_properties.copy_reference_to_clipboard")}
icon="bx bx-copy"
onClick={() => parentComponent?.triggerEvent("copyImageReferenceToClipboard", { ntxId })}
@@ -161,7 +166,7 @@ function RefreshButton({ note, noteType, isDefaultViewMode, parentComponent, not
const isEnabled = (note.noteId === "_backendLog" || noteType === "render") && isDefaultViewMode;
return (isEnabled &&
<ActionButton
<NoteAction
text={t("backend_log.refresh")}
icon="bx bx-refresh"
onClick={() => parentComponent.triggerEvent("refreshData", { ntxId: noteContext.ntxId })}
@@ -170,11 +175,11 @@ function RefreshButton({ note, noteType, isDefaultViewMode, parentComponent, not
}
function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: NoteActionsCustomInnerProps) {
const isShown = note.type === "mermaid" && note.isContentAvailable() && isDefaultViewMode;
const isShown = note.type === "mermaid" && !cachedIsMobile && note.isContentAvailable() && isDefaultViewMode;
const [ splitEditorOrientation, setSplitEditorOrientation ] = useTriliumOption("splitEditorOrientation");
const upcomingOrientation = splitEditorOrientation === "horizontal" ? "vertical" : "horizontal";
return isShown && <ActionButton
return isShown && <NoteAction
text={upcomingOrientation === "vertical" ? t("switch_layout_button.title_vertical") : t("switch_layout_button.title_horizontal")}
icon={upcomingOrientation === "vertical" ? "bx bxs-dock-bottom" : "bx bxs-dock-left"}
onClick={() => setSplitEditorOrientation(upcomingOrientation)}
@@ -188,7 +193,7 @@ export function ToggleReadOnlyButton({ note, isDefaultViewMode }: NoteActionsCus
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || isSavedSqlite)
&& note.isContentAvailable() && isDefaultViewMode;
return isEnabled && <ActionButton
return isEnabled && <NoteAction
text={isReadOnly ? t("toggle_read_only_button.unlock-editing") : t("toggle_read_only_button.lock-editing")}
icon={isReadOnly ? "bx bx-lock-open-alt" : "bx bx-lock-alt"}
onClick={() => setReadOnly(!isReadOnly)}
@@ -197,7 +202,7 @@ export function ToggleReadOnlyButton({ note, isDefaultViewMode }: NoteActionsCus
function RunActiveNoteButton({ noteMime }: NoteActionsCustomInnerProps) {
const isEnabled = noteMime.startsWith("application/javascript") || noteMime === "text/x-sqlite;schema=trilium";
return isEnabled && <ActionButton
return isEnabled && <NoteAction
icon="bx bx-play"
text={t("code_buttons.execute_button_title")}
triggerCommand="runActiveNote"
@@ -218,7 +223,7 @@ function SaveToNoteButton({ note, noteMime }: NoteActionsCustomInnerProps) {
}
});
return isEnabled && <ActionButton
return isEnabled && <NoteAction
icon="bx bx-save"
text={t("code_buttons.save_to_note_button_title")}
onClick={buildSaveSqlToNoteHandler(note)}
@@ -227,7 +232,7 @@ function SaveToNoteButton({ note, noteMime }: NoteActionsCustomInnerProps) {
function OpenTriliumApiDocsButton({ noteMime }: NoteActionsCustomInnerProps) {
const isEnabled = noteMime.startsWith("application/javascript;env=");
return isEnabled && <ActionButton
return isEnabled && <NoteAction
icon="bx bx-help-circle"
text={t("code_buttons.trilium_api_docs_button_title")}
onClick={() => openInAppHelpFromUrl(noteMime.endsWith("frontend") ? "Q2z6av6JZVWm" : "MEtfsqa5VwNi")}
@@ -239,7 +244,7 @@ function InAppHelpButton({ note }: NoteActionsCustomInnerProps) {
const isEnabled = !!helpUrl;
return isEnabled && (
<ActionButton
<NoteAction
icon="bx bx-help-circle"
text={t("help-button.title")}
onClick={() => helpUrl && openInAppHelpFromUrl(helpUrl)}
@@ -249,7 +254,7 @@ function InAppHelpButton({ note }: NoteActionsCustomInnerProps) {
function AddChildButton({ parentComponent, noteType, ntxId, isReadOnly }: NoteActionsCustomInnerProps) {
if (noteType === "relationMap") {
return <ActionButton
return <NoteAction
icon="bx bx-folder-plus"
text={t("relation_map_buttons.create_child_note_title")}
onClick={() => parentComponent.triggerEvent("relationMapCreateChildNote", { ntxId })}
@@ -258,3 +263,19 @@ function AddChildButton({ parentComponent, noteType, ntxId, isReadOnly }: NoteAc
}
}
//#endregion
function NoteAction({ text, ...props }: Pick<ActionButtonProps, "text" | "icon" | "disabled" | "triggerCommand"> & {
onClick?: ((e: MouseEvent) => void) | undefined;
}) {
return (cachedIsMobile
? <FormListItem {...props}>{text}</FormListItem>
: <ActionButton text={text} {...props} />
);
}
function NoteActionWithFileUpload({ text, ...props }: Pick<ActionButtonProps, "text" | "icon" | "disabled" | "triggerCommand"> & Pick<FormFileUploadProps, "onChange">) {
return (cachedIsMobile
? <FormFileUploadFormListItem {...props}>{text}</FormFileUploadFormListItem>
: <FormFileUploadActionButton text={text} {...props} />
);
}

View File

@@ -0,0 +1,63 @@
body.experimental-feature-new-layout .note-paths-widget {
.note-path-intro {
color: var(--muted-text-color);
}
.note-path-list {
margin: 12px 0;
padding: 0;
list-style: none;
/* Note path card */
li {
--border-radius: 6px;
position: relative;
background: var(--card-background-color);
padding: 8px 20px 8px 25px;
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
& + li {
margin-top: 2px;
}
/* Current path arrow */
&.path-current::before {
position: absolute;
display: flex;
justify-content: flex-end;
align-items: center;
content: "\ee8f";
top: 0;
left: 0;
width: 20px;
bottom: 0;
font-family: "boxicons";
font-size: .75em;
color: var(--menu-item-icon-color);
}
}
/* Note path segment */
a {
margin-inline: 2px;
padding-inline: 2px;
color: currentColor;
font-weight: normal;
text-decoration: none;
/* The last segment of the note path */
&.basename {
color: var(--muted-text-color);
}
}
}
}

View File

@@ -1,15 +1,16 @@
import { useEffect, useMemo, useState } from "preact/hooks";
import "./NotePathsTab.css";
import clsx from "clsx";
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import FNote, { NotePathRecord } from "../../entities/fnote";
import { t } from "../../services/i18n";
import { NOTE_PATH_TITLE_SEPARATOR } from "../../services/tree";
import { useTriliumEvent } from "../react/hooks";
import NoteLink from "../react/NoteLink";
import { joinElements } from "../react/react_utils";
import { TabContext } from "./ribbon-interface";
import LinkButton from "../react/LinkButton";
import clsx from "clsx";
import NoteLink from "../react/NoteLink";
import { joinElements, ParentComponent } from "../react/react_utils";
import { TabContext } from "./ribbon-interface";
export default function NotePathsTab({ note, hoistedNoteId, notePath }: TabContext) {
const sortedNotePaths = useSortedNotePaths(note, hoistedNoteId);
@@ -20,6 +21,7 @@ export function NotePathsWidget({ sortedNotePaths, currentNotePath }: {
sortedNotePaths: NotePathRecord[] | undefined;
currentNotePath?: string | null | undefined;
}) {
const parentComponent = useContext(ParentComponent);
return (
<div class="note-paths-widget">
<>
@@ -39,7 +41,7 @@ export function NotePathsWidget({ sortedNotePaths, currentNotePath }: {
<LinkButton
text={t("note_paths.clone_button")}
triggerCommand="cloneNoteIdsTo"
onClick={() => parentComponent?.triggerCommand("cloneNoteIdsTo")}
/>
</>
</div>
@@ -112,9 +114,9 @@ function NotePath({ currentNotePath, notePathRecord }: { currentNotePath?: strin
<li class={classes}>
{joinElements(fullNotePaths.map((notePath, index, arr) => (
<NoteLink key={notePath}
className={clsx({"basename": (index === arr.length - 1)})}
notePath={notePath}
noPreview />
className={clsx({"basename": (index === arr.length - 1)})}
notePath={notePath}
noPreview />
)), NOTE_PATH_TITLE_SEPARATOR)}
{icons.map(({ icon, title }) => (

View File

@@ -1,4 +1,5 @@
import { AttributeType } from "@triliumnext/commons";
import clsx from "clsx";
import { ComponentChildren, VNode } from "preact";
import { useEffect, useMemo, useRef } from "preact/hooks";
@@ -7,6 +8,7 @@ import FNote from "../../entities/fnote";
import { removeOwnedAttributesByNameOrType } from "../../services/attributes";
import { t } from "../../services/i18n";
import server from "../../services/server";
import Admonition from "../react/Admonition";
import FormSelect from "../react/FormSelect";
import FormTextArea from "../react/FormTextArea";
import FormTextBox from "../react/FormTextBox";
@@ -105,8 +107,9 @@ export const SEARCH_OPTIONS: SearchOption[] = [
}
];
function SearchOption({ note, title, titleIcon, children, help, attributeName, attributeType, additionalAttributesToDelete }: {
function SearchOption({ note, className, title, titleIcon, children, help, attributeName, attributeType, additionalAttributesToDelete }: {
note: FNote;
className?: string;
title: string,
titleIcon?: string,
children?: ComponentChildren,
@@ -116,7 +119,7 @@ function SearchOption({ note, title, titleIcon, children, help, attributeName, a
additionalAttributesToDelete?: { type: "label" | "relation", name: string }[]
}) {
return (
<tr className={attributeName}>
<tr className={clsx(attributeName, className)}>
<td className="title-column">
{titleIcon && <><Icon icon={titleIcon} />{" "}</>}
{title}
@@ -154,64 +157,57 @@ function SearchStringOption({ note, refreshResults, error, ...restProps }: Searc
}
}, 1000);
// React to errors
const { showTooltip, hideTooltip } = useTooltip(inputRef, {
trigger: "manual",
title: `${t("search_string.error", { error: error?.message })}`,
html: true,
placement: "bottom"
});
// Auto-focus.
useEffect(() => inputRef.current?.focus(), []);
useEffect(() => {
if (error) {
showTooltip();
setTimeout(() => hideTooltip(), 4000);
} else {
hideTooltip();
}
}, [ error ]);
return <>
<SearchOption
title={t("search_string.title_column")}
className={clsx({ "has-error": !!error })}
help={<>
<strong>{t("search_string.search_syntax")}</strong> - {t("search_string.also_see")} <a href="#" data-help-page="search.html">{t("search_string.complete_help")}</a>
<ul style="marigin-bottom: 0;">
<li>{t("search_string.full_text_search")}</li>
<li><code>#abc</code> - {t("search_string.label_abc")}</li>
<li><code>#year = 2019</code> - {t("search_string.label_year")}</li>
<li><code>#rock #pop</code> - {t("search_string.label_rock_pop")}</li>
<li><code>#rock or #pop</code> - {t("search_string.label_rock_or_pop")}</li>
<li><code>#year &lt;= 2000</code> - {t("search_string.label_year_comparison")}</li>
<li><code>note.dateCreated &gt;= MONTH-1</code> - {t("search_string.label_date_created")}</li>
</ul>
</>}
note={note} {...restProps}
>
<FormTextArea
inputRef={inputRef}
className="search-string"
placeholder={t("search_string.placeholder")}
currentValue={searchString ?? ""}
onChange={text => {
currentValue.current = text;
spacedUpdate.scheduleUpdate();
}}
onKeyDown={async (e) => {
if (e.key === "Enter") {
e.preventDefault();
return <SearchOption
title={t("search_string.title_column")}
help={<>
<strong>{t("search_string.search_syntax")}</strong> - {t("search_string.also_see")} <a href="#" data-help-page="search.html">{t("search_string.complete_help")}</a>
<ul style="marigin-bottom: 0;">
<li>{t("search_string.full_text_search")}</li>
<li><code>#abc</code> - {t("search_string.label_abc")}</li>
<li><code>#year = 2019</code> - {t("search_string.label_year")}</li>
<li><code>#rock #pop</code> - {t("search_string.label_rock_pop")}</li>
<li><code>#rock or #pop</code> - {t("search_string.label_rock_or_pop")}</li>
<li><code>#year &lt;= 2000</code> - {t("search_string.label_year_comparison")}</li>
<li><code>note.dateCreated &gt;= MONTH-1</code> - {t("search_string.label_date_created")}</li>
</ul>
</>}
note={note} {...restProps}
>
<FormTextArea
inputRef={inputRef}
className="search-string"
placeholder={t("search_string.placeholder")}
currentValue={searchString ?? ""}
onChange={text => {
currentValue.current = text;
spacedUpdate.scheduleUpdate();
}}
onKeyDown={async (e) => {
if (e.key === "Enter") {
e.preventDefault();
// this also in effect disallows new lines in query string.
// on one hand, this makes sense since search string is a label
// on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
await spacedUpdate.updateNowIfNecessary();
refreshResults();
}
}}
/>
</SearchOption>;
// this also in effect disallows new lines in query string.
// on one hand, this makes sense since search string is a label
// on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
await spacedUpdate.updateNowIfNecessary();
refreshResults();
}
}}
/>
</SearchOption>
{error?.message && (
<tr>
<td colspan={3}>
<Admonition type="caution">{error.message}</Admonition>
</td>
</tr>
)}
</>;
}
function SearchScriptOption({ note, ...restProps }: SearchOptionProps) {

View File

@@ -4,6 +4,12 @@
width: 100%;
border-collapse: separate;
border-spacing: 10px;
.admonition {
margin-top: 0.25em;
margin-bottom: 1em;
text-wrap: wrap;
}
}
.search-setting-table div {
@@ -141,20 +147,26 @@ body.mobile .search-definition-widget {
gap: 0.5em;
}
.search-setting-table tr.searchString td:nth-of-type(2) {
flex-grow: 1;
}
.search-setting-table tr.searchString {
td:nth-of-type(2) {
flex-grow: 1;
}
.search-setting-table tr.searchString .button-column {
flex-grow: 0;
flex-shrink: 0;
width: 64px;
.button-column {
flex-grow: 0;
flex-shrink: 0;
width: 64px;
}
&.has-error {
border-bottom: 0;
}
}
.search-setting-table tr.ancestor > td > div {
flex-direction: column;
align-items: flex-start !important;
}
}
.search-actions tr {
border-bottom: 0;
@@ -171,4 +183,4 @@ body.mobile .search-definition-widget {
overflow: unset;
height: unset !important;
}
}
}

View File

@@ -2,6 +2,7 @@ import "./SearchDefinitionTab.css";
import { SaveSearchNoteResponse } from "@triliumnext/commons";
import { useContext, useEffect, useState } from "preact/hooks";
import { Fragment } from "preact/jsx-runtime";
import appContext from "../../components/app_context";
import FNote from "../../entities/fnote";
@@ -15,12 +16,13 @@ import tree from "../../services/tree";
import { getErrorMessage } from "../../services/utils";
import ws from "../../services/ws";
import RenameNoteBulkAction from "../bulk_actions/note/rename_note";
import Button from "../react/Button";
import Button, { SplitButton } from "../react/Button";
import Dropdown from "../react/Dropdown";
import { FormListHeader, FormListItem } from "../react/FormList";
import { useTriliumEvent } from "../react/hooks";
import Icon from "../react/Icon";
import { ParentComponent } from "../react/react_utils";
import ResponsiveContainer from "../react/ResponsiveContainer";
import { TabContext } from "./ribbon-interface";
import { SEARCH_OPTIONS, SearchOption } from "./SearchDefinitionOptions";
@@ -84,15 +86,32 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
<tr>
<td className="title-column">{t("search_definition.add_search_option")}</td>
<td colSpan={2} className="add-search-option">
{searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }) => (
<Button
size="small"
icon={icon}
text={label}
title={tooltip}
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
/>
))}
<ResponsiveContainer
desktop={searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }) => (
<Button
key={`${attributeType}-${attributeName}`}
size="small" icon={icon} text={label} title={tooltip}
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
/>
))}
mobile={
<Dropdown
buttonClassName="action-add-toggle btn btn-sm"
text={<><Icon icon="bx bx-plus" />{" "}{t("search_definition.option")}</>}
dropdownContainerClassName="mobile-bottom-menu" mobileBackdrop
noSelectButtonStyle
>
{searchOptions?.availableOptions.map(({ icon, label, tooltip, attributeName, attributeType, defaultValue }) => (
<FormListItem
key={`${attributeType}-${attributeName}`}
icon={icon}
description={tooltip}
onClick={() => attributes.setAttribute(note, attributeType, attributeName, defaultValue ?? "")}
>{label}</FormListItem>
))}
</Dropdown>
}
/>
<AddBulkActionButton note={note} />
</td>
@@ -113,48 +132,7 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
})}
</tbody>
<BulkActionsList note={note} />
<tbody className="search-actions">
<tr>
<td colSpan={3}>
<div className="search-actions-container">
<Button
icon="bx bx-search"
text={t("search_definition.search_button")}
keyboardShortcut="Enter"
onClick={refreshResults}
/>
<Button
icon="bx bxs-zap"
text={t("search_definition.search_execute")}
onClick={async () => {
await server.post(`search-and-execute-note/${note.noteId}`);
refreshResults();
toast.showMessage(t("search_definition.actions_executed"), 3000);
}}
/>
{note.isHiddenCompletely() && <Button
icon="bx bx-save"
text={t("search_definition.save_to_note")}
onClick={async () => {
const { notePath } = await server.post<SaveSearchNoteResponse>("special-notes/save-search-note", { searchNoteId: note.noteId });
if (!notePath) {
return;
}
await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext()?.setNote(notePath);
// Note the {{- notePathTitle}} in json file is not typo, it's unescaping
// See https://www.i18next.com/translation-function/interpolation#unescape
toast.showMessage(t("search_definition.search_note_saved", { notePathTitle: await tree.getNotePathTitle(notePath) }));
}}
/>}
</div>
</td>
</tr>
</tbody>
<SearchButtonBar note={note} refreshResults={refreshResults} />
</table>
)}
</div>
@@ -162,6 +140,56 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
);
}
function SearchButtonBar({ note, refreshResults }: {
note: FNote;
refreshResults(): void;
}) {
async function searchAndExecuteActions() {
await server.post(`search-and-execute-note/${note.noteId}`);
refreshResults();
toast.showMessage(t("search_definition.actions_executed"), 3000);
}
async function saveSearchNote() {
const { notePath } = await server.post<SaveSearchNoteResponse>("special-notes/save-search-note", { searchNoteId: note.noteId });
if (!notePath) return;
await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext()?.setNote(notePath);
// Note the {{- notePathTitle}} in json file is not typo, it's unescaping
// See https://www.i18next.com/translation-function/interpolation#unescape
toast.showMessage(t("search_definition.search_note_saved", { notePathTitle: await tree.getNotePathTitle(notePath) }));
}
return (
<tbody className="search-actions">
<tr>
<td colSpan={3}>
<ResponsiveContainer
desktop={
<div className="search-actions-container">
<Button icon="bx bx-search" text={t("search_definition.search_button")} keyboardShortcut="Enter" onClick={refreshResults} />
<Button icon="bx bxs-zap" text={t("search_definition.search_execute")} onClick={searchAndExecuteActions} />
{note.isHiddenCompletely() && <Button icon="bx bx-save" text={t("search_definition.save_to_note")} onClick={saveSearchNote} />}
</div>
}
mobile={
<SplitButton
text={t("search_definition.search_button")} icon="bx bx-search"
onClick={refreshResults}
>
<FormListItem icon="bx bxs-zap" onClick={searchAndExecuteActions}>{t("search_definition.search_execute")}</FormListItem>
{note.isHiddenCompletely() && <FormListItem icon="bx bx-save" onClick={saveSearchNote}>{t("search_definition.save_to_note")}</FormListItem>}
</SplitButton>
}
/>
</td>
</tr>
</tbody>
);
}
function BulkActionsList({ note }: { note: FNote }) {
const [ bulkActions, setBulkActions ] = useState<RenameNoteBulkAction[]>();
@@ -194,15 +222,18 @@ function AddBulkActionButton({ note }: { note: FNote }) {
buttonClassName="action-add-toggle btn btn-sm"
text={<><Icon icon="bx bxs-zap" />{" "}{t("search_definition.action")}</>}
noSelectButtonStyle
dropdownContainerClassName="mobile-bottom-menu" mobileBackdrop
>
{ACTION_GROUPS.map(({ actions, title }) => (
<>
{ACTION_GROUPS.map(({ actions, title }, index) => (
<Fragment key={index}>
<FormListHeader text={title} />
{actions.map(({ actionName, actionTitle }) => (
<FormListItem onClick={() => bulk_action.addAction(note.noteId, actionName)}>{actionTitle}</FormListItem>
))}
</>
<div>
{actions.map(({ actionName, actionTitle }) => (
<FormListItem key={actionName} onClick={() => bulk_action.addAction(note.noteId, actionName)}>{actionTitle}</FormListItem>
))}
</div>
</Fragment>
))}
</Dropdown>
);

View File

@@ -0,0 +1,4 @@
.similar-notes-widget > .similar-notes-wrapper {
/* The font size of the links with the highest similarity score */
font-size: 17px;
}

View File

@@ -1,3 +1,5 @@
import "./SimilarNotesTab.css";
import { SimilarNoteResponse } from "@triliumnext/commons";
import { useEffect, useState } from "preact/hooks";
@@ -33,7 +35,7 @@ export default function SimilarNotesTab({ note }: Pick<TabContext, "note">) {
notePath={notePath}
noTnLink
style={{
"font-size": 20 * (1 - 1 / (1 + score))
"font-size": (1 - 1 / (1 + score)) + "em"
}}
/>
))}

View File

@@ -6,10 +6,16 @@
.attachment-list .links-wrapper {
font-size: larger;
margin-bottom: 15px;
margin-block: 12px;
display: flex;
justify-content: space-between;
align-items: baseline;
@media (max-width: 991px) {
margin-block: 1em;
flex-direction: column;
gap: 0.5em;
}
}
/* #endregion */
@@ -42,6 +48,12 @@
display: flex;
align-items: center;
gap: 1em;
@media (max-width: 991px) {
gap: 0.5em;
flex-wrap: wrap;
.attachment-title { flex-grow: 1; }
}
}
.attachment-details {
@@ -127,10 +139,6 @@
/* #region Attachment actions */
.attachment-actions .dropdown-menu {
width: 20em;
}
.attachment-actions .dropdown-item .tn-icon {
position: relative;
top: 3px;

View File

@@ -239,6 +239,8 @@ function AttachmentActions({ attachment, copyAttachmentLinkToClipboard }: { atta
text={<Icon icon="bx bx-dots-vertical-rounded" />}
buttonClassName="icon-action-always-border"
iconAction
dropdownContainerClassName="mobile-bottom-menu"
mobileBackdrop
>
<FormListItem
icon="bx bx-file-find"

View File

@@ -1,16 +1,7 @@
.type-contentWidget .note-detail {
height: 100%;
}
.note-detail-content-widget {
height: 100%;
}
.note-detail-content-widget-content {
padding: 15px;
height: 100%;
}
.note-detail.full-height .note-detail-content-widget-content {
padding: 0;
}
}

View File

@@ -34,6 +34,7 @@ const LOCALE_MAPPINGS: Record<DISPLAYABLE_LOCALE_IDS, Options["locale"] | null>
"en-GB": "en",
es: "es",
fr: "fr",
ga: null,
it: "it",
ja: "ja",
pt: "pt",

View File

@@ -1,9 +1,6 @@
.protected-session-password-component {
width: 300px;
margin: 30px auto auto;
}
.protected-session-password-component input,
.protected-session-password-component button {
margin-top: 12px;
display: flex;
margin-inline: 40px;
flex-direction: column;
justify-content: center;
}

View File

@@ -20,7 +20,9 @@ export default function ProtectedSession() {
}, [ passwordRef ]);
return (
<form class="protected-session-password-form" onSubmit={submitCallback}>
<form class="protected-session-password-form tn-centered-form" onSubmit={submitCallback}>
<span class="form-icon bx bx-key" />
<FormGroup name="protected-session-password-in-detail" label={t("protected_session.enter_password_instruction")}>
<FormTextBox
type="password"
@@ -37,4 +39,4 @@ export default function ProtectedSession() {
/>
</form>
)
}
}

View File

@@ -16,3 +16,20 @@
width: 100%;
height: 100%;
}
.web-view-setup-form {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding-inline: 40px;
.form-icon {
margin-bottom: 12px;
}
.form-group {
width: 100%;
max-width: 600px;
}
}

View File

@@ -1,9 +1,13 @@
import { useCallback, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
import utils from "../../services/utils";
import Alert from "../react/Alert";
import { useNoteLabel } from "../react/hooks";
import { TypeWidgetProps } from "./type_widget";
import "./WebView.css";
import FormGroup from "../react/FormGroup";
import toast from "../../services/toast";
import Button from "../react/Button";
const isElectron = utils.isElectron();
@@ -12,7 +16,7 @@ export default function WebView({ note }: TypeWidgetProps) {
return (webViewSrc
? <WebViewContent src={webViewSrc} />
: <WebViewHelp />
: <SetupWebView note={note} />
);
}
@@ -24,12 +28,41 @@ function WebViewContent({ src }: { src: string }) {
}
}
function WebViewHelp() {
return (
<Alert className="note-detail-web-view-help" type="warning">
<h4>{t("web_view.web_view")}</h4>
<p>{t("web_view.embed_websites")}</p>
<p>{t("web_view.create_label")}</p>
</Alert>
)
function SetupWebView({note}: {note: FNote}) {
const [srcLabel, setSrcLabel] = useNoteLabel(note, "webViewSrc");
const [src, setSrc] = useState("");
const submit = useCallback((url: string) => {
try {
// Validate URL
new URL(url);
} catch (ex) {
toast.showErrorTitleAndMessage(t("web_view_setup.invalid_url_title"),
t("web_view_setup.invalid_url_message"));
return;
}
setSrcLabel(url);
}, [note]);
return <div class="web-view-setup-form">
<form class="tn-centered-form" onSubmit={() => submit(src)}>
<span className="bx bx-globe-alt form-icon" />
<FormGroup name="web-view-src-detail" label={t("web_view_setup.title")}>
<input className="form-control"
type="text"
value={src}
placeholder={t("web_view_setup.url_placeholder")}
onChange={(e) => {setSrc((e.target as HTMLInputElement)?.value)}}
/>
</FormGroup>
<Button
text={t("web_view_setup.create_button")}
primary
keyboardShortcut="Enter"
/>
</form>
</div>
}

View File

@@ -2,6 +2,7 @@ import { LOCALES } from "@triliumnext/commons";
import { readdirSync } from "fs";
import { join } from "path";
import { describe, expect, it } from "vitest";
import { LANGUAGE_MAPPINGS } from "./i18n.js";
const localeDir = join(__dirname, "../../../../../../node_modules/@excalidraw/excalidraw/dist/prod/locales");
@@ -21,9 +22,9 @@ describe("Canvas i18n", () => {
for (const locale of LOCALES) {
if (locale.contentOnly || locale.devOnly) continue;
const languageCode = LANGUAGE_MAPPINGS[locale.id];
if (!supportedLanguageCodes.has(languageCode)) {
if (languageCode && !supportedLanguageCodes.has(languageCode)) {
console.log("Supported locales:", Array.from(supportedLanguageCodes.values()).join(", "));
expect.fail(`Unable to find locale for ${locale.id} -> ${languageCode}.`)
expect.fail(`Unable to find locale for ${locale.id} -> ${languageCode}.`);
}
}
});

View File

@@ -10,6 +10,7 @@ export const LANGUAGE_MAPPINGS: Record<DISPLAYABLE_LOCALE_IDS, Language["code"]
en_rtl: "en",
es: "es-ES",
fr: "fr-FR",
ga: null,
it: "it-IT",
ja: "ja-JP",
pt: "pt-PT",

View File

@@ -388,10 +388,10 @@ function Performance() {
currentValue={shadowsEnabled} onChange={setShadowsEnabled}
/>
<FormCheckbox
{!isMobile() && <FormCheckbox
label={t("ui-performance.enable-backdrop-effects")}
currentValue={backdropEffectsEnabled} onChange={setBackdropEffectsEnabled}
/>
/>}
{isElectron() && <SmoothScrollEnabledOption />}

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