Compare commits

...

1011 Commits

Author SHA1 Message Date
perf3ct
66074ddbc9 fix(docs): try to fix more docs links... 2025-09-07 22:30:10 -07:00
Elian Doran
61ec341c27 Revert "fix: close context menu when clicking items with submenus"
This reverts commit 2f93af4d6f.
2025-09-07 22:57:27 +03:00
Elian Doran
22835108be fix: add left-click check for tree button handlers (#6903) 2025-09-07 22:53:42 +03:00
SiriusXT
617548f6b6 Merge branch 'main' into sirius_tree_patch 2025-09-07 19:18:23 +08:00
SiriusXT
2f93af4d6f fix: close context menu when clicking items with submenus 2025-09-07 17:15:09 +08:00
Elian Doran
145f89eded fix(shortcuts): try to fix ime composition checks (#6851) 2025-09-07 11:17:35 +03:00
Elian Doran
6c0e4b6a48 Merge branch 'main' into fix/ime-shortcut-input-fix 2025-09-07 11:13:18 +03:00
Elian Doran
87d1eefc86 chore(deps): update softprops/action-gh-release action to v2.3.3 (#6915) 2025-09-07 11:07:52 +03:00
Elian Doran
a87ec6f2e7 Translations update from Hosted Weblate (#6916) 2025-09-07 11:01:48 +03:00
donut
a9d5478bcd Translated using Weblate (Polish)
Currently translated at 37.8% (143 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/pl/
2025-09-07 09:59:34 +02:00
Elian Doran
5eae51a1b4 Apply note wrapper on mobile (#6847) 2025-09-07 10:59:28 +03:00
Elian Doran
ac94ab6914 feat: Make splits resizable (#6866) 2025-09-07 10:48:37 +03:00
Elian Doran
38673a85c9 feat: show source diff between note and revision (#6887) 2025-09-07 10:42:21 +03:00
renovate[bot]
d75951c869 chore(deps): update softprops/action-gh-release action to v2.3.3 2025-09-07 07:24:01 +00:00
Elian Doran
67d36a9e28 fix(deps): update dependency mind-elixir to v5.1.1 (#6914) 2025-09-07 10:02:35 +03:00
renovate[bot]
de8e8915ff fix(deps): update dependency mind-elixir to v5.1.1 2025-09-07 01:22:19 +00:00
Elian Doran
2161816ef4 fix(deps): update ckeditor monorepo to v46.0.3 (#6895) 2025-09-06 23:15:09 +03:00
renovate[bot]
d046bdec65 fix(deps): update ckeditor monorepo to v46.0.3 2025-09-06 20:05:18 +00:00
Elian Doran
cddd7d1562 fix(deps): update dependency ckeditor5 to v46.0.3 [security] (#6885) 2025-09-06 23:01:30 +03:00
renovate[bot]
15a3104904 fix(deps): update dependency ckeditor5 to v46.0.3 [security] 2025-09-06 19:53:33 +00:00
Elian Doran
e1ca6eca0f chore(deps): update dependency vite to v7.1.4 (#6893) 2025-09-06 22:52:01 +03:00
Elian Doran
cc0137bdc9 fix(deps): update dependency @mermaid-js/layout-elk to v0.2.0 (#6899) 2025-09-06 22:42:06 +03:00
Elian Doran
86a620bc08 Add an option to disable smooth scrolling for the Electron app (#6912) 2025-09-06 22:41:26 +03:00
Elian Doran
3ba9c3b4a8 Merge branch 'main' into history_diff 2025-09-06 22:37:55 +03:00
Elian Doran
8dc5ada553 Style: improve window background effects (#6913) 2025-09-06 22:36:59 +03:00
Elian Doran
9fe744c545 chore(deps): update actions/setup-node action to v5 (#6902) 2025-09-06 22:36:15 +03:00
Elian Doran
cc29eb0f9b chore(deps): update svelte monorepo (#6892) 2025-09-06 22:27:17 +03:00
renovate[bot]
901edde634 chore(deps): update dependency vite to v7.1.4 2025-09-06 19:24:39 +00:00
Elian Doran
44dd6d499d fix(deps): update dependency eslint-linter-browserify to v9.35.0 (#6905) 2025-09-06 22:24:05 +03:00
renovate[bot]
375f09cbaf fix(deps): update dependency @mermaid-js/layout-elk to v0.2.0 2025-09-06 19:23:04 +00:00
Elian Doran
d4e5a31de4 chore(deps): update tailwindcss monorepo to v4.1.13 (#6894) 2025-09-06 22:22:25 +03:00
Elian Doran
f3fa3864b2 chore(deps): update dependency node to v22 (#6891) 2025-09-06 22:21:44 +03:00
Elian Doran
1c978c2497 chore(deps): update dependency @types/leaflet-gpx to v1.3.8 (#6890) 2025-09-06 22:21:05 +03:00
Elian Doran
0f4ec2b3e2 chore(deps): update dependency @anthropic-ai/sdk to v0.61.0 (#6896) 2025-09-06 22:20:02 +03:00
Elian Doran
e30b1abaa4 chore(deps): update dependency @smithy/middleware-retry to v4.2.0 (#6897) 2025-09-06 22:19:26 +03:00
renovate[bot]
16cbee1fb2 chore(deps): update svelte monorepo 2025-09-06 19:18:48 +00:00
Elian Doran
56932f2b56 chore(deps): update dependency express-rate-limit to v8.1.0 (#6898) 2025-09-06 22:18:40 +03:00
Elian Doran
6a8f6b8370 fix(deps): update dependency i18next to v25.5.2 (#6900) 2025-09-06 22:17:19 +03:00
Elian Doran
7fa8e65015 fix(deps): update dependency mermaid to v11.11.0 (#6901) 2025-09-06 22:16:53 +03:00
Elian Doran
4f50b8c7d5 fix(deps): update eslint monorepo to v9.35.0 (#6907) 2025-09-06 22:15:36 +03:00
Elian Doran
eca85d9978 chore(deps): update actions/setup-python action to v6 (#6909) 2025-09-06 22:15:10 +03:00
Elian Doran
f0ea2eb39b fix(deps): update dependency force-graph to v1.51.0 (#6906) 2025-09-06 22:14:42 +03:00
Elian Doran
4c5b229680 chore(deps): update actions/github-script action to v8 (#6908) 2025-09-06 22:13:07 +03:00
Elian Doran
83251cbc43 Merge branch 'main' into feat/performance/disable-smooth-scroll 2025-09-06 22:12:19 +03:00
Adorian Doran
c2f20cce32 Merge branch 'main' into feat/electron-app/background-effects-improvements 2025-09-06 22:02:47 +03:00
Adorian Doran
ec5ab44519 style/background effects: tweak launcher pane colors 2025-09-06 22:00:16 +03:00
renovate[bot]
ed6d21a05a chore(deps): update dependency node to v22 2025-09-06 18:58:17 +00:00
renovate[bot]
a2f3913fe5 chore(deps): update actions/setup-python action to v6 2025-09-06 18:57:17 +00:00
renovate[bot]
d66c0ef308 chore(deps): update actions/setup-node action to v5 2025-09-06 18:57:14 +00:00
renovate[bot]
0f9f6746ed chore(deps): update actions/github-script action to v8 2025-09-06 18:57:10 +00:00
renovate[bot]
9b534a0dc1 fix(deps): update eslint monorepo to v9.35.0 2025-09-06 18:57:07 +00:00
renovate[bot]
1ce73c1238 fix(deps): update dependency mermaid to v11.11.0 2025-09-06 18:56:11 +00:00
renovate[bot]
3b5b7ca01d fix(deps): update dependency i18next to v25.5.2 2025-09-06 18:55:40 +00:00
renovate[bot]
ce64a7816d fix(deps): update dependency force-graph to v1.51.0 2025-09-06 18:55:09 +00:00
renovate[bot]
37e095a93c fix(deps): update dependency eslint-linter-browserify to v9.35.0 2025-09-06 18:54:39 +00:00
Adorian Doran
300f6a103f style/background effects: tweak launcher pane colors 2025-09-06 21:53:47 +03:00
renovate[bot]
e7cb5a6b92 chore(deps): update dependency express-rate-limit to v8.1.0 2025-09-06 18:53:07 +00:00
renovate[bot]
67296fabf7 chore(deps): update dependency @smithy/middleware-retry to v4.2.0 2025-09-06 18:52:37 +00:00
renovate[bot]
d868f7fb26 chore(deps): update dependency @anthropic-ai/sdk to v0.61.0 2025-09-06 18:52:08 +00:00
renovate[bot]
1555d98f7d chore(deps): update tailwindcss monorepo to v4.1.13 2025-09-06 18:51:05 +00:00
renovate[bot]
3a02941b38 chore(deps): update dependency @types/leaflet-gpx to v1.3.8 2025-09-06 18:49:37 +00:00
Elian Doran
f25de1ffbe chore(ci): bring back typecheck 2025-09-06 21:43:48 +03:00
Adorian Doran
008e90324f style/background effects: tweak launcher pane colors 2025-09-06 21:43:09 +03:00
Adorian Doran
73dcc2eb26 style/background effects: convert the tree action button background color to a transparent color 2025-09-06 21:32:31 +03:00
Adorian Doran
eae2540a31 style/background effects: convert the tree item hover color to a transparent color 2025-09-06 21:21:43 +03:00
Adorian Doran
2a7fc8edb6 style/background effects: extract color overrides as theme variables 2025-09-06 21:13:09 +03:00
Adorian Doran
25698f5d9b electron app: display the smooth scrolling setting only on the Electron app 2025-09-06 19:31:45 +03:00
Adorian Doran
c729731c7e electron app: mention that a restart is required for the smooth scrolling setting to take effect 2025-09-06 19:24:32 +03:00
Adorian Doran
dcc2f28079 electron app: add Romanian translation 2025-09-06 19:05:20 +03:00
Adorian Doran
97aa00e18b electron app: add an option to disable smooth scrolling 2025-09-06 19:00:45 +03:00
Elian Doran
5d8f789791 fix(desktop): background effects causing issues on Win10 2025-09-06 17:47:13 +03:00
Elian Doran
4faabb7770 Translations update from Hosted Weblate (#6911) 2025-09-06 17:34:54 +03:00
donut
8f9b3df681 Translated using Weblate (Polish)
Currently translated at 37.3% (141 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/pl/
2025-09-06 14:08:09 +00:00
Mik Piet
449575e0f7 Translated using Weblate (Polish)
Currently translated at 7.3% (115 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pl/
2025-09-06 14:08:08 +00:00
donut
b7d47779d6 Translated using Weblate (Polish)
Currently translated at 7.3% (115 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pl/
2025-09-06 14:08:08 +00:00
Elian Doran
fe443c8a89 fix(next): window border cut-off on macOS 2025-09-06 17:07:56 +03:00
perf3ct
3bf1a77381 feat(docs): also deploy docs upon README change 2025-09-05 19:52:03 +00:00
perf3ct
c4d430c62d feat(docs): use readme as index.md in mkdocs deployment 2025-09-05 19:40:59 +00:00
perf3ct
d583ee2de3 feat(docs): just remove the other language READMEs from the hide list 2025-09-05 19:34:54 +00:00
perf3ct
406a381ef4 feat(docs): try to continue fixing the links in the mkdocs 2025-09-05 19:18:14 +00:00
Elian Doran
1d82308c43 feat(docs): create docs.triliumnotes.org as additional place to serve user-facing docs (#6889) 2025-09-05 21:49:36 +03:00
perf3ct
5c1595b1fd feat(docs): remove unused python 2025-09-05 15:15:12 +00:00
perf3ct
667cfb999b feat(docs): oops forgot to add it to the package.json 2025-09-05 15:11:48 +00:00
perf3ct
9b0e817635 feat(docs): transition from python to ts 2025-09-05 15:09:27 +00:00
SiriusXT
7f3c34178b fix: add left-click check for tree button handlers 2025-09-05 19:32:42 +08:00
perf3ct
e8ca443697 feat(docs): try to fix local doc links 2025-09-04 22:16:43 -07:00
perf3ct
94089113ef feat(docs): try to handle moved files too in script 2025-09-04 21:55:28 -07:00
perf3ct
1847fc2060 feat(docs): fix nav and scripts 2025-09-04 23:50:22 +00:00
perf3ct
7ca21b52a0 feat(docs): fix references to zadam 2025-09-04 23:46:50 +00:00
perf3ct
444beb4908 feat(docs): get images to work now 2025-09-04 23:39:40 +00:00
perf3ct
791869ca9e feat(docs): try to capture all pages 2025-09-04 23:30:20 +00:00
perf3ct
33c8406b8a feat(docs): try to make pnpm happy for mkdocs
asfd

asdf
2025-09-04 21:57:26 +00:00
perf3ct
b6212c4e98 feat(docs): try to get wrangler to work...
feat(docs)asdf

asdf
2025-09-04 21:32:32 +00:00
perf3ct
fcd2409ee3 feat(docs): try to get mkdocs to work again 2025-09-04 21:20:01 +00:00
perf3ct
dad060d0c9 feat(docs): let's try to deploy our stuff to mkdocs 2025-09-04 21:13:12 +00:00
SiriusXT
5e572a8c6a fix: remove unnecessary line breaks 2025-09-04 22:07:04 +08:00
SiriusXT
c60c738c7e feat: show source diff between note and revision 2025-09-04 21:34:13 +08:00
SiriusXT
1c451fb98a fix: adapt diff highlight for dark theme 2025-09-04 18:47:14 +08:00
SiriusXT
7eeb43a83b Merge branch 'main' into history_diff 2025-09-04 17:37:00 +08:00
SiriusXT
fa2188f087 fix: improve <pre> tag regex handling when formatting HTML strings 2025-09-04 17:36:19 +08:00
Elian Doran
df1b87e3ac chore(dx/nix): fix flake partially 2025-09-04 12:12:23 +03:00
Elian Doran
62a0a44049 chore(dx): have electron-rebuild read from path on NixOS too 2025-09-04 09:26:16 +03:00
SiriusXT
0ae25d2212 feat: show source diff between note and revision 2025-09-04 10:53:46 +08:00
Elian Doran
88aa76bcab Improve development experience (#6842) 2025-09-03 22:13:17 +03:00
Elian Doran
401120fa28 Merge remote-tracking branch 'origin/main' into feature/dx_improvement 2025-09-03 21:24:51 +03:00
Elian Doran
534113b303 fix(dx/share): ckcontent for share theme not preserved 2025-09-03 21:09:56 +03:00
Elian Doran
53df7835d3 Revert "chore(dx/server): remove dependency on CKEditor for now"
This reverts commit 4739e2e3b2.
2025-09-03 20:24:23 +03:00
Elian Doran
ee9afb7fa0 fix(next): 1px border on tab when background effects are on 2025-09-03 20:09:19 +03:00
Elian Doran
6163ab8c42 refactor(dx): remove unused .env files 2025-09-03 20:08:29 +03:00
Elian Doran
e73724a576 chore(dx/desktop): integrate e2e tests in same project 2025-09-03 20:08:17 +03:00
Elian Doran
e71284d887 chore(dx): get rid of references to NX 2025-09-03 18:23:47 +03:00
Elian Doran
11d95b89e1 chore(dx/edit-docs): de-nxify 2025-09-03 18:16:03 +03:00
Elian Doran
ee7052ebc2 chore(deps): update dependency dotenv to v17.2.2 (#6876) 2025-09-03 18:02:23 +03:00
renovate[bot]
9059642738 chore(deps): update dependency dotenv to v17.2.2 2025-09-03 14:14:24 +00:00
Elian Doran
fe8e3b4489 chore(deps): update svelte monorepo (#6874) 2025-09-03 17:11:58 +03:00
Elian Doran
4e00e5b995 chore(deps): update dependency dotenv to v17.2.2 (#6875) 2025-09-03 17:11:45 +03:00
Elian Doran
05ae0ca9d7 chore(deps): update dependency @anthropic-ai/sdk to v0.61.0 (#6877) 2025-09-03 17:11:38 +03:00
Elian Doran
ac0116109b chore(deps): update typescript-eslint monorepo to v8.42.0 (#6878) 2025-09-03 17:11:28 +03:00
renovate[bot]
710ed9dd0e chore(deps): update svelte monorepo 2025-09-03 12:45:44 +00:00
Elian Doran
c75d2435fa fix(dx/share): ckcontent missing 2025-09-03 15:23:32 +03:00
Elian Doran
050aa40e20 fix(dx/share): templates and script not accessible 2025-09-03 12:12:01 +03:00
Elian Doran
cb6d87302d fix(dx/client): doc notes not working 2025-09-03 12:00:06 +03:00
Elian Doran
f9e725bcf8 chore(dx): add aliases to desktop 2025-09-03 11:56:44 +03:00
Elian Doran
a56d622df7 chore(dx): address self-review 2025-09-03 10:55:40 +03:00
Adorian Doran
267f5105b2 Theme tweaks (#6832) 2025-09-03 10:53:57 +03:00
SiriusXT
4e2ffad70d Merge branch 'main' into siriusxt_split 2025-09-03 14:05:08 +08:00
Elian Doran
7db3bde933 chore(e2e): merge .env in playwright config + add retry 2025-09-03 09:02:10 +03:00
Adorian Doran
a9564f8f38 style/launchbar buttons: fix broken hover state when background effects are enabled 2025-09-03 05:25:26 +03:00
Adorian Doran
9c5a130ab4 style/text editor/forms: restyle text areas 2025-09-03 04:38:37 +03:00
Adorian Doran
c40398df5d style/text editor/forms: tweak text boxes 2025-09-03 04:29:02 +03:00
Adorian Doran
27fdd9e715 style/text editor/find and replace: add style for the "replace" buttons 2025-09-03 04:13:29 +03:00
renovate[bot]
59697095b1 chore(deps): update typescript-eslint monorepo to v8.42.0 2025-09-03 00:40:09 +00:00
renovate[bot]
a16f5f5505 chore(deps): update dependency @anthropic-ai/sdk to v0.61.0 2025-09-03 00:38:49 +00:00
renovate[bot]
922d484a33 chore(deps): update dependency dotenv to v17.2.2 2025-09-03 00:37:18 +00:00
Elian Doran
f63f24ac9d feat(server/e2e): upload test report if it fails 2025-09-02 22:51:57 +03:00
Elian Doran
e7521fe30c chore(server/e2e): increase timeout of a flaky test 2025-09-02 22:30:09 +03:00
Elian Doran
f6579ac434 fix(e2e/server): data dir not working 2025-09-02 21:45:59 +03:00
Elian Doran
e1b4a0b720 fix(deps): update dependency @codemirror/view to v6.38.2 (#6860) 2025-09-02 21:33:12 +03:00
Elian Doran
9c43d661be fix(desktop): forge building for the wrong arch 2025-09-02 21:13:32 +03:00
Elian Doran
d2d8bff9f7 fix(e2e/server): wrong database dir 2025-09-02 21:06:41 +03:00
Elian Doran
a1beb13094 chore(ci): add logs for electron-forge flatpak build 2025-09-02 20:46:44 +03:00
Elian Doran
37d66848d6 Merge remote-tracking branch 'origin/main' into feature/dx_improvement
; Conflicts:
;	pnpm-lock.yaml
2025-09-02 20:43:43 +03:00
Elian Doran
991399fe4f Merge branch 'main' into renovate/codemirror 2025-09-02 20:41:41 +03:00
Elian Doran
27855456a0 chore(deps): update dependency lint-staged to v16.1.6 (#6856) 2025-09-02 20:40:55 +03:00
Elian Doran
0687ed9ec4 chore(deps): update dependency typedoc to v0.28.12 (#6857) 2025-09-02 20:40:42 +03:00
Elian Doran
632976e71f chore(deps): update pnpm to v10.15.1 (#6859) 2025-09-02 20:40:25 +03:00
Elian Doran
98addef614 fix(deps): update dependency dayjs to v1.11.18 (#6861) 2025-09-02 20:40:10 +03:00
Elian Doran
c72c9934b5 fix(deps): update dependency react-i18next to v15.7.3 (#6862) 2025-09-02 20:39:54 +03:00
Elian Doran
6362f24ae9 chore(deps): update dependency @stylistic/eslint-plugin to v5.3.1 (#6863) 2025-09-02 20:39:41 +03:00
Elian Doran
dc2d2fe25b chore(deps): update dependency vite to v7.1.4 (#6858) 2025-09-02 20:39:20 +03:00
renovate[bot]
bbc007e6cf chore(deps): update dependency vite to v7.1.4 2025-09-02 17:36:12 +00:00
Elian Doran
3dfd195630 chore(deps): update dependency @sveltejs/kit to v2.37.0 (#6864) 2025-09-02 20:34:24 +03:00
Elian Doran
e3f72baab3 chore(deps): update node.js to v22.19.0 (#6865) 2025-09-02 20:33:23 +03:00
Elian Doran
1bb19d0d9e Translations update from Hosted Weblate (#6871) 2025-09-02 20:31:45 +03:00
Микола Копитін
bc1b69a836 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-09-02 17:03:22 +00:00
Elian Doran
26c7f0b017 feat(dx/desktop): improve rebuilding experience on NixOS 2025-09-02 19:56:27 +03:00
Elian Doran
d058dbe9af chore(dx/desktop): clean up env 2025-09-02 19:56:18 +03:00
Elian Doran
c1c237402a chore(dx/desktop): clean up package.json 2025-09-02 19:49:36 +03:00
Elian Doran
bb20de6c24 chore(dx/env): remove unnecessary nx config 2025-09-02 19:44:38 +03:00
Elian Doran
8d7af7b01d chore(dx/server): de-nxify 2025-09-02 19:44:28 +03:00
Elian Doran
fd1c122cd4 chore(dx/apps): build db-compare & dump-db 2025-09-02 19:29:38 +03:00
Elian Doran
3925ba3eef chore(dx/ci): fix sequential/parallel tests 2025-09-02 19:16:49 +03:00
Elian Doran
4306072ca7 chore(dx/ci): sequential/parallel tests 2025-09-02 19:09:45 +03:00
Elian Doran
15fba23ad7 chore(dx/ci): denx-ify playwright 2025-09-02 19:00:49 +03:00
Elian Doran
04753226e5 chore(dx/ci): fix package command 2025-09-02 18:42:30 +03:00
Elian Doran
3fda97a9bd chore(dx/client): allocate more memory for the build 2025-09-02 18:38:50 +03:00
Elian Doran
26afdd105f chore(dx/server): get tests to run 2025-09-02 18:33:22 +03:00
Elian Doran
7c50251c37 chore(dx): clean up global package.json 2025-09-02 18:32:03 +03:00
Elian Doran
3de9d07769 chore: update lock 2025-09-02 17:54:05 +03:00
Elian Doran
d60899e362 chore(dx): remove unnecessary nx configs 2025-09-02 17:43:32 +03:00
Elian Doran
7c8019ac5b chore(dx/ci): get rid of nx-specific workflows 2025-09-02 17:38:24 +03:00
Elian Doran
1258d0cf7d chore(dx/desktop): read electron version from package.json 2025-09-02 17:37:57 +03:00
SiriusXT
2264369e9e feat: Make splits resizable 2025-09-02 22:05:26 +08:00
Elian Doran
e18a8556c1 chore(dx/ci): remove most references to NX, apart from unit test 2025-09-02 16:40:52 +03:00
SiriusXT
5436011f8e feat: Make splits resizable 2025-09-02 20:17:01 +08:00
Elian Doran
ce0fd3cec2 chore(dx/desktop): get forge to run 2025-09-02 13:59:09 +03:00
Elian Doran
bd349f5abc feat(dx/desktop): support raw NixOS via LD_LIBRARY_PATH injection 2025-09-02 12:18:22 +03:00
Elian Doran
7fdea613ff feat(dx/desktop): perfect way to override bettersqlite native 2025-09-02 11:50:58 +03:00
Elian Doran
16beeb2e88 fix(dx): broken imports after changing hoisting 2025-09-02 11:03:24 +03:00
Elian Doran
ae74f8ea83 feat(dx/desktop): isolate node_modules dependency 2025-09-02 10:45:42 +03:00
SiriusXT
88b748e67b Merge branch 'main' into siriusxt_split 2025-09-02 09:36:06 +08:00
SiriusXT
3254069999 feat: Make splits resizable 2025-09-02 09:28:53 +08:00
renovate[bot]
0bfa9f0c58 chore(deps): update node.js to v22.19.0 2025-09-02 01:18:18 +00:00
renovate[bot]
498ffa806d chore(deps): update dependency @sveltejs/kit to v2.37.0 2025-09-02 01:18:13 +00:00
renovate[bot]
9bfed2a80d chore(deps): update dependency @stylistic/eslint-plugin to v5.3.1 2025-09-02 01:17:37 +00:00
renovate[bot]
ec902c5762 fix(deps): update dependency react-i18next to v15.7.3 2025-09-02 01:17:32 +00:00
renovate[bot]
bb1d31f877 fix(deps): update dependency dayjs to v1.11.18 2025-09-02 01:16:58 +00:00
renovate[bot]
09f938fb72 fix(deps): update dependency @codemirror/view to v6.38.2 2025-09-02 01:16:21 +00:00
renovate[bot]
a3e9192998 chore(deps): update pnpm to v10.15.1 2025-09-02 01:15:40 +00:00
renovate[bot]
80b7c0b4c9 chore(deps): update dependency typedoc to v0.28.12 2025-09-02 01:14:46 +00:00
renovate[bot]
f01d6938f3 chore(deps): update dependency lint-staged to v16.1.6 2025-09-02 01:14:40 +00:00
Adorian Doran
ab95d707a3 style/text editor/forms: refactor 2025-09-02 02:43:11 +03:00
Adorian Doran
6475b4029a style/text editor/forms: restyle number inputs 2025-09-02 02:42:05 +03:00
Adorian Doran
f646b3dc5c style/text editor/color selector dropdown: fix layout 2025-09-02 02:14:43 +03:00
Adorian Doran
bcef0802e4 style/text editor/insert emoji flyout: fix the spacing of the skin tone dropdown items 2025-09-02 02:08:04 +03:00
Adorian Doran
47099cc77b style/text editor/insert math flyout: fix layout 2025-09-02 02:03:49 +03:00
Elian Doran
793102f3ad chore(dx/electron): fix tray icons 2025-09-01 22:55:53 +03:00
Elian Doran
6f29bdf355 chore(dx/electron): different window icon 2025-09-01 22:25:06 +03:00
Elian Doran
edf53c8a0f chore(dx/desktop): configure dev & start-prod 2025-09-01 21:16:10 +03:00
Elian Doran
24859e33c1 chore(dx/desktop): generate prod package.json 2025-09-01 21:15:44 +03:00
Elian Doran
ebcf4315f7 chore(dx/desktop): remote main not working in dist build 2025-09-01 21:15:17 +03:00
Elian Doran
135e2bb10e chore(dx/desktop): get prod build 2025-09-01 20:50:22 +03:00
Elian Doran
72a256eccf refactor(dx/server): simplify build script even further 2025-09-01 20:29:34 +03:00
Elian Doran
1e991c0526 refactor(dx/server): extract basic build commands to separate file 2025-09-01 19:36:14 +03:00
Elian Doran
978e6b9dde chore(dx/server): unnecessary import 2025-09-01 19:22:46 +03:00
perf3ct
a2acb3cbb7 fix(shortcuts): try to fix ime composition checks 2025-09-01 16:21:58 +00:00
Papierkorb2292
623fcce3d1 Also update note context in other note context events in note wrapper so it works with tabs 2025-09-01 11:33:15 +02:00
Papierkorb2292
c99ef4a549 Make note wrapper widget aware of note context on mobile 2025-09-01 11:01:29 +02:00
Papierkorb2292
c629ce6ef8 Add note wrapper widget in mobile_layout.tsx 2025-09-01 11:01:08 +02:00
Elian Doran
35743de0df fix(dx/client): client not starting due to duplicate config 2025-09-01 11:53:17 +03:00
Elian Doran
5cf182cf98 fix(dx/client): not serving Vite due to NODE_ENV 2025-09-01 11:50:41 +03:00
Elian Doran
01022546e8 fix(dx/client): insert math not working due to icon import 2025-09-01 11:33:18 +03:00
Elian Doran
83be42f4ea Translations update from Hosted Weblate (#6843) 2025-09-01 09:10:52 +03:00
Mik Piet
ab9fec0186 Translated using Weblate (Polish)
Currently translated at 7.0% (111 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pl/
2025-09-01 06:02:00 +02:00
Flowerlywind
c6dd32ea7b Translated using Weblate (Vietnamese)
Currently translated at 2.5% (40 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/vi/
2025-09-01 06:01:58 +02:00
Kuzma Simonov
1d4cd538ac Translated using Weblate (Russian)
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-09-01 06:01:57 +02:00
rodrigomescua
dc99f725f9 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-09-01 06:01:55 +02:00
Newcomer1989
2f804f3eac Translated using Weblate (German)
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-09-01 06:01:54 +02:00
Elian Doran
4b9688af04 chore(dx/server): minify output 2025-08-31 23:19:07 +03:00
Elian Doran
897b896c11 chore(dx/server): fix vite interfering in production 2025-08-31 23:12:52 +03:00
Elian Doran
3600b46824 chore(dx/server): fix missing path to client 2025-08-31 23:04:18 +03:00
Elian Doran
9266fe63b9 chore(dx/client): disable share CSS for now 2025-08-31 23:03:38 +03:00
Elian Doran
a3ea52968f chore(dx/client): fix codemirror dep 2025-08-31 23:03:30 +03:00
Elian Doran
a06f2aeb8b chore(dx/server): trigger build of client & copy artifacts 2025-08-31 23:03:21 +03:00
Elian Doran
f4a56d4e19 chore(dx/client): get vite build to work 2025-08-31 23:02:41 +03:00
Elian Doran
f3f7ff5622 chore(dx/server): copy share templates when building 2025-08-31 22:48:50 +03:00
Elian Doran
dbf016adaf chore(dx/server): build all entrypoints with right ext 2025-08-31 22:43:21 +03:00
Elian Doran
0e5108bd08 chore(dx/server): start building & copying assets 2025-08-31 22:30:07 +03:00
Elian Doran
cf1180faa9 chore(dx/server): remove babel compacting for tiny gain in perf 2025-08-31 21:05:23 +03:00
Elian Doran
1b25275b2e fix(dx/electron): web contents not working 2025-08-31 20:43:48 +03:00
Elian Doran
886c694db7 chore(dx/server): update server:start command 2025-08-31 20:43:33 +03:00
Elian Doran
3d38a2aa14 chore(dx/desktop): get dev mode for Electron 2025-08-31 20:43:20 +03:00
Elian Doran
51d879ba6f style(client): toast sometimes going out of bounds 2025-08-31 20:42:46 +03:00
Elian Doran
91ae9d75f7 Revert "chore(dx/server): improve asset management for DB init"
This reverts commit 42559364e4.
2025-08-31 20:31:51 +03:00
Elian Doran
42559364e4 chore(dx/server): improve asset management for DB init 2025-08-31 20:28:21 +03:00
Elian Doran
9d6bb306e7 fix(electron): history navigation context menu not working 2025-08-31 19:39:54 +03:00
Elian Doran
c92860ae49 chore(dx/client): fix error when optimizing premium plugins 2025-08-31 19:27:05 +03:00
Elian Doran
b012624b67 chore(dx/client): fix emoji import error 2025-08-31 19:26:56 +03:00
Elian Doran
5f1d2f02ee chore(dx/client): fix SVG icons causing errors in CKEditor 2025-08-31 19:26:47 +03:00
Elian Doran
46cb869237 chore(dx/server): client paths not correct 2025-08-31 19:21:36 +03:00
Elian Doran
054c497678 chore(dx/client): improve startup speed by properly configuring middleware 2025-08-31 18:57:31 +03:00
Elian Doran
8362424976 chore(dx/client): fix highlightjs not working 2025-08-31 18:52:52 +03:00
Elian Doran
f7a0dc00e8 chore(dx/client): fix bootstrap CSS imports 2025-08-31 18:52:22 +03:00
Elian Doran
e49c4655a6 chore(dx/client): ckeditor5 CSS imports 2025-08-31 18:51:18 +03:00
Elian Doran
1dcb3b1529 chore(dx/server): set up cache for Vite 2025-08-31 18:28:20 +03:00
Elian Doran
cc474f39d8 chore(dx/server): basic middleware integration for vite 2025-08-31 18:24:02 +03:00
Elian Doran
113d36f5dd chore(dx/client): set paths for client dependencies 2025-08-31 18:19:03 +03:00
Elian Doran
63c0841c32 chore(dx/server): get server to run up to missing public server 2025-08-31 16:58:57 +03:00
Elian Doran
4739e2e3b2 chore(dx/server): remove dependency on CKEditor for now 2025-08-31 16:52:32 +03:00
Elian Doran
aa316091e6 chore(dx): fix cannot read properties of undefined if DB dir is missing 2025-08-31 16:41:03 +03:00
Elian Doran
2297721228 chore(dx): get rid of nx 2025-08-31 16:36:55 +03:00
Adorian Doran
03ab912495 style/text editor/forms: tweak buttons 2025-08-31 03:33:30 +03:00
Adorian Doran
d12dfabd0b style/text editor/forms: various layout fixes 2025-08-31 03:19:33 +03:00
Adorian Doran
ed748bbebd style/text editor/forms: restyle dropdowns 2025-08-31 02:58:53 +03:00
Adorian Doran
e85858d22d style/text editor/forms: fix visible focus for buttons 2025-08-31 02:30:13 +03:00
Adorian Doran
0afa9717e5 style/text editor/forms: restyle buttons 2025-08-31 02:28:24 +03:00
Adorian Doran
1e2e3498c6 style/text editor/forms: restyle text boxes 2025-08-31 01:45:12 +03:00
Elian Doran
59a01b816c Translations update from Hosted Weblate (#6838) 2025-08-30 20:33:31 +03:00
Francis C
508f46af42 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hant/
2025-08-30 19:24:31 +02:00
Francis C
1af865a577 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-30 19:24:31 +02:00
Francis C
74834af222 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1566 of 1566 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-30 19:24:31 +02:00
Elian Doran
f55e33f303 refactor(note_tree): improve type safety 2025-08-30 20:02:32 +03:00
Adorian Doran
fcb77360e1 style/text editor/text alignment dropdown: use a horizontal toolbar instead of a vertical one 2025-08-30 19:22:27 +03:00
Adorian Doran
3d285e105e style/text editor/insert text snippet dropdown: tweak appearance 2025-08-30 18:25:44 +03:00
Elian Doran
bb55544f25 Translations update from Hosted Weblate (#6834) 2025-08-30 14:08:47 +03:00
Hosted Weblate
2e9e9a60bf Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/
2025-08-30 13:08:15 +02:00
Elian Doran
f7e77cd6cb fix(auth): add missing TOTP verification for /login/token (#6823) 2025-08-30 14:08:10 +03:00
Elian Doran
a7a94789e6 chore(server/tree): improve type safety 2025-08-30 14:06:35 +03:00
Elian Doran
864ac1a270 fix(tree): defend against stray null values that may occur when multi… (#6821) 2025-08-30 14:04:02 +03:00
Elian Doran
fbec6d8873 feat(react/widgets): port shared_info 2025-08-30 13:59:53 +03:00
Elian Doran
5f647a932d Port small widgets to React (#6830) 2025-08-30 12:51:31 +03:00
Elian Doran
6e5046c0d4 chore(react/widgets): fix import error 2025-08-30 12:16:29 +03:00
Elian Doran
3c9a8e38d3 feat(react/widgets): port close zen button 2025-08-30 12:04:31 +03:00
Elian Doran
b3a3196136 style(react/widgets): improve api log slightly 2025-08-30 11:36:47 +03:00
Elian Doran
3229b7d106 feat(react/widgets): port api_log 2025-08-30 11:31:49 +03:00
Elian Doran
4213c377f8 fix(react/widgets): alignment of shortcuts in context menu 2025-08-30 11:15:06 +03:00
Elian Doran
86365ebd44 feat(react/widgets): port left pane toggle 2025-08-30 10:29:03 +03:00
Elian Doran
20cf685174 Translations update from Hosted Weblate (#6833) 2025-08-30 09:32:32 +03:00
nvcutrb
aeb5a7b251 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1565 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-30 04:01:58 +02:00
Aitanuqui
47a50bb449 Translated using Weblate (Spanish)
Currently translated at 100.0% (1565 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2025-08-30 04:01:57 +02:00
nvcutrb
a2a5b67496 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1565 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-30 04:01:55 +02:00
Adorian Doran
a94cc5bdab style/text editor/insert text snippet dropdown: tweak appearance 2025-08-30 02:55:23 +03:00
Adorian Doran
526c5a6dd8 Merge branch 'main' of https://github.com/TriliumNext/Trilium into feat/theme/improvements 2025-08-30 01:06:02 +03:00
Elian Doran
3f5239706f feat(text_snippets): support color class as well 2025-08-29 23:41:46 +03:00
Elian Doran
d2761abd04 feat(text_snippets): display actual note icon 2025-08-29 23:39:40 +03:00
Elian Doran
57983b54d2 fix(react/widgets): electron imports breaking browser 2025-08-29 23:24:12 +03:00
Elian Doran
703cf8434a fix(react/widgets): unnecessary padding due to SQL schemas 2025-08-29 22:45:20 +03:00
Elian Doran
aa4375e25f feat(react/widgets): port mobile editor toolbar 2025-08-29 22:40:03 +03:00
Adorian Doran
7d3a672b55 style/toasts: prevent long text from overflowing 2025-08-29 21:36:09 +03:00
Adorian Doran
c08b30a060 style/text editor: hide icons from the text snippets dropdown 2025-08-29 21:30:04 +03:00
Elian Doran
d579e39b40 feat(react/widgets): port mobile detail menu 2025-08-29 21:18:34 +03:00
Adorian Doran
b147d4bdeb style: brighten the border of dropdowns 2025-08-29 21:17:43 +03:00
Adorian Doran
48faa8a813 client/quick search results: tweak the busy indicator 2025-08-29 21:06:48 +03:00
Elian Doran
ec646809dd feat(react/widgets): port toggle sidebar 2025-08-29 19:39:46 +03:00
Elian Doran
ab48a28635 refactor(react/widgets): typings for dynamic require + solve type errors 2025-08-29 19:29:15 +03:00
Elian Doran
3fd7afbb57 feat(react/widgets): port title bar buttons 2025-08-29 19:09:40 +03:00
Elian Doran
4074929c6b chore(react/global_menu): disable auto-show 2025-08-29 18:50:21 +03:00
Adorian Doran
d73e84ea6c client/quick search results: tweak icon alignment 2025-08-29 18:37:02 +03:00
Elian Doran
753f1dc7b6 feat(react/widgets): sql table schemas 2025-08-29 17:14:27 +03:00
Adorian Doran
9464667323 client/quick search results: remove deprecated styles 2025-08-29 17:12:52 +03:00
Adorian Doran
4d82f2f22d client/quick search results: tweak footer divider margins 2025-08-29 17:11:10 +03:00
Adorian Doran
e3d28e703f client/quick search results: tweak snippet background color 2025-08-29 17:01:08 +03:00
Adorian Doran
5f39a314b5 client/quick search results: fix overflowing snippets 2025-08-29 16:58:13 +03:00
Adorian Doran
43caadc472 client/quick search results: refactor the item delimiter line 2025-08-29 16:57:02 +03:00
Elian Doran
f2ce8b9f3c feat(react/widgets): search results interfering with SQL results + bad note path style 2025-08-29 16:28:49 +03:00
Elian Doran
735e91e636 feat(react/widgets): port sql_result 2025-08-29 16:10:37 +03:00
Elian Doran
4df94d1f20 chore(react/global_menu): add missing command names 2025-08-29 15:02:56 +03:00
Elian Doran
70440520e1 fix(react/global_menu): misalignment of the "advanced" submenu 2025-08-29 15:01:29 +03:00
Elian Doran
e49e2d5093 fix(react/global_menu): styling and layout of keyboard shortcuts 2025-08-29 13:01:54 +03:00
Elian Doran
f0ac301417 refactor(react/global_menu): get rid of outsideChildren 2025-08-29 12:50:45 +03:00
Elian Doran
168ff90e38 fix(react/global_menu): menu layout on mobile 2025-08-29 12:48:10 +03:00
Elian Doran
5e4f529b26 chore(react/global_menu): advanced submenu toggle on mobile 2025-08-29 12:40:16 +03:00
Elian Doran
0d1bd3e298 feat(react/global_menu): add show/hide conditions 2025-08-29 12:36:12 +03:00
Elian Doran
70f826b737 feat(react/global_menu): add update indicator 2025-08-29 12:30:22 +03:00
Elian Doran
8bd5af3fd2 feat(react/global_menu): add a few more items 2025-08-29 12:00:45 +03:00
Elian Doran
dbbae87cd3 feat(react/global_menu): port advanced options 2025-08-29 11:55:05 +03:00
Elian Doran
83fd42aff2 feat(react): add bootstrap tooltip to menu items 2025-08-29 11:54:16 +03:00
Nriver
93c9383a92 fix(auth): add missing TOTP verification for /login/token to align with /login 2025-08-29 11:13:50 +08:00
Romain DEP.
7c490d8b72 fix(tree): defend against stray null values that may occur when multiple sorting overrides are defined
fixes #6820
2025-08-29 01:57:18 +02:00
Elian Doran
b4b5e86a14 Translations update from Hosted Weblate (#6819) 2025-08-29 01:31:37 +03:00
Elian Doran
e166b97b8f feat(react/widgets): port a few more global menu items 2025-08-29 01:07:11 +03:00
Elian Doran
829f382726 feat(react/widgets): global menu with zoom controls 2025-08-29 00:47:47 +03:00
Elian Doran
4ef103063d feat(react/widgets): port search result 2025-08-28 23:16:04 +03:00
Elian Doran
fa66e50193 feat(react/widgets): port scroll padding 2025-08-28 22:12:39 +03:00
Antonio Liccardo (TuxmAL)
255ad96c8b Translated using Weblate (Italian)
Currently translated at 12.9% (202 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/it/
2025-08-28 20:52:03 +02:00
Kuzma Simonov
a12fa1177b Translated using Weblate (Russian)
Currently translated at 100.0% (1565 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-08-28 20:52:02 +02:00
rodrigomescua
6fa7cc8201 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/pt_BR/
2025-08-28 20:52:02 +02:00
Newcomer1989
2fff5418a9 Translated using Weblate (German)
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-28 20:52:01 +02:00
rodrigomescua
2e805cd5a3 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (1565 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-28 20:52:00 +02:00
Newcomer1989
61eaa89de6 Translated using Weblate (German)
Currently translated at 100.0% (1565 of 1565 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-28 20:51:59 +02:00
Elian Doran
aa0c021f8b Theme tweaks (#6783) 2025-08-28 21:51:52 +03:00
Elian Doran
4fd02db079 chore(react): remove irrelevant TODO 2025-08-28 21:03:33 +03:00
Elian Doran
88bbc7e8c1 Port floating buttons to React (#6811) 2025-08-28 21:01:43 +03:00
Elian Doran
af0ba32dd9 chore(react/floating_buttons): fix wrong import of fnote 2025-08-28 20:28:55 +03:00
Elian Doran
938d295bf3 chore(react/floating_buttons): fix type error 2025-08-28 20:13:55 +03:00
Elian Doran
f82667066f feat(react/floating_buttons): add mobile support 2025-08-28 20:10:21 +03:00
Elian Doran
03a7fe1282 fix(react/floating_buttons): react to note type/mime changes 2025-08-28 20:04:47 +03:00
Elian Doran
0c0504ffd1 refactor(react/floating_buttons): use enabled at component level 2025-08-28 19:56:53 +03:00
Elian Doran
e4900ce87b fix(react/floating_buttons): style differences from original 2025-08-28 19:12:26 +03:00
Elian Doran
04de87722b fix(react/floating_buttons): backlinks affecting show/hide button 2025-08-28 19:05:30 +03:00
Elian Doran
a95e28c085 feat(react/floating_buttons): port backlinks 2025-08-28 18:35:37 +03:00
Elian Doran
9fbcfb0f0f fix(deps): update dependency marked to v16.2.1 (#6815) 2025-08-28 08:52:41 +03:00
renovate[bot]
f24a3442fb fix(deps): update dependency marked to v16.2.1 2025-08-28 05:05:28 +00:00
Elian Doran
918a945e3b chore(deps): update dependency svelte to v5.38.6 (#6813) 2025-08-28 08:03:31 +03:00
Elian Doran
3b96b5779b fix(deps): update dependency dayjs to v1.11.14 (#6814) 2025-08-28 08:02:48 +03:00
Elian Doran
a93b20428e chore(deps): update dependency electron to v37.4.0 (#6816) 2025-08-28 08:02:23 +03:00
renovate[bot]
0522024f6d chore(deps): update dependency electron to v37.4.0 2025-08-28 02:54:57 +00:00
renovate[bot]
f27f135a61 fix(deps): update dependency dayjs to v1.11.14 2025-08-28 02:53:40 +00:00
renovate[bot]
6a76136878 chore(deps): update dependency svelte to v5.38.6 2025-08-28 02:53:04 +00:00
Elian Doran
1766d28fc2 feat(react/floating_buttons): port show/hide 2025-08-28 00:44:18 +03:00
Elian Doran
f51d944bb3 feat(react/floating_buttons): port in-app help button 2025-08-28 00:23:00 +03:00
Elian Doran
cabe240e7e refactor(react/floating_buttons): split into two buttons 2025-08-28 00:06:40 +03:00
Elian Doran
e72fb39c4d feat(react/floating_buttons): port PNG/SVG export buttons 2025-08-28 00:02:02 +03:00
Elian Doran
0ca30e0e87 feat(react/floating_buttons): port copy image reference 2025-08-27 23:57:33 +03:00
Elian Doran
cc362393be chore(react/floating_buttons): port geo map buttons 2025-08-27 23:45:51 +03:00
Elian Doran
40bfd827d2 chore(react/floating_buttons): improve sizing 2025-08-27 23:36:50 +03:00
Elian Doran
a4046fbf6e feat(react/floating_buttons): port relation map buttons 2025-08-27 23:33:07 +03:00
Elian Doran
28605f2687 feat(react/floating_buttons): fancy title + keyboard shortcut 2025-08-27 23:20:19 +03:00
Elian Doran
2085d1bbba feat(react/floating_buttons): port save to note button 2025-08-27 23:14:25 +03:00
Elian Doran
08db03800e feat(react/floating_buttons): port open Trilium API docs 2025-08-27 22:59:07 +03:00
Elian Doran
04b7e0cde9 feat(react/floating_buttons): port execute note button 2025-08-27 22:54:05 +03:00
Elian Doran
401260d3ca feat(react/floating_buttons): port highlights list 2025-08-27 22:47:20 +03:00
Elian Doran
53e0c05290 feat(react/floating_buttons): port toc 2025-08-27 22:44:11 +03:00
Elian Doran
cdbb89482e feat(react/floating_buttons): port edit button 2025-08-27 22:33:36 +03:00
Elian Doran
e290635ba5 feat(react/floating_buttons): port toggle read only button 2025-08-27 22:09:00 +03:00
Elian Doran
e340e6f5e3 feat(react/floating_buttons): port switch split orientation 2025-08-27 21:56:02 +03:00
Elian Doran
2d950e8f3a refactor(react/floating_buttons): use component-driven approach 2025-08-27 21:37:48 +03:00
Elian Doran
4c70d72ba2 feat(react/floating_buttons): port refresh button 2025-08-27 21:29:49 +03:00
Elian Doran
80edc4c4e0 feat(react): base structure for floating buttons 2025-08-27 21:15:54 +03:00
Adorian Doran
4d07a1aab6 client/quick search results: add a whitespace between attributes 2025-08-27 20:51:12 +03:00
Adorian Doran
e2cd357319 style/quick search results: customize the highlight color 2025-08-27 20:51:12 +03:00
Adorian Doran
620b57bfa6 style/quick search results: refactor 2025-08-27 20:51:12 +03:00
Adorian Doran
934c9d3df8 style/quick search results: improve appearance 2025-08-27 20:51:12 +03:00
Adorian Doran
f034e8bb37 style/quick search results: tweak layout 2025-08-27 20:51:12 +03:00
Adorian Doran
93f80c6837 client/quick search: extract inline styles 2025-08-27 20:51:12 +03:00
Adorian Doran
a54177fee0 style(legacy)/checkboxes and radios: add a gap between the tickbox and the label 2025-08-27 20:51:12 +03:00
Adorian Doran
0e6ad42923 style: fix button class 2025-08-27 20:51:12 +03:00
Adorian Doran
b0beb74011 style/dropdown buttons: tweak 2025-08-27 20:51:12 +03:00
Adorian Doran
4857fecc41 style/launcher/calendar: fix the width for the horizontal layout 2025-08-27 20:49:01 +03:00
Adorian Doran
8405d960be style/ribbon/attribute editor: improve layout 2025-08-27 20:45:17 +03:00
Adorian Doran
b9101c9fb2 style/delete note dialog: tweak layout 2025-08-27 20:41:14 +03:00
Adorian Doran
e457e6a2f2 style/note icon buttons: fade the color and use the default cursor when disabled 2025-08-27 20:41:14 +03:00
Elian Doran
f3416fa03e Translations update from Hosted Weblate (#6807) 2025-08-27 20:31:34 +03:00
rodrigomescua
3e5ab2b1e1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 82.0% (1284 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-27 17:15:24 +00:00
Newcomer1989
5c0bc9a7c2 Translated using Weblate (German)
Currently translated at 91.8% (1437 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-27 17:15:23 +00:00
Elian Doran
2adfa55acd React context-aware widgets (ribbon, note actions, note title) (#6731) 2025-08-27 20:15:04 +03:00
Elian Doran
8125e8afcd fix(react/options): plain text not disabled 2025-08-27 20:14:33 +03:00
Elian Doran
e328f18558 feat(react/ribbon): edit content languages in modal 2025-08-27 19:56:14 +03:00
Elian Doran
cbdfa9079c fix(react/ribbon): bad right margin of note type 2025-08-27 19:51:24 +03:00
Elian Doran
e08c4515a7 fix(react/ribbon): note type not always updating 2025-08-27 19:49:22 +03:00
Elian Doran
650aa16b89 feat(react/ribbon): improve style of note type modal 2025-08-27 19:46:26 +03:00
Elian Doran
11d908218b feat(react/ribbon): allow editing code types directly 2025-08-27 19:42:20 +03:00
Elian Doran
3627a7dc93 refactor(react/dialogs): allow modals in sub-components 2025-08-27 19:35:47 +03:00
Elian Doran
1e1c8cc4ff fix(ribbon): code note types not refreshing 2025-08-27 19:13:30 +03:00
Elian Doran
d616bc09c9 chore(e2e): remove test that is no longer relevant 2025-08-27 18:55:52 +03:00
Elian Doran
f1cef44d5d Merge remote-tracking branch 'origin/main' into react/note_context_aware
; Conflicts:
;	pnpm-lock.yaml
2025-08-27 18:30:19 +03:00
Elian Doran
3795be4750 Translations update from Hosted Weblate (#6802) 2025-08-27 18:29:13 +03:00
Elian Doran
2bb66a7526 chore(react): fix some more type errors 2025-08-27 18:27:47 +03:00
Elian Doran
28a472782f chore(react): remove debug log 2025-08-27 18:26:27 +03:00
Elian Doran
a4da002352 fix(react/ribbon): saving from attribute dialog not working 2025-08-27 18:25:54 +03:00
Elian Doran
94fdc2beee fix(react/dialogs): formatting toolbar shown in code notes in quick edit 2025-08-27 18:12:39 +03:00
Elian Doran
dd4a01d9f8 fix(react/dialogs): jump to note sometimes showing empty list 2025-08-27 17:59:37 +03:00
Elian Doran
a2a6c67350 fix(react): alignment and size of search/bulk action buttons 2025-08-27 17:53:27 +03:00
Elian Doran
2152ca7ba6 fix(react): search crashing due to bad rendering mechanism 2025-08-27 17:46:20 +03:00
Elian Doran
40e4d236f4 fix(react): owned attributes not showing up the first time 2025-08-27 17:42:38 +03:00
Elian Doran
0450cd080d fix(react): note context sometimes not working on mobile 2025-08-27 17:37:28 +03:00
Elian Doran
1eaac79d63 fix(react/ribbon): note context menu button looking off 2025-08-27 16:58:01 +03:00
Elian Doran
19c0305ed9 fix(react/revisions): revision list overflowing when too many 2025-08-27 16:41:42 +03:00
Elian Doran
f0d14a966a fix(react/revisions): wrong selection when navigating between notes 2025-08-27 16:39:48 +03:00
Elian Doran
37e6ccdc1a fix(react/revisions): selection not possible due to new hierarchy 2025-08-27 16:37:07 +03:00
Elian Doran
06cea99b40 fix(react): note title not selecting text 2025-08-27 16:28:07 +03:00
Elian Doran
1851336862 fix(react/ribbon): solve some type errors 2025-08-27 16:17:19 +03:00
Elian Doran
461eb273d9 fix(react/ribbon): attribute editor sometimes not clearing between notes 2025-08-27 15:53:27 +03:00
Elian Doran
470edc4d70 fix(react/ribbon): attribute editor saving unnecessarily 2025-08-27 15:35:44 +03:00
Elian Doran
26132a2a56 fix(react/ribbon): crash due to misuse of component rendering 2025-08-27 15:19:31 +03:00
Elian Doran
d92bd16042 chore(react/ribbon): react to note type changes 2025-08-27 14:56:18 +03:00
Elian Doran
1c7dfa6c91 chore(react/ribbon): fix activation of add new label to all tabs 2025-08-27 13:19:52 +03:00
Elian Doran
3a3fed4314 chore(react/ribbon): fix 3px height of the ribbon when collapsed 2025-08-27 13:13:02 +03:00
Elian Doran
82bdb76d75 chore(react/ribbon): simplify useNoteContext & handle setNoteContext 2025-08-27 13:06:57 +03:00
Elian Doran
066f3ea078 chore(react/ribbon): react to add new label/relation even when not shown 2025-08-27 13:05:51 +03:00
Elian Doran
9d760a21d5 chore(react/ribbon): disable when view mode is not good 2025-08-27 12:20:11 +03:00
Elian Doran
976c795ac6 chore(react/ribbon): add tooltip with keyboard shortcut 2025-08-27 12:16:40 +03:00
Elian Doran
ed320e4e24 chore(client): fix type error due to React integration 2025-08-27 11:59:07 +03:00
Newcomer1989
3111738700 Translated using Weblate (German)
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-27 10:28:44 +02:00
rodrigomescua
1fa0bada23 Translated using Weblate (Portuguese (Brazil))
Currently translated at 62.5% (978 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-27 10:28:44 +02:00
Newcomer1989
11eca7e58b Translated using Weblate (German)
Currently translated at 90.3% (1413 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-27 10:28:44 +02:00
toaik
4b50e2f14d Translated using Weblate (German)
Currently translated at 90.3% (1413 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-27 10:28:43 +02:00
Newcomer1989
63faba9603 Translated using Weblate (German)
Currently translated at 90.3% (1413 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-27 10:28:43 +02:00
Elian Doran
3e213699e0 chore(client): fix type error due to React integration 2025-08-27 11:27:49 +03:00
Elian Doran
c88ff07691 chore(deps): update dependency typedoc to v0.28.11 (#6792) 2025-08-27 11:25:52 +03:00
Elian Doran
b53aa5cf6e chore(deps): update svelte monorepo (#6793) 2025-08-27 11:25:44 +03:00
Elian Doran
2641b9b3fe fix(deps): update dependency bootstrap to v5.3.8 (#6794) 2025-08-27 11:25:35 +03:00
Elian Doran
3a2a73992c chore(deps): update dependency @types/node to v22.18.0 (#6795) 2025-08-27 11:24:39 +03:00
Elian Doran
b82b17a701 chore(deps): update typescript-eslint monorepo to v8.41.0 (#6796) 2025-08-27 11:24:27 +03:00
renovate[bot]
a6202edcd1 chore(deps): update typescript-eslint monorepo to v8.41.0 2025-08-27 02:40:21 +00:00
renovate[bot]
6eac0cb75d chore(deps): update dependency @types/node to v22.18.0 2025-08-27 02:38:43 +00:00
renovate[bot]
83672d6138 fix(deps): update dependency bootstrap to v5.3.8 2025-08-27 02:38:15 +00:00
renovate[bot]
51dadf72d0 chore(deps): update svelte monorepo 2025-08-27 02:37:45 +00:00
renovate[bot]
0cbf61acb3 chore(deps): update dependency typedoc to v0.28.11 2025-08-27 02:37:12 +00:00
Elian Doran
399c7435ac Merge remote-tracking branch 'origin/main' into react/note_context_aware 2025-08-26 23:49:00 +03:00
Elian Doran
d51fae7878 fix(mobile): file properties not displayed 2025-08-26 23:23:45 +03:00
Elian Doran
9750e25ad5 fix(mobile): note title not working 2025-08-26 22:21:42 +03:00
Elian Doran
2f9b2f0e8f refactor(react): rename formatting toolbar for clarity 2025-08-26 21:57:28 +03:00
Elian Doran
8deaf22544 fix(quick_edit): classic toolbar not shown 2025-08-26 21:45:22 +03:00
Elian Doran
b192f43187 chore(release): prepare for 0.98.1 2025-08-26 20:35:41 +03:00
Elian Doran
8cb8d1303c docs(release): v0.98.1 2025-08-26 20:34:36 +03:00
Elian Doran
5237348975 chore(docs): fix quick search documentation not in meta 2025-08-26 19:44:01 +03:00
Elian Doran
72e2f6757e fix(client): autocomplete looking off in new tab 2025-08-26 19:15:36 +03:00
Elian Doran
cf059e7f86 Translations update from Hosted Weblate (#6787) 2025-08-26 15:36:21 +03:00
Elian Doran
44d69216b6 Translated using Weblate (Romanian)
Currently translated at 100.0% (1564 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ro/
2025-08-26 14:35:50 +02:00
Elian Doran
c38bf09af0 chore(client): fix a table nesting issue 2025-08-26 11:53:58 +03:00
Elian Doran
9373d47e86 fix: log same error message on api 401 as on login error to allow fail2ban blocking (#6782) 2025-08-26 08:53:55 +03:00
hulmgulm
29350628c3 Merge branch 'main' into main 2025-08-26 06:26:41 +02:00
Elian Doran
83d1a68879 fix(i18n): pt_BR still not working 2025-08-25 23:32:20 +03:00
hulmgulm
f188408099 Merge branch 'main' into main 2025-08-25 21:22:07 +02:00
Elian Doran
449ab3a798 fix(client/about): directory not displayed on desktop 2025-08-25 22:19:17 +03:00
hulmgulm
21504d1417 Logout same error message on api 401 as on login error 2025-08-25 21:18:01 +02:00
Elian Doran
3060b496e3 chore(client): remove redundant log 2025-08-25 22:09:29 +03:00
Elian Doran
bd35539fa1 fix(i18n): electron locale for pt_BR 2025-08-25 21:34:59 +03:00
Elian Doran
df6447e3ad feat(quick_search): also allow for the equals operator in note title's quick search (#6769) 2025-08-25 20:49:45 +03:00
Elian Doran
24fd898f0d fix(add_link): inserting link to selection not working properly (closes #6776) 2025-08-25 20:48:15 +03:00
Elian Doran
1aa6238288 chore(deps): update dependency @types/jquery to v3.5.33 (#6748) 2025-08-25 20:22:33 +03:00
Elian Doran
c16c4788da fix(client): type error 2025-08-25 20:18:51 +03:00
Elian Doran
0c35daab85 Merge remote-tracking branch 'origin/main' into renovate/jquery-3.x 2025-08-25 20:01:59 +03:00
Elian Doran
4a19639e92 fix(deps): update dependency tsx to v4.20.5 (#6775) 2025-08-25 20:00:55 +03:00
renovate[bot]
36cceea677 fix(deps): update dependency tsx to v4.20.5 2025-08-25 16:33:16 +00:00
Elian Doran
4dbc76790a Merge remote-tracking branch 'origin/main' into react/note_context_aware 2025-08-25 19:33:14 +03:00
Elian Doran
b32a344a21 chore(deps): update dependency tsx to v4.20.5 (#6772) 2025-08-25 19:31:04 +03:00
Elian Doran
3896ab822f Translations update from Hosted Weblate (#6778) 2025-08-25 19:30:49 +03:00
OKiU Network
cfa4ba57d4 Translated using Weblate (Dutch)
Currently translated at 1.8% (7 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/nl/
2025-08-25 18:29:51 +02:00
OKiU Network
da051e0269 Translated using Weblate (Dutch)
Currently translated at 0.3% (5 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/nl/
2025-08-25 18:29:50 +02:00
Cédric MARCOUX
3eda77a91f Translated using Weblate (French)
Currently translated at 88.3% (334 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fr/
2025-08-25 18:29:49 +02:00
OKiU Network
5c2f4be5dd Added translation using Weblate (Dutch) 2025-08-25 18:29:49 +02:00
OKiU Network
435b501db9 Added translation using Weblate (Dutch) 2025-08-25 18:29:48 +02:00
Newcomer1989
5a27ffef5f Translated using Weblate (German)
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-25 18:29:47 +02:00
Luis Rebhan
02256d9a45 Translated using Weblate (German)
Currently translated at 82.9% (1297 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-25 18:29:47 +02:00
Newcomer1989
7e069009d6 Translated using Weblate (German)
Currently translated at 82.9% (1297 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
2025-08-25 18:29:46 +02:00
Elian Doran
3c25cda4c0 feat(docs): also improve how environment variables are shown in docs (#6727) 2025-08-25 19:29:40 +03:00
Elian Doran
70d7ad0b1a fix(jump_to): fix issue where attributes/tags don't show in results (#6752) 2025-08-25 19:26:48 +03:00
Elian Doran
e793b2f661 feat(config): fix previously documented env var formula not working (#6726) 2025-08-25 19:23:35 +03:00
Elian Doran
917ea3e401 feat(react): set up ESLint 2025-08-25 18:42:07 +03:00
Elian Doran
5a54dd666f refactor(react): fix a few more rules of hooks violations 2025-08-25 18:41:48 +03:00
Elian Doran
733ec2c145 refactor(react): fix a few rules of hooks violations 2025-08-25 18:00:10 +03:00
Elian Doran
e386b03b90 refactor(react): fix all eslint issues in .tsx files 2025-08-25 17:20:47 +03:00
Elian Doran
25d5d51085 chore(react): fix note path when empty 2025-08-25 16:15:13 +03:00
Elian Doran
50c4301a34 chore(react): fix height of title bar 2025-08-25 16:05:09 +03:00
Elian Doran
0f60c0696b chore(react): fix leak in content widget 2025-08-25 15:53:21 +03:00
Elian Doran
3ec2947c4f chore(react): fix configure languages open in background 2025-08-25 14:48:13 +03:00
Elian Doran
e89162838e chore(react): fix events not updating properly 2025-08-25 14:48:00 +03:00
Elian Doran
72181090a5 refactor(react): get rid of ReactBasicWidget 2025-08-25 14:36:17 +03:00
Elian Doran
72b2a5cc0d chore(react): use effects for event handlers to prevent leaks 2025-08-25 14:27:32 +03:00
Elian Doran
1eaeec8100 Revert "chore(react): prototype for note context"
This reverts commit 660db3b3ab.
2025-08-25 13:51:43 +03:00
Elian Doran
660db3b3ab chore(react): prototype for note context 2025-08-25 11:48:56 +03:00
Elian Doran
89d2fcb81e refactor(react): add debug information for devtools 2025-08-25 11:01:12 +03:00
Elian Doran
ccda623840 refactor(react/ribbon): remove unnecessary hook 2025-08-25 10:32:11 +03:00
renovate[bot]
a6e7dff61e chore(deps): update dependency tsx to v4.20.5 2025-08-25 06:59:19 +00:00
Elian Doran
86d1bbe8ff chore(deps): update dependency webdriverio to v9.19.2 (#6773) 2025-08-25 09:54:52 +03:00
Elian Doran
a10cb06f14 fix(deps): update dependency i18next to v25.4.2 (#6774) 2025-08-25 09:54:41 +03:00
renovate[bot]
dd9a62818b fix(deps): update dependency i18next to v25.4.2 2025-08-25 00:34:50 +00:00
renovate[bot]
c0e936675c chore(deps): update dependency webdriverio to v9.19.2 2025-08-25 00:34:18 +00:00
Jon Fuller
b0b788b7dc Merge branch 'main' into fix/quick-search-equals-operator 2025-08-24 14:47:31 -07:00
Jon Fuller
18f0f3ecac Fix for casing and formatting in i18n.ts which was causing compile errors (#6770) 2025-08-24 14:47:18 -07:00
Sky Swimmer
e7d745ac94 Update calendar_view.ts
Same as the previous, another casing error
2025-08-24 23:22:54 +02:00
Sky Swimmer
24abf7f0ed Update i18n.ts
Another casing error throwing off tests
2025-08-24 23:22:24 +02:00
Elian Doran
36fb097d1d chore(react/ribbon): add CSS for context menu 2025-08-25 00:21:44 +03:00
Elian Doran
35ef5fd0d3 chore(react/ribbon): add disable rules for context menu 2025-08-25 00:19:12 +03:00
Sky Swimmer
9a08f6534b Fix casing and formatting in i18n.ts which was causing compile errors 2025-08-24 23:14:45 +02:00
Elian Doran
885dd2053b chore(react/ribbon): add rest of the note action items 2025-08-25 00:08:40 +03:00
Elian Doran
6f6f280bdd chore(react/ribbon): add part of the note actions menu 2025-08-24 23:56:05 +03:00
Elian Doran
a3e8fd374f chore(react/ribbon): port convert to attachment 2025-08-24 23:21:28 +03:00
Elian Doran
f91c1f4180 chore(react/ribbon): port revisions button 2025-08-24 22:56:47 +03:00
Elian Doran
d85746c1b9 Revert "refactor(react/ribbon): use effects for event handling"
This reverts commit 5a17075eef.
2025-08-24 22:43:20 +03:00
Elian Doran
5a17075eef refactor(react/ribbon): use effects for event handling 2025-08-24 22:21:11 +03:00
Elian Doran
6cab47fb55 feat(react/ribbon): bring back toggling tabs via keyboard shortcut 2025-08-24 22:14:42 +03:00
perf3ct
93c5413790 feat(quick_search): also allow for the equals operator in note title's quick search 2025-08-24 18:53:05 +00:00
Elian Doran
f2db7baeba refactor(react): use beta approach for handling events everywhere 2025-08-24 21:18:48 +03:00
Elian Doran
a507991808 refactor(react/modals): use classless components 2025-08-24 20:57:23 +03:00
Elian Doran
7c86f90ac6 chore(react/ribbon): fix some more crashes when rapidly switching tabs 2025-08-24 20:31:39 +03:00
Elian Doran
1e9b772692 chore(react/ribbon): fix cannot set style when switching attributes 2025-08-24 20:25:11 +03:00
Elian Doran
096ab52216 refactor(react/ribbon): solve type errors 2025-08-24 20:23:00 +03:00
Elian Doran
88c3cd5cdd refactor(react/ribbon): move files around & remove imports 2025-08-24 20:16:58 +03:00
Elian Doran
99a911a220 chore(react/ribbon): port bulk actions for search 2025-08-24 20:12:22 +03:00
Elian Doran
3218ab971b chore(react/ribbon): fix alignment of help/close buttons 2025-08-24 19:03:19 +03:00
Elian Doran
274e3c1f7f refactor(react/ribbon): split into two files 2025-08-24 18:40:05 +03:00
Elian Doran
f8916a6e35 chore(react/ribbon): port limit 2025-08-24 18:34:29 +03:00
Elian Doran
73f20d01e4 chore(react/ribbon): port order by 2025-08-24 18:29:47 +03:00
Elian Doran
2fd3a875b6 chore(react/ribbon): port include archived notes 2025-08-24 18:11:29 +03:00
Elian Doran
68cba8d3b2 chore(react/ribbon): port debug 2025-08-24 18:09:33 +03:00
Elian Doran
6b28fd405e chore(react/ribbon): port fast search 2025-08-24 18:07:16 +03:00
Elian Doran
3bccbabe53 chore(react/ribbon): port ancestor depth 2025-08-24 18:02:18 +03:00
Elian Doran
c97c66ed8a Translations update from Hosted Weblate (#6767) 2025-08-24 17:30:55 +03:00
Elian Doran
4b212232c8 chore(react/ribbon): port ancestor (without depth) 2025-08-24 17:29:48 +03:00
Elian Doran
ac3a8edf2b chore(react/ribbon): add search script 2025-08-24 17:20:40 +03:00
Sleepy0Duck5
b581025bbe Translated using Weblate (Korean)
Currently translated at 10.0% (38 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ko/
2025-08-24 16:02:09 +02:00
Sleepy0Duck5
7bc5331747 Translated using Weblate (Korean)
Currently translated at 0.9% (15 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ko/
2025-08-24 16:02:07 +02:00
Kuzma Simonov
2415976475 Translated using Weblate (Russian)
Currently translated at 100.0% (1564 of 1564 strings)

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

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

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-24 16:02:01 +02:00
chdagenais
df73a420f9 Translated using Weblate (French)
Currently translated at 83.5% (316 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fr/
2025-08-24 16:01:59 +02:00
Newcomer1989
1e4d57f275 Translated using Weblate (German)
Currently translated at 92.8% (351 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-24 16:01:56 +02:00
chdagenais
19a238c8d3 Translated using Weblate (French)
Currently translated at 82.2% (1287 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fr/
2025-08-24 16:01:54 +02:00
Francis C
5ffd8a79eb Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1564 of 1564 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-24 16:01:53 +02:00
Elian Doran
04fbc82d7c chore(react/ribbon): save search to note 2025-08-24 16:58:10 +03:00
Elian Doran
3f105f7b8b chore(react/ribbon): focus on search textbox 2025-08-24 16:45:17 +03:00
Elian Doran
b9193a5562 chore(react/ribbon): handle search error 2025-08-24 16:41:44 +03:00
Elian Doran
e1fa188244 chore(react/ribbon): refresh options 2025-08-24 16:17:10 +03:00
Elian Doran
80ad87671a chore(react/ribbon): search & execute button 2025-08-24 16:02:15 +03:00
Elian Doran
b6d5a6ec2e chore(react/ribbon): dynamic rendering of search options 2025-08-24 15:59:22 +03:00
Elian Doran
759398d804 chore(react/ribbon): working execute search button 2025-08-24 15:48:53 +03:00
Elian Doran
c1b30db3d1 chore(react/ribbon): port search string 2025-08-24 15:29:07 +03:00
Elian Doran
0c8bfc39ef refactor(react/ribbon): bring back tab activation 2025-08-24 12:23:25 +03:00
Elian Doran
3815fddb27 Merge branch 'react/note_context_aware' of https://github.com/TriliumNext/trilium into react/note_context_aware 2025-08-24 12:22:16 +03:00
Elian Doran
b585a64a38 Merge remote-tracking branch 'origin/main' into react/note_context_aware 2025-08-24 12:05:05 +03:00
Elian Doran
58e58c192f Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-08-24 11:58:56 +03:00
Elian Doran
5939344378 fix(deps): update dependency i18next to v25.4.1 (#6754) 2025-08-24 11:49:48 +03:00
Elian Doran
ad85ee3531 feat(react/ribbon): start porting search definitions (buttons) 2025-08-24 11:42:25 +03:00
renovate[bot]
349f19fef7 fix(deps): update dependency i18next to v25.4.1 2025-08-24 08:18:50 +00:00
Elian Doran
d5777a024e chore(deps): update svelte monorepo (#6753) 2025-08-24 11:17:52 +03:00
Elian Doran
b7f4ee6171 fix(deps): update dependency react-i18next to v15.7.2 (#6755) 2025-08-24 11:17:05 +03:00
Elian Doran
a83c4e3970 fix(deps): update dependency eslint-linter-browserify to v9.34.0 (#6756) 2025-08-24 11:16:53 +03:00
Elian Doran
5a767dae34 feat(i18n): add support for Brazilian Portuguese 2025-08-24 11:05:52 +03:00
Elian Doran
9f93d30b99 feat(i18n): add support for Ukrainian 2025-08-24 10:53:21 +03:00
Elian Doran
dff525edc6 Translations update from Hosted Weblate (#6743) 2025-08-24 10:52:42 +03:00
renovate[bot]
26da431320 fix(deps): update dependency react-i18next to v15.7.2 2025-08-24 07:32:43 +00:00
renovate[bot]
cde4622693 fix(deps): update dependency eslint-linter-browserify to v9.34.0 2025-08-24 07:30:44 +00:00
renovate[bot]
5ede7ecc69 chore(deps): update svelte monorepo 2025-08-24 01:36:13 +00:00
Elian Doran
b607d1e628 refactor(react/ribbon): shared component for labelled entry 2025-08-23 23:59:41 +03:00
Elian Doran
d7e36bdf93 feat(react/ribbon): reintroduce combobox collection properties 2025-08-23 23:54:14 +03:00
Elian Doran
2b8b185b5b feat(react/ribbon): reintroduce number collection properties 2025-08-23 23:39:47 +03:00
Elian Doran
927ebcbec9 feat(react/ribbon): reintroduce checkbox collection properties 2025-08-23 23:32:12 +03:00
Elian Doran
ea1397de63 feat(react/ribbon): reintroduce button collection properties 2025-08-23 23:25:25 +03:00
Elian Doran
ce1f5c6204 feat(react/ribbon): port view type 2025-08-23 22:49:32 +03:00
Elian Doran
652114c7b5 feat(react/ribbon): finalize port of inherited attributes tab 2025-08-23 22:18:04 +03:00
Elian Doran
17cd2128fd chore(react): add editorconfig for .tsx 2025-08-23 22:02:41 +03:00
Elian Doran
bc4378cb3e chore(react/ribbon): port inherited attributes partially 2025-08-23 22:02:33 +03:00
perf3ct
513878dfef feat(jump_to): got the styling to look exactly how we were hoping for 2025-08-23 18:58:18 +00:00
perf3ct
753d5529b2 feat(jump_to): get the styling very close to what we want it to look like... 2025-08-23 18:40:11 +00:00
Elian Doran
9f217b88e4 refactor(react/ribbon): set up keyboard shortcuts 2025-08-23 20:59:21 +03:00
Elian Doran
d53faa8c01 refactor(react/ribbon): imperative api for saving, reloading, updating attributes 2025-08-23 20:49:54 +03:00
Elian Doran
a934760960 refactor(react/ribbon): use custom method for injecting handlers 2025-08-23 20:44:03 +03:00
Elian Doran
82914fc2aa chore(react/ribbon): unable to create notes in attribute editor 2025-08-23 20:35:19 +03:00
Elian Doran
db687197de chore(react/ribbon): add focus to attribute editor 2025-08-23 20:31:00 +03:00
Elian Doran
efd713dc61 chore(react/ribbon): add blur & keydown events 2025-08-23 19:54:02 +03:00
Elian Doran
3f3c7cfe88 chore(react/ribbon): add menu 2025-08-23 19:48:01 +03:00
Elian Doran
73ca285b7a chore(react/ribbon): support reference links in attributes 2025-08-23 19:26:23 +03:00
Elian Doran
168d25c020 chore(react/ribbon): fix save icon displayed when it shouldn't 2025-08-23 19:13:48 +03:00
Elian Doran
e8ae5486c8 chore(react/ribbon): display attribute errors 2025-08-23 18:28:42 +03:00
Elian Doran
f049b8b915 chore(react/ribbon): save attribute changes 2025-08-23 18:23:38 +03:00
Микола Копитін
4e755dc537 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1560 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-23 14:16:31 +02:00
Francis C
5351310a38 Translated using Weblate (Japanese)
Currently translated at 67.9% (1060 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-23 14:16:31 +02:00
Микола Копитін
211ca43a82 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-23 14:16:31 +02:00
Микола Копитін
e5235e7f22 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1560 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-23 14:16:31 +02:00
Astryd Park
e72298f0b4 Translated using Weblate (Korean)
Currently translated at 0.2% (1 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ko/
2025-08-23 14:16:31 +02:00
Микола Копитін
3abf5c65c6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-23 14:16:31 +02:00
Микола Копитін
268acb0b88 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (1560 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-23 14:16:30 +02:00
Kuzma Simonov
196b3b873f Translated using Weblate (Russian)
Currently translated at 100.0% (378 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ru/
2025-08-23 14:16:30 +02:00
Микола Копитін
4d9801a372 Translated using Weblate (Russian)
Currently translated at 100.0% (1560 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-08-23 14:16:30 +02:00
Kuzma Simonov
bd710ba665 Translated using Weblate (Russian)
Currently translated at 100.0% (1560 of 1560 strings)

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

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-23 14:16:30 +02:00
Newcomer1989
206007bbce Translated using Weblate (German)
Currently translated at 81.7% (309 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-23 14:16:30 +02:00
rodrigomescua
8ad05b92c0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 60.8% (950 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-23 14:16:30 +02:00
Francis C
735da2a855 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1560 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-23 14:16:30 +02:00
Elian Doran
980077f559 Add UI performance-related settings (#6747) 2025-08-23 15:16:22 +03:00
Elian Doran
12053e75bb chore(react/ribbon): fix size of attribute widget 2025-08-23 13:13:01 +03:00
Elian Doran
62372ed4c5 chore(react/ribbon): add logic for displaying attribute detail 2025-08-23 12:55:48 +03:00
Elian Doran
e5caf37697 chore(react/ribbon): load current attributes in editor 2025-08-23 12:39:49 +03:00
Elian Doran
befc5a9530 feat(react/ribbon): display help tooltip in attribute editor 2025-08-23 12:31:54 +03:00
Elian Doran
1e00407864 chore(react/ribbon): use separate component for editor 2025-08-23 12:05:03 +03:00
Elian Doran
73038efccf chore(react/ribbon): add some CKEditor events 2025-08-23 11:52:40 +03:00
Elian Doran
6d37e19b40 chore(react/ribbon): start implementing attribute editor 2025-08-23 11:44:51 +03:00
Elian Doran
2c33ef2b0d chore(react/ribbon): similar notes style 2025-08-23 11:28:10 +03:00
Elian Doran
6c30e0836f chore(react/ribbon): also react to width, not just height 2025-08-23 11:21:05 +03:00
Elian Doran
5f77ca31bd chore(react/ribbon): react note map to height changes 2025-08-23 11:12:14 +03:00
Elian Doran
f7c82d6b09 chore(react/ribbon): watch note map size 2025-08-23 11:00:25 +03:00
Elian Doran
86dd9aa42a chore(react/ribbon): integrate expand/collapse button 2025-08-23 10:47:46 +03:00
Elian Doran
5daca270e4 fix(deps): update dependency mermaid to v11.10.1 (#6749) 2025-08-23 08:47:35 +03:00
Elian Doran
e18813a4bf fix(deps): update eslint monorepo to v9.34.0 (#6750) 2025-08-23 08:47:16 +03:00
renovate[bot]
4aa7e211f3 fix(deps): update eslint monorepo to v9.34.0 2025-08-23 00:25:46 +00:00
renovate[bot]
419dc7edfb fix(deps): update dependency mermaid to v11.10.1 2025-08-23 00:24:33 +00:00
renovate[bot]
eaa84a6b39 chore(deps): update dependency @types/jquery to v3.5.33 2025-08-23 00:23:57 +00:00
Adorian Doran
1d0503d0e4 client/settings/ui-performance-settings: remove form groups 2025-08-23 03:22:59 +03:00
Adorian Doran
f7f98aa9a3 client/settings/ui-performance-settings: improve code formatting 2025-08-23 03:10:51 +03:00
Adorian Doran
575d14261a Merge branch 'main' of https://github.com/TriliumNext/Trilium into client/settings/ui-performance-settings 2025-08-23 02:20:37 +03:00
Adorian Doran
9aab606deb style: improve the support of disabled backdrop effects 2025-08-23 02:15:06 +03:00
Adorian Doran
2e11681b52 client/settings/disable backdrop effects: add the CSS implementation 2025-08-23 01:26:21 +03:00
Adorian Doran
8cca6637f7 client/settings/disable backdrop effects: react to the option change 2025-08-23 01:23:20 +03:00
Adorian Doran
82e076378c client/settings/disable backdrop effects: add the corresponding checkbox in the Appearance settings page 2025-08-23 01:20:54 +03:00
Adorian Doran
94ddad3c49 client/settings/disable backdrop effects: add an option to enable or disable backdrop effects 2025-08-23 01:15:00 +03:00
Adorian Doran
d35dbca18b client/settings/disable shadows: add the CSS implementation 2025-08-23 00:58:50 +03:00
Adorian Doran
7468d6147a client/settings/disable shadows: react to the option change 2025-08-23 00:55:46 +03:00
Adorian Doran
7c78d749de client/settings/disable shadows: add the corresponding checkbox in the Appearance settings page 2025-08-23 00:49:35 +03:00
Adorian Doran
85dd99a3c4 client/settings/disable shadows: add an option to enable or disable shadows 2025-08-23 00:43:49 +03:00
Adorian Doran
0a9c0234e2 client/settings/disable motion: update translation 2025-08-23 00:38:06 +03:00
JYC333
fad77ba5a0 chore(deps): update dependency vite-plugin-static-copy to v3.1.2 [security] (#6735) 2025-08-22 23:30:13 +02:00
JYC333
12723f3216 fix(deps): update dependency react-i18next to v15.7.1 (#6739) 2025-08-22 23:29:38 +02:00
JYC333
a43140515f chore(deps): update ckeditor5 config packages to v12.1.1 (#6738) 2025-08-22 23:29:26 +02:00
Adorian Doran
3e3cc8c541 client/settings/disable motion: refactor 2025-08-23 00:19:26 +03:00
Elian Doran
a85141ace2 feat(react/ribbon): port note map partially 2025-08-22 23:47:02 +03:00
Elian Doran
c33280bbb2 chore(react): fix leak & adjustable class name 2025-08-22 23:45:31 +03:00
Elian Doran
df3aa04787 chore(react): proper legacy widget injection & event handling 2025-08-22 23:33:02 +03:00
Elian Doran
4bd25a0d4a chore(react): use different injection mechanism 2025-08-22 23:24:00 +03:00
Elian Doran
7fadf4c6e1 chore(react): prototype hook to render legacy widgets 2025-08-22 23:12:14 +03:00
Adorian Doran
d1538508e8 client/settings/disable motion: turn off jQuery animations if motion is disabled 2025-08-22 22:20:57 +03:00
Elian Doran
b24d786933 chore(react/ribbon): fix a few type errors 2025-08-22 21:58:35 +03:00
Elian Doran
8f69b87dd1 feat(react/ribbon): port note paths tab 2025-08-22 21:45:03 +03:00
Adorian Doran
9b1da8c311 Settings/Appearance: improve CSS selector specificity 2025-08-22 21:37:56 +03:00
Elian Doran
8287063aab feat(react/ribbon): port image properties 2025-08-22 21:09:51 +03:00
Adorian Doran
e4a8258acf client/settings/disable motion: fix submenus not opening 2025-08-22 20:52:31 +03:00
Adorian Doran
5e88043c7b client/settings/disable motion: add the CSS implementation 2025-08-22 20:48:26 +03:00
Adorian Doran
bedf9112fb client/settings/disable motion: add localization support 2025-08-22 20:42:17 +03:00
Adorian Doran
03681d23c5 client/settings/disable motion: add an option to allow transitions and animations to be disabled 2025-08-22 20:32:08 +03:00
Elian Doran
21683db0b8 refactor(react/ribbon): dedicated component for file upload 2025-08-22 20:25:15 +03:00
Elian Doran
978d829150 feat(react/ribbon): port file notes 2025-08-22 20:17:00 +03:00
Elian Doran
cc05572a35 feat(react/ribbon): port similar notes 2025-08-22 19:27:58 +03:00
Elian Doran
c5bb310613 chore(react/ribbon): bring back note info auto-refresh 2025-08-22 19:07:04 +03:00
Elian Doran
77551b1fed feat(react/ribbon): port note info tab 2025-08-22 18:23:54 +03:00
Elian Doran
70728c274e feat(react/ribbon): port note properties 2025-08-22 17:41:45 +03:00
Elian Doran
cee4714665 feat(react/ribbon): port edited notes 2025-08-22 17:31:15 +03:00
Elian Doran
c3eca3b626 feat(react/ribbon): port script tab 2025-08-22 16:58:44 +03:00
Elian Doran
01e4cd2e78 chore(react/ribbon): set up formatting toolbar 2025-08-22 16:34:16 +03:00
Elian Doran
b99d01ad7b chore(react/ribbon): bring back tab filtering 2025-08-22 16:07:30 +03:00
Elian Doran
bf0213907e chore(react/ribbon): finalize language switcher 2025-08-22 15:40:36 +03:00
Elian Doran
eff5b6459d chore(react/ribbon): fix event 2025-08-22 15:11:12 +03:00
Elian Doran
8e29b5eed6 feat(react/ribbon): port note language 2025-08-22 12:34:21 +03:00
Elian Doran
c91748da15 feat(react/ribbon): port template switch 2025-08-22 12:15:03 +03:00
Elian Doran
f04f9dc262 feat(react/ribbon): port shared switch 2025-08-22 11:57:45 +03:00
Elian Doran
e873cdab7e chore(react/ribbon): fix width of editability select 2025-08-22 11:42:07 +03:00
Elian Doran
f9b6fd6ac5 feat(react): port bookmark switch 2025-08-22 11:40:27 +03:00
renovate[bot]
aa191e110c fix(deps): update dependency react-i18next to v15.7.1 2025-08-22 02:44:08 +00:00
renovate[bot]
dd09907925 chore(deps): update ckeditor5 config packages to v12.1.1 2025-08-22 02:43:31 +00:00
Elian Doran
da4810672d feat(react/ribbon): improve editability select 2025-08-21 22:24:35 +03:00
Elian Doran
f772f59d7c feat(react/ribbon): port editability select 2025-08-21 22:19:26 +03:00
Elian Doran
1964fb90d5 feat(react/ribbon): port toggle protected note 2025-08-21 21:26:45 +03:00
Elian Doran
5945f2860a chore(react/ribbon): finalize note type selection 2025-08-21 21:01:57 +03:00
Elian Doran
f45da049b9 chore(react/ribbon): change note type 2025-08-21 20:56:37 +03:00
Elian Doran
c0beab8a5d chore(react/ribbon): display current note type 2025-08-21 20:38:19 +03:00
Elian Doran
cabeb13adb chore(react/ribbon): add note types 2025-08-21 20:30:12 +03:00
Elian Doran
e2e9721d5f chore(react/ribbon): add basic note types & badges 2025-08-21 20:16:06 +03:00
Elian Doran
4e9deab605 chore(react/ribbon): make content work 2025-08-21 19:59:56 +03:00
Elian Doran
9bb048fb01 chore(react/ribbon): toggleable tabs 2025-08-21 19:48:07 +03:00
Elian Doran
6849f80506 chore(react/ribbon): make tabs activable 2025-08-21 19:35:21 +03:00
Elian Doran
5597f4e2e0 chore(react/ribbon): add all ribbon tab titles & icon 2025-08-21 19:27:18 +03:00
renovate[bot]
35e9508bde chore(deps): update dependency vite-plugin-static-copy to v3.1.2 [security] 2025-08-21 15:41:25 +00:00
Elian Doran
45fbcec805 feat(react/ribbon): port base structure 2025-08-21 18:29:13 +03:00
Elian Doran
4c8da70ef3 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.4 (#6721) 2025-08-21 17:49:28 +03:00
Elian Doran
ed5da5cd4a Translations update from Hosted Weblate (#6732) 2025-08-21 17:49:10 +03:00
Astryd Park
dc5fccdbcd Added translation using Weblate (Korean) 2025-08-21 16:32:31 +02:00
Astryd Park
91aea333c7 Added translation using Weblate (Korean) 2025-08-21 16:32:31 +02:00
Микола Копитін
a0de01cff1 Translated using Weblate (Ukrainian)
Currently translated at 99.4% (376 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-21 16:32:30 +02:00
Микола Копитін
a41ed34193 Translated using Weblate (Ukrainian)
Currently translated at 37.0% (578 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-21 16:32:30 +02:00
Newcomer1989
49e8811c18 Translated using Weblate (German)
Currently translated at 77.7% (294 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
2025-08-21 16:32:29 +02:00
Lucas Fernandes de Camargo
488563a82e Translated using Weblate (Portuguese (Brazil))
Currently translated at 22.5% (351 of 1560 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pt_BR/
2025-08-21 16:32:29 +02:00
Elian Doran
d76d50f30e chore(react): fix a type error 2025-08-21 17:16:03 +03:00
Elian Doran
4685aef88d chore(react/note_icon): reset to default icon 2025-08-21 16:19:18 +03:00
Elian Doran
a106510924 chore(react/note_icon): react to icon changes 2025-08-21 15:50:14 +03:00
Elian Doran
9d54503ef7 chore(react/note_icon): reintroduce setting the icon 2025-08-21 15:21:32 +03:00
Elian Doran
b1449eebf3 chore(react/note_icon): reintroduce read-only 2025-08-21 15:14:19 +03:00
Elian Doran
b213453062 refactor(react/note_icon): introduce autofocus at text box level 2025-08-21 15:11:08 +03:00
Elian Doran
076c0321cf chore(react/note_icon): focus search by default 2025-08-21 15:09:25 +03:00
Elian Doran
4d71b73f38 chore(react/note_icon): case insensitive search 2025-08-21 15:07:41 +03:00
Elian Doran
b20ffdf7db chore(react/note_icon): sort by count 2025-08-21 15:05:55 +03:00
Elian Doran
ef018e22d6 feat(react/note_icon): render dropdown only when needed 2025-08-21 15:03:54 +03:00
Elian Doran
3fa290a257 chore(react/note_icon): add back filter by category 2025-08-21 14:57:54 +03:00
Elian Doran
cdde530b60 chore(react/note_icon): add back filter by text 2025-08-21 14:45:19 +03:00
Elian Doran
aa608510d0 feat(react): port note icon 2025-08-21 14:41:59 +03:00
Elian Doran
009fd63ce9 chore(react): finalize note title porting 2025-08-21 13:18:39 +03:00
Elian Doran
bea352855a refactor(react): allow binding multiple events at once 2025-08-21 13:17:28 +03:00
Elian Doran
51e8a80ca3 chore(react/note_title): delete new notes on escape 2025-08-21 13:13:48 +03:00
Elian Doran
8a543d4513 chore(react/note_title): focus content on enter 2025-08-21 13:00:08 +03:00
Elian Doran
945e180a6f chore(react/note_title): add before unload listener 2025-08-21 12:55:33 +03:00
Elian Doran
b93fa332d3 fix(client): please wait for save showing up multiple times 2025-08-21 12:49:03 +03:00
Elian Doran
9e947f742d fix(react/note_title): title shown on empty widget pane 2025-08-21 12:15:12 +03:00
Elian Doran
033e90f8b7 fix(react/note_title): not refreshing on protected session 2025-08-21 12:13:30 +03:00
Elian Doran
be576176c5 feat(react/note_title): bring back navigation title 2025-08-21 11:08:33 +03:00
Elian Doran
4da3e8a4d8 refactor(react/note_title): use note property for title as well 2025-08-21 10:54:38 +03:00
Elian Doran
db2bf537ea refactor(react/note_title): use hook for listening to note property 2025-08-21 10:44:58 +03:00
Elian Doran
9a4fdcaef2 chore(react/note_title): bring back styles 2025-08-21 10:34:30 +03:00
Elian Doran
ca40360f7d feat(react): basic implementation of note title 2025-08-21 10:08:49 +03:00
Elian Doran
799e705ff8 fix(react): note context not always updated 2025-08-21 09:18:52 +03:00
Elian Doran
a1b18c7f97 chore(deps): update dependency @sveltejs/kit to v2.36.1 (#6723) 2025-08-21 08:22:09 +03:00
Elian Doran
9958a6e1bf fix(deps): update dependency i18next to v25.4.0 (#6724) 2025-08-21 08:21:24 +03:00
renovate[bot]
1fc6d8aca7 fix(deps): update dependency i18next to v25.4.0 2025-08-21 05:21:13 +00:00
Elian Doran
3e9ec2d943 chore(deps): update dependency @playwright/test to v1.55.0 (#6722) 2025-08-21 08:20:47 +03:00
Elian Doran
1420def1c3 fix(deps): update dependency react-i18next to v15.7.0 (#6725) 2025-08-21 08:19:38 +03:00
renovate[bot]
3b4184e765 chore(deps): update dependency @sveltejs/kit to v2.36.1 2025-08-21 02:22:23 +00:00
perf3ct
4ce9102f93 feat(docs): try to also improve how environment variables are shown in docs 2025-08-21 02:21:00 +00:00
perf3ct
eb27ec2234 feat(config): fix previously documented env var formula not working
asdf
2025-08-21 02:18:08 +00:00
renovate[bot]
b70e25d348 fix(deps): update dependency react-i18next to v15.7.0 2025-08-21 00:05:32 +00:00
renovate[bot]
772c0bbe1a chore(deps): update dependency @playwright/test to v1.55.0 2025-08-21 00:04:01 +00:00
renovate[bot]
144021c053 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.4 2025-08-21 00:03:29 +00:00
Elian Doran
59486cd55d feat(react): basic handling of note context aware 2025-08-20 23:53:13 +03:00
Elian Doran
afe3904ea3 feat(react): render raw react components 2025-08-20 22:13:52 +03:00
Elian Doran
8abd3ed3f1 feat(docs): implement swagger ui endpoint for internal api (#6719) 2025-08-20 21:36:21 +03:00
perf3ct
53ed510c92 feat(docs): remove old json api docs 2025-08-20 17:36:22 +00:00
Elian Doran
4ec46a2ebd chore(client): add some documentation 2025-08-20 20:34:00 +03:00
Elian Doran
db6f948499 Translations update from Hosted Weblate (#6720) 2025-08-20 20:33:26 +03:00
perf3ct
05c73011f5 feat(docs): add additional api routes 2025-08-20 17:30:26 +00:00
Микола Копитін
3b733d01f1 Translated using Weblate (Ukrainian)
Currently translated at 44.1% (167 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-20 17:29:08 +00:00
Микола Копитін
ebf21296d4 Translated using Weblate (Ukrainian)
Currently translated at 11.8% (184 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-20 17:29:07 +00:00
Elian Doran
6f83ac4822 Port settings to React (#6660) 2025-08-20 20:28:54 +03:00
perf3ct
d358924324 feat(docs): implement swagger ui endpoint for internal api 2025-08-20 17:14:44 +00:00
perf3ct
f9a3606ca2 feat(docs): implement swagger ui endpoint for internal api 2025-08-20 17:11:54 +00:00
Elian Doran
33299ad51e chore(deps): update package lock 2025-08-20 19:45:49 +03:00
Elian Doran
8752182e7e Bump mermaid from 11.9.0 to 11.10.0 in /apps/client (#6717) 2025-08-20 19:22:44 +03:00
Elian Doran
0551ac8ead feat(ci): don't run checks outside main repo 2025-08-20 19:22:21 +03:00
Elian Doran
6d5a11bd4d Translations update from Hosted Weblate (#6718) 2025-08-20 19:14:25 +03:00
Elian Doran
ce19d84247 test(e2e): i18n test broken due to button 2025-08-20 19:10:41 +03:00
Микола Копитін
f24aa45a3b Translated using Weblate (Ukrainian)
Currently translated at 39.6% (150 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/uk/
2025-08-20 18:02:18 +02:00
Микола Копитін
64a28a7e75 Translated using Weblate (Ukrainian)
Currently translated at 10.8% (168 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/uk/
2025-08-20 18:02:16 +02:00
Flowerlywind
249a755312 Translated using Weblate (Vietnamese)
Currently translated at 2.1% (34 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/vi/
2025-08-20 18:02:13 +02:00
Kuzma Simonov
a3d51a013c Translated using Weblate (Russian)
Currently translated at 88.6% (335 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ru/
2025-08-20 18:02:12 +02:00
repilac
839def9959 Translated using Weblate (Japanese)
Currently translated at 97.6% (369 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-20 18:02:10 +02:00
Kuzma Simonov
fd432a7100 Translated using Weblate (Russian)
Currently translated at 81.9% (1271 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-08-20 18:02:08 +02:00
repilac
60a07ce1e7 Translated using Weblate (Japanese)
Currently translated at 68.2% (1059 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-20 18:02:07 +02:00
diego diaz
88c5700d87 Translated using Weblate (Spanish)
Currently translated at 100.0% (1551 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2025-08-20 18:02:05 +02:00
Elian Doran
d59993abf6 fix(server): potential race condition when rotating logs 2025-08-20 19:00:41 +03:00
Elian Doran
0754011909 chore(react): fix type errors 2025-08-20 18:52:14 +03:00
Elian Doran
376bb66cab Merge remote-tracking branch 'origin/main' into react/settings
; Conflicts:
;	pnpm-lock.yaml
2025-08-20 18:32:57 +03:00
Elian Doran
588e15c633 fix(settings): not fitting properly on mobile 2025-08-20 18:17:52 +03:00
Elian Doran
93b8ad20d7 fix(react/settings): settings displayed inline 2025-08-20 18:17:42 +03:00
dependabot[bot]
e51b3d760d Bump mermaid from 11.9.0 to 11.10.0 in /apps/client
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.9.0 to 11.10.0.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.9.0...mermaid@11.10.0)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-version: 11.10.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-20 14:44:27 +00:00
Elian Doran
91f3bc4488 chore(deps): update svelte monorepo (#6707) 2025-08-20 08:41:54 +03:00
Elian Doran
3e80a99bbf fix(deps): update dependency mermaid to v11.10.0 [security] (#6700) 2025-08-20 08:41:11 +03:00
renovate[bot]
37cdb55e79 chore(deps): update svelte monorepo 2025-08-20 05:37:42 +00:00
renovate[bot]
58b66c0c95 fix(deps): update dependency mermaid to v11.10.0 [security] 2025-08-20 05:36:02 +00:00
Elian Doran
e5f9db86a1 fix(deps): update ckeditor monorepo to v46.0.2 (#6704) 2025-08-20 08:32:25 +03:00
Elian Doran
f138f99356 chore(deps): update pnpm to v10.15.0 (#6706) 2025-08-20 08:31:17 +03:00
renovate[bot]
c42f4b9814 fix(deps): update ckeditor monorepo to v46.0.2 2025-08-20 05:31:11 +00:00
Elian Doran
0a9fb886e3 fix(deps): update dependency @mermaid-js/layout-elk to v0.1.9 (#6705) 2025-08-20 08:31:04 +03:00
Elian Doran
3c4577201f chore(deps): update dependency vite to v7.1.3 (#6703) 2025-08-20 08:30:16 +03:00
Elian Doran
816421188f chore(deps): update dependency electron to v37.3.1 (#6702) 2025-08-20 08:29:36 +03:00
renovate[bot]
5b15d2c4c6 chore(deps): update pnpm to v10.15.0 2025-08-20 01:18:37 +00:00
renovate[bot]
4bc7165452 fix(deps): update dependency @mermaid-js/layout-elk to v0.1.9 2025-08-20 01:18:28 +00:00
renovate[bot]
82d6531e8c chore(deps): update dependency vite to v7.1.3 2025-08-20 01:17:10 +00:00
renovate[bot]
d6209035c3 chore(deps): update dependency electron to v37.3.1 2025-08-20 01:16:36 +00:00
Elian Doran
1d7799f981 refactor(react/settings): add names to all form groups 2025-08-19 23:34:25 +03:00
Elian Doran
51291a61e6 refactor(react/settings): associate IDs for labels 2025-08-19 22:54:23 +03:00
Elian Doran
0841603be0 refactor(react/settings): use FormGroup for time selector 2025-08-19 22:36:47 +03:00
Elian Doran
59ba6a0b1e chore(react/settings): use FormGroup for labels 2025-08-19 22:32:20 +03:00
Elian Doran
53eda46043 chore(react/settings): use translation for all units 2025-08-19 21:50:29 +03:00
Elian Doran
cbc9fb7d08 chore(react/settings): solve type errors 2025-08-19 21:41:05 +03:00
Elian Doran
1f479b20be chore(react/settings): use onBlur instead of onChange 2025-08-19 20:09:56 +03:00
Elian Doran
f00b8e9522 chore(react/settings): set 100% width for textarea 2025-08-19 17:39:41 +03:00
Elian Doran
c7dd271516 Translations update from Hosted Weblate (#6697) 2025-08-19 17:07:49 +03:00
Hosted Weblate
a947a61d65 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/
2025-08-19 16:02:30 +02:00
Székely Miklós
0122f1cc5e Translated using Weblate (Hungarian)
Currently translated at 6.3% (24 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/hu/
2025-08-19 16:02:27 +02:00
Wojciech O
acb905a3e6 Translated using Weblate (Polish)
Currently translated at 37.0% (140 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/pl/
2025-08-19 16:02:26 +02:00
Wojciech O
7422eb5598 Translated using Weblate (Polish)
Currently translated at 1.3% (21 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pl/
2025-08-19 16:02:22 +02:00
Kuzma Simonov
e721166f95 Translated using Weblate (Russian)
Currently translated at 50.2% (190 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ru/
2025-08-19 16:02:18 +02:00
acwr47
5a48130fa4 Translated using Weblate (Japanese)
Currently translated at 96.8% (366 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ja/
2025-08-19 16:02:16 +02:00
Kuzma Simonov
b60fe1ad10 Translated using Weblate (Russian)
Currently translated at 81.8% (1269 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-08-19 16:02:15 +02:00
gri-gri
1405b0147c Translated using Weblate (Russian)
Currently translated at 81.8% (1269 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ru/
2025-08-19 16:02:13 +02:00
acwr47
222a7a57bc Translated using Weblate (Japanese)
Currently translated at 67.8% (1052 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-19 16:02:12 +02:00
Elian Doran
cddf9f0242 Merge remote-tracking branch 'origin/main' into react/settings
; Conflicts:
;	apps/client/package.json
;	apps/client/src/translations/en/translation.json
;	apps/client/src/translations/tw/translation.json
;	pnpm-lock.yaml
2025-08-19 13:50:27 +03:00
Elian Doran
3e17ff5e7b chore(react/settings): clean up options widget 2025-08-19 13:47:15 +03:00
Elian Doran
04973094f2 feat(react/settings): port LLM settings 2025-08-19 13:46:13 +03:00
Elian Doran
018a6cb84a chore(react/settings): add back some checks for MFA 2025-08-19 10:54:02 +03:00
Elian Doran
44825af0c0 feat(react/settings): port OAuth settings 2025-08-19 10:51:05 +03:00
Elian Doran
cfb3607052 feat(react/settings): port totp settings 2025-08-19 10:37:14 +03:00
Elian Doran
c5ec928aac fix(deps): update dependency preact to v10.27.1 (#6690) 2025-08-19 08:40:36 +03:00
Elian Doran
8d0183a9fb chore(deps): update svelte monorepo (#6691) 2025-08-19 08:38:55 +03:00
Elian Doran
ecd4079871 chore(deps): update typescript-eslint monorepo to v8.40.0 (#6692) 2025-08-19 08:38:24 +03:00
Elian Doran
3ed975f2e6 fix(deps): update dependency marked to v16.2.0 (#6693) 2025-08-19 08:35:28 +03:00
renovate[bot]
c6deb537d5 fix(deps): update dependency marked to v16.2.0 2025-08-19 01:29:40 +00:00
renovate[bot]
e7b3d806a7 chore(deps): update typescript-eslint monorepo to v8.40.0 2025-08-19 01:28:54 +00:00
renovate[bot]
d1a0778b48 chore(deps): update svelte monorepo 2025-08-19 01:27:47 +00:00
renovate[bot]
378634567f fix(deps): update dependency preact to v10.27.1 2025-08-19 01:27:13 +00:00
Elian Doran
ed56ed2be0 feat(quick_search): format multi-line results better (#6672) 2025-08-18 23:14:15 +03:00
Elian Doran
648aa7e3b0 fix(hotkeys): interpret shortcut in the user's locale (#6681) 2025-08-18 23:10:59 +03:00
Elian Doran
73ff41f2b2 fix(react/settings): hook leak after closing tabs 2025-08-18 22:15:47 +03:00
Elian Doran
3837466cb3 feat(react/settings): react to external changes 2025-08-18 20:41:33 +03:00
Elian Doran
b97a5ef888 chore(react/settings): reimplement reset shortcuts 2025-08-18 19:47:40 +03:00
Elian Doran
2ff1276ebb Translations update from Hosted Weblate (#6686) 2025-08-18 19:06:42 +03:00
Elian Doran
227cf5de85 feat(react/settings): port protected session timeout 2025-08-18 19:00:42 +03:00
Elian Doran
ccf52be431 feat(react/settings): port tray options 2025-08-18 18:47:18 +03:00
Elian Doran
07713e988c feat(react/settings): port search engine settings 2025-08-18 18:43:27 +03:00
Elian Doran
f934318625 feat(react/settings): port revision snapshot list 2025-08-18 18:27:12 +03:00
Elian Doran
6fb90abd75 feat(react/settings): port network connections 2025-08-18 18:22:07 +03:00
Elian Doran
27cc33888a feat(react/settings): port share settings 2025-08-18 18:19:48 +03:00
Elian Doran
95af901808 feat(react/settings): port HTML import tags 2025-08-18 18:07:58 +03:00
Elian Doran
c5a7f84250 feat(react/settings): port note revision snapshot interval 2025-08-18 17:51:45 +03:00
Elian Doran
a71d28500d feat(react/settings): port attachment erasure timeout 2025-08-18 17:42:39 +03:00
Elian Doran
436fd16f3a feat(react/settings): port note erasure timeout 2025-08-18 17:37:20 +03:00
Székely Miklós
ca34bf42f6 Added translation using Weblate (Hungarian) 2025-08-18 15:38:27 +02:00
Székely Miklós
fbf2315f57 Added translation using Weblate (Hungarian) 2025-08-18 15:38:26 +02:00
tomek7667
72f50dcb6b Translated using Weblate (Polish)
Currently translated at 16.4% (62 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/pl/
2025-08-18 15:38:25 +02:00
tomek7667
fd4c2f79a7 Translated using Weblate (Polish)
Currently translated at 0.6% (10 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/pl/
2025-08-18 15:38:25 +02:00
acwr47
72f9335213 Translated using Weblate (Japanese)
Currently translated at 66.9% (1038 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2025-08-18 15:38:25 +02:00
Elian Doran
53d97047a3 feat(react/settings): port code read-only size 2025-08-18 16:10:05 +03:00
Elian Doran
2ba3666e23 feat(react/settings): port code mime types 2025-08-18 15:55:18 +03:00
Elian Doran
4a1d379ab4 feat(react/settings): port code editor appearance 2025-08-18 15:02:58 +03:00
Elian Doran
73167e1e30 feat(react/settings): port code editor settings 2025-08-18 14:27:45 +03:00
Elian Doran
ffc13f5de3 feat(react/settings): port date time format 2025-08-18 14:19:38 +03:00
tomek7667
9ba23d49d8 Added translation using Weblate (Polish) 2025-08-18 12:32:13 +02:00
tomek7667
222a6c48a7 Added translation using Weblate (Polish) 2025-08-18 12:32:13 +02:00
VortexP
e33208e6ec Translated using Weblate (Finnish)
Currently translated at 6.1% (95 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fi/
2025-08-18 12:32:12 +02:00
acwr47
af8781eaa7 Translated using Weblate (Japanese)
Currently translated at 65.5% (1016 of 1551 strings)

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

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/zh_Hant/
2025-08-18 12:32:12 +02:00
Francis C
0a7aff507c Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (1551 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
2025-08-18 12:32:12 +02:00
Francis C
103532aad9 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (1551 of 1551 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-18 12:32:11 +02:00
Elian Doran
16939e9fd5 feat(react/settings): port auto read-only size 2025-08-18 12:14:38 +03:00
Elian Doran
4ef6169041 feat(react/settings): port highlight list settings 2025-08-18 12:11:29 +03:00
Elian Doran
9ebee42118 feat(react/settings): port TOC settings 2025-08-18 11:26:58 +03:00
Elian Doran
234d3997b1 feat(react/settings): port code block settings 2025-08-18 11:21:09 +03:00
Elian Doran
3ba0bcea4e feat(react/settings): port heading style 2025-08-18 09:47:18 +03:00
Elian Doran
701855344e feat(react/settings): port text features 2025-08-18 09:40:36 +03:00
Elian Doran
71b627fbc7 feat(react/settings): port text formatting toolbar 2025-08-18 09:34:16 +03:00
Elian Doran
5a4fc2c690 fix(deps): update dependency mind-elixir to v5.0.6 (#6682) 2025-08-18 09:07:10 +03:00
Elian Doran
0d67db52a2 chore(deps): update dependency chalk to v5.6.0 (#6683) 2025-08-18 09:06:25 +03:00
perf3ct
d971554201 feat(quick_search): also show the tags/attributes in quick search results 2025-08-18 03:20:04 +00:00
perf3ct
8fd7d7176e Merge branch 'main' into feat/quick-search-multiline-results 2025-08-18 00:30:58 +00:00
renovate[bot]
675575eed9 chore(deps): update dependency chalk to v5.6.0 2025-08-18 00:29:14 +00:00
renovate[bot]
2122cde293 fix(deps): update dependency mind-elixir to v5.0.6 2025-08-18 00:28:33 +00:00
Romain DEP.
b68a554bba fix(hotkeys): interpret shortcut in the user's locale
fixes #6547
2025-08-17 23:48:48 +02:00
Elian Doran
33043c7133 chore(call_to_action): add missing translation 2025-08-17 23:37:33 +03:00
Elian Doran
2e0f606a7a chore(release): prepare for v0.98.0 2025-08-17 22:46:16 +03:00
Elian Doran
87878dd6a7 Translations update from Hosted Weblate (#6679) 2025-08-17 22:20:39 +03:00
morteza rahvard
5296e073cc Translated using Weblate (Persian)
Currently translated at 0.7% (12 of 1550 strings)

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

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

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

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

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

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

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

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/fr/
2025-08-16 20:01:57 +00:00
perf3ct
bbf8d757cd feat(quick_search): format multi-line results better 2025-08-16 19:16:27 +00:00
Elian Doran
318d504fad chore(deps): update dependency @types/node to v22.17.2 (#6665) 2025-08-16 08:55:02 +03:00
Elian Doran
fd5038148c Merge branch 'main' into renovate/node-22.x 2025-08-16 08:55:00 +03:00
renovate[bot]
693ca9291e chore(deps): update dependency @types/node to v22.17.2 2025-08-16 05:45:48 +00:00
Elian Doran
cfd8afc226 chore(deps): update dependency @sveltejs/kit to v2.31.0 (#6666) 2025-08-16 08:44:44 +03:00
Elian Doran
3e52ca7600 chore(deps): update dependency electron to v37.3.0 (#6667) 2025-08-16 08:44:09 +03:00
renovate[bot]
482522e802 chore(deps): update dependency electron to v37.3.0 2025-08-16 02:10:14 +00:00
renovate[bot]
8b5b6a01c6 chore(deps): update dependency @sveltejs/kit to v2.31.0 2025-08-16 02:09:37 +00:00
Elian Doran
5614891d92 fix(react/settings): unnecessary top margin 2025-08-16 00:22:18 +03:00
Elian Doran
b9b4961f3c fix(react/settings): shortcuts saved upon render 2025-08-16 00:13:18 +03:00
Elian Doran
7b83b20339 feat(react/settings): port shortcuts 2025-08-16 00:08:51 +03:00
Elian Doran
e4403dd316 Translations update from Hosted Weblate (#6664) 2025-08-15 23:04:33 +03:00
ali mohammadi
3f267fe6c9 Added translation using Weblate (Persian) 2025-08-15 21:31:45 +02:00
ali mohammadi
3229471485 Added translation using Weblate (Persian) 2025-08-15 21:31:44 +02:00
neketos851
62bac1adf9 Translated using Weblate (Ukrainian)
Currently translated at 0.7% (3 of 378 strings)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/fr/
2025-08-15 14:01:58 +02:00
Elian Doran
3e00e490cf chore(react/settings): start porting protected session timeout 2025-08-15 14:23:54 +03:00
Elian Doran
c02ed17ebc feat(react/settings): port change password 2025-08-15 14:19:49 +03:00
Elian Doran
fb559d66fe feat(react/settings): port spellcheck 2025-08-15 13:52:52 +03:00
Elian Doran
25dce64c3b feat(react/settings): port backup DB list 2025-08-15 13:16:57 +03:00
Elian Doran
6f19fde76e feat(react/settings): port backup DB now 2025-08-15 12:52:59 +03:00
Elian Doran
33ae91f49c feat(backup): display full path to the database 2025-08-15 12:52:50 +03:00
Elian Doran
99c179e29a feat(react/settings): port automatic backup 2025-08-15 12:47:35 +03:00
Elian Doran
1dbcb5a027 fix(react/dialogs): unable to chain prompts 2025-08-15 12:33:23 +03:00
Elian Doran
54d613e00e fix(react/dialogs): prompt not setting default value properly 2025-08-15 12:15:29 +03:00
Elian Doran
1f8aa90482 fix(react/settings): etapi list not always reacting to changes 2025-08-15 12:11:40 +03:00
Elian Doran
c9dcbef014 feat(react/settings): port etapi tokens 2025-08-15 12:00:11 +03:00
Elian Doran
68086ec3f1 feat(react/settings): port sync test 2025-08-15 11:30:48 +03:00
Elian Doran
f62078d02b feat(react/settings): port sync options 2025-08-15 11:21:19 +03:00
SiriusXT
ab1d8594ea Fix (Update): No update notification in the global menu 2025-08-15 16:04:59 +08:00
Elian Doran
c368ec3c38 feat(react/settings): port content languages 2025-08-15 10:26:25 +03:00
Elian Doran
1a15782686 fix(deps): update dependency i18next to v25.3.6 (#6652) 2025-08-15 09:05:12 +03:00
Elian Doran
3bd0aeef77 chore(deps): update svelte monorepo (#6654) 2025-08-15 09:04:48 +03:00
Elian Doran
b463baedd2 chore(deps): update dependency tsx to v4.20.4 (#6632) 2025-08-15 09:04:22 +03:00
Elian Doran
ae77c41dab chore(deps): update tailwindcss monorepo to v4.1.12 (#6651) 2025-08-15 09:03:58 +03:00
Elian Doran
807d909acd chore(deps): update dependency @anthropic-ai/sdk to v0.60.0 (#6653) 2025-08-15 09:03:36 +03:00
Elian Doran
fa4f5f526e chore(deps): update dependency turndown to v7.2.1 (#6650) 2025-08-15 09:03:13 +03:00
renovate[bot]
edff43cdb3 chore(deps): update dependency tsx to v4.20.4 2025-08-15 05:51:01 +00:00
renovate[bot]
46fe45528c chore(deps): update svelte monorepo 2025-08-15 02:17:16 +00:00
renovate[bot]
b4b53da6a4 chore(deps): update dependency @anthropic-ai/sdk to v0.60.0 2025-08-15 02:16:43 +00:00
renovate[bot]
41fd270080 fix(deps): update dependency i18next to v25.3.6 2025-08-15 02:16:06 +00:00
renovate[bot]
410bb3cdca chore(deps): update tailwindcss monorepo to v4.1.12 2025-08-15 02:15:22 +00:00
renovate[bot]
bc6fc24fbd chore(deps): update dependency turndown to v7.2.1 2025-08-15 02:14:46 +00:00
Elian Doran
c039f06c2b chore(react/settings): fix a margin between radios 2025-08-15 00:19:37 +03:00
Elian Doran
520effbbb7 chore(react/settings): bring back style 2025-08-15 00:08:43 +03:00
Elian Doran
a42d780724 refactor(react/settings): fix type errors 2025-08-14 23:58:58 +03:00
Elian Doran
da92255dd6 refactor(react/settings): use better option mechanism 2025-08-14 23:54:32 +03:00
Elian Doran
cce3d3bce8 chore(react/settings): port date settings 2025-08-14 23:51:27 +03:00
Elian Doran
f524e99290 chore(react/settings): port first day of the week 2025-08-14 23:37:25 +03:00
Elian Doran
ba19fc7cf3 chore(react/settings): port formatting locale 2025-08-14 23:32:44 +03:00
Elian Doran
22c3de582f chore(react/settings): port language selection 2025-08-14 23:25:44 +03:00
Elian Doran
48896e67cb chore(react/settings): remove unnecessary ribbon settings 2025-08-14 23:11:13 +03:00
Elian Doran
16cd91eb02 feat(react/settings): port database anonymization 2025-08-14 23:10:53 +03:00
Elian Doran
7e03774b8e feat(react/settings): port vacuum database 2025-08-14 22:42:49 +03:00
Elian Doran
a04f6e3858 feat(react/settings): port integrity check 2025-08-14 22:40:54 +03:00
Elian Doran
96eb1be556 feat(react/settings): port advanced sync options 2025-08-14 22:35:58 +03:00
Jon Fuller
f8e20a1405 Update README.md (#6648) 2025-08-14 12:31:35 -07:00
Elian Doran
c67c3a6861 feat(react/settings): port images 2025-08-14 22:27:07 +03:00
Elian Doran
d04897e011 feat(react/settings): port related settings 2025-08-14 22:05:45 +03:00
MeIchthys
558ae1a2ea Update README.md
- Update links to point to new TriliumNext/Trilium repo
- Update a couple broken links
2025-08-14 14:59:08 -04:00
Elian Doran
64bffb82b1 feat(react/settings): port max content width 2025-08-14 21:55:16 +03:00
Elian Doran
81ac390eab feat(react/settings): port electron integration 2025-08-14 21:44:30 +03:00
Elian Doran
0db556fac2 feat(react/settings): port font size 2025-08-14 21:31:09 +03:00
Elian Doran
2793df06c4 fix(react/settings): useTriliumEvent not cleaning up properly 2025-08-14 21:05:24 +03:00
Elian Doran
e7b448e2bc fix(react/settings): event leak in useOption 2025-08-14 19:55:45 +03:00
Elian Doran
d2bc72d54f chore(react/settings): port font family settings 2025-08-14 19:25:22 +03:00
Elian Doran
83b22b4861 chore(react/settings): allow combo to have any possible object structure 2025-08-14 18:51:32 +03:00
Elian Doran
d42a949602 refactor(react/settings): use separate components inside same file 2025-08-14 18:29:08 +03:00
Elian Doran
83e1512b59 feat(react/settings): port override theme fonts 2025-08-14 18:26:40 +03:00
Elian Doran
ba6a1ec584 feat(react/settings): port theme switch 2025-08-14 18:18:45 +03:00
Elian Doran
6685e583f2 chore(react/settings): make layout switch functional 2025-08-14 17:59:17 +03:00
Elian Doran
d6032c912e chore(react/settings): improve layout 2025-08-14 17:54:52 +03:00
Elian Doran
25527ecc21 fix(react/settings): not working properly when side-by-side in split 2025-08-14 17:53:24 +03:00
Elian Doran
e0e7bd42cc feat(react/settings): react to property change 2025-08-14 17:47:45 +03:00
Elian Doran
fbc1af56ed feat(react/settings): basic hook to read Trilium option 2025-08-14 17:36:11 +03:00
Elian Doran
8ff108db9e feat(react/settings): basic rendering of React content widgets 2025-08-14 17:19:38 +03:00
Elian Doran
1dfcf960d3 fix(client): missing calendar view language support 2025-08-14 15:20:08 +03:00
Elian Doran
9bdc51a3fb feat(i18n): add Japanese language 2025-08-14 14:51:57 +03:00
Elian Doran
dbf3bcfacf Merge remote-tracking branch 'weblate/main' 2025-08-14 14:34:30 +03:00
Elian Doran
3d5b269315 chore(docs): fix file 2025-08-14 14:23:37 +03:00
Elian Doran
48f97da9cc chore(forge/rpm): rename key properly 2025-08-14 14:10:33 +03:00
Elian Doran
9c954fbd81 Create CNAME 2025-08-14 13:53:25 +03:00
Elian Doran
c6bd41654f chore(forge/rpm): add public key 2025-08-14 13:40:23 +03:00
Francis C
d65a74bb23 Translated using Weblate (Japanese)
Currently translated at 96.8% (366 of 378 strings)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
2025-08-14 09:02:15 +00:00
Elian Doran
d33d27ee82 feat(forge): validate rpm signing 2025-08-14 11:45:59 +03:00
Elian Doran
e2b13573ae feat(forge): rpm signing 2025-08-14 10:43:38 +03:00
Elian Doran
ec74f5f1de feat(logs): provide an option to keep all logs (#6644) 2025-08-14 08:51:46 +03:00
Elian Doran
5dee56debc Add Traditional Chinese translation for README file & fix Docker Hub URL (#6645) 2025-08-14 08:43:08 +03:00
Francis C.
5623fc992d Update README-ZH_TW.md (tiny fix) 2025-08-14 12:04:45 +08:00
Francis C.
1d28bfc570 Update README-ZH_TW.md (tiny fix) 2025-08-14 11:25:53 +08:00
Francis C.
084327e973 Revise some words for Simplified Chinese translation 2025-08-14 11:20:32 +08:00
Francis C.
b2885efdc1 Update README-ZH_CN.md 2025-08-14 10:56:54 +08:00
Francis C.
b65a75f138 fix relative path for URLs 2025-08-14 10:27:41 +08:00
Francis C.
0ee7f50bb4 Move readme to docs folder 2025-08-14 10:15:03 +08:00
Francis C.
02ce21bc18 Add readme file translation for Traditional Chinese & fix Docker Hub URL 2025-08-14 10:12:14 +08:00
Romain DEP.
3ba487bb00 feat(logs): provide an option to keep all logs 2025-08-13 23:35:31 +02:00
562 changed files with 43258 additions and 33627 deletions

View File

@@ -1,6 +1,6 @@
root = true
[*.{js,ts}]
[*.{js,ts,.tsx}]
charset = utf-8
end_of_line = lf
indent_size = 4

1
.env
View File

@@ -1 +0,0 @@
NODE_OPTIONS=--max_old_space_size=4096

View File

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

View File

@@ -10,7 +10,7 @@ runs:
steps:
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 22
cache: "pnpm"
@@ -23,7 +23,7 @@ runs:
shell: bash
run: |
pnpm run chore:update-build-info
pnpm nx --project=server package
pnpm run --filter server package
- name: Prepare artifacts
shell: bash
run: |

View File

@@ -1,40 +0,0 @@
---
applyTo: '**'
---
// This file is automatically generated by Nx Console
You are in an nx workspace using Nx 21.3.9 and pnpm as the package manager.
You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user:
# General Guidelines
- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture
- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration
- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors
- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool
# Generation Guidelines
If the user wants to generate something, use the following flow:
- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable
- get the available generators using the 'nx_generators' tool
- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them
- get generator details using the 'nx_generator_schema' tool
- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure
- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic
- open the generator UI using the 'nx_open_generate_ui' tool
- wait for the user to finish the generator
- read the generator log file using the 'nx_read_generator_log' tool
- use the information provided in the log file to answer the user's question or continue with what they were doing
# Running Tasks Guidelines
If the user wants help with tasks or commands (which include keywords like "test", "build", "lint", or other similar actions), use the following flow:
- Use the 'nx_current_running_tasks_details' tool to get the list of tasks (this can include tasks that were completed, stopped or failed).
- If there are any tasks, ask the user if they would like help with a specific task then use the 'nx_current_running_task_output' tool to get the terminal output for that task/command
- Use the terminal output from 'nx_current_running_task_output' to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
- If the user would like to rerun the task or command, always use `nx run <taskId>` to rerun in the terminal. This will ensure that the task will run in the nx context and will be run the same way it originally executed
- If the task was marked as "continuous" do not offer to rerun the task. This task is already running and the user can see the output in the terminal. You can use 'nx_current_running_task_output' to get the output of the task to verify the output.

View File

@@ -12,6 +12,7 @@ jobs:
steps:
- name: Check if PRs have conflicts
uses: eps1lon/actions-label-merge-conflict@v3
if: github.repository == ${{ vars.REPO_MAIN }}
with:
dirtyLabel: "merge-conflicts"
repoToken: "${{ secrets.MERGE_CONFLICT_LABEL_PAT }}"

190
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,190 @@
# GitHub Actions workflow for deploying MkDocs documentation to Cloudflare Pages
# This workflow builds and deploys your MkDocs site when changes are pushed to main
name: Deploy MkDocs Documentation
on:
# Trigger on push to main branch
push:
branches:
- main
- master # Also support master branch
# Only run when docs files change
paths:
- 'docs/**'
- 'README.md' # README is synced to docs/index.md
- 'mkdocs.yml'
- 'requirements-docs.txt'
- '.github/workflows/deploy-docs.yml'
- 'scripts/fix-mkdocs-structure.ts'
- 'validate-docs-links.ts'
# Allow manual triggering from Actions tab
workflow_dispatch:
# Run on pull requests for preview deployments
pull_request:
branches:
- main
- master
paths:
- 'docs/**'
- 'README.md' # README is synced to docs/index.md
- 'mkdocs.yml'
- 'requirements-docs.txt'
- '.github/workflows/deploy-docs.yml'
- 'scripts/fix-mkdocs-structure.ts'
- 'validate-docs-links.ts'
jobs:
build-and-deploy:
name: Build and Deploy MkDocs
runs-on: ubuntu-latest
timeout-minutes: 10
# Required permissions for deployment
permissions:
contents: read
deployments: write
pull-requests: write # For PR preview comments
id-token: write # For OIDC authentication (if needed)
steps:
- name: Checkout Repository
uses: actions/checkout@v5
with:
fetch-depth: 0 # Fetch all history for git info and mkdocs-git-revision-date plugin
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.13'
cache: 'pip'
cache-dependency-path: 'requirements-docs.txt'
- name: Install MkDocs and Dependencies
run: |
pip install --upgrade pip
pip install -r requirements-docs.txt
env:
PIP_DISABLE_PIP_VERSION_CHECK: 1
# Setup pnpm before fixing docs structure
- name: Setup pnpm
uses: pnpm/action-setup@v4
# Setup Node.js with pnpm
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '22'
cache: 'pnpm'
# Install Node.js dependencies for the TypeScript script
- name: Install Dependencies
run: |
pnpm install --frozen-lockfile
- name: Fix Documentation Structure
run: |
# Fix duplicate navigation entries by moving overview pages to index.md
pnpm run chore:fix-mkdocs-structure
- name: Build MkDocs Site
run: |
# Build with strict mode but allow expected warnings
mkdocs build --verbose || {
EXIT_CODE=$?
# Check if the only issue is expected warnings
if mkdocs build 2>&1 | grep -E "WARNING.*(README|not found)" && \
[ $(mkdocs build 2>&1 | grep -c "ERROR") -eq 0 ]; then
echo "✅ Build succeeded with expected warnings"
mkdocs build --verbose
else
echo "❌ Build failed with unexpected errors"
exit $EXIT_CODE
fi
}
- name: Validate Built Site
run: |
# Basic validation that important files exist
test -f site/index.html || (echo "ERROR: site/index.html not found" && exit 1)
test -f site/sitemap.xml || (echo "ERROR: site/sitemap.xml not found" && exit 1)
test -d site/assets || (echo "ERROR: site/assets directory not found" && exit 1)
echo "✅ Site validation passed"
- name: Validate Documentation Links
run: |
# Run the TypeScript link validation script
pnpm tsx validate-docs-links.ts
# Install wrangler globally to avoid workspace issues
- name: Install Wrangler
run: |
npm install -g wrangler
# Deploy using Wrangler (use pre-installed wrangler)
- name: Deploy to Cloudflare Pages
id: deploy
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy site --project-name=trilium-docs --branch=${{ github.ref_name }}
wranglerVersion: '' # Use pre-installed version
# Deploy preview for PRs
- name: Deploy Preview to Cloudflare Pages
id: preview-deployment
if: github.event_name == 'pull_request'
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy site --project-name=trilium-docs --branch=pr-${{ github.event.pull_request.number }}
wranglerVersion: '' # Use pre-installed version
# Post deployment URL as PR comment
- name: Comment PR with Preview URL
if: github.event_name == 'pull_request'
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = context.issue.number;
// Construct preview URL based on Cloudflare Pages pattern
const previewUrl = `https://pr-${prNumber}.trilium-docs.pages.dev`;
const mainUrl = 'https://docs.triliumnotes.org';
// Check if we already commented
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber
});
const botComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Documentation preview is ready')
);
const commentBody = `📚 Documentation preview is ready!\n\n🔗 Preview URL: ${previewUrl}\n📖 Production URL: ${mainUrl}\n\n✅ All checks passed\n\n_This preview will be updated automatically with new commits._`;
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
} else {
// Create new comment
await github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
}

View File

@@ -19,45 +19,24 @@ permissions:
pull-requests: write # for PR comments
jobs:
check-affected:
name: Check affected jobs (NX)
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v5
with:
fetch-depth: 0 # needed for https://github.com/marketplace/actions/nx-set-shas
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- uses: nrwl/nx-set-shas@v4
- name: Check affected
run: pnpm nx affected --verbose -t typecheck build rebuild-deps test-build
test_dev:
name: Test development
runs-on: ubuntu-latest
needs:
- check-affected
steps:
- name: Checkout the repository
uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm typecheck
- name: Run the unit tests
run: pnpm run test:all
@@ -66,7 +45,6 @@ jobs:
runs-on: ubuntu-latest
needs:
- test_dev
- check-affected
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
@@ -75,7 +53,7 @@ jobs:
- name: Update build info
run: pnpm run chore:update-build-info
- name: Trigger client build
run: pnpm nx run client:build
run: pnpm client:build
- name: Send client bundle stats to RelativeCI
if: false
uses: relative-ci/agent-action@v3
@@ -83,7 +61,7 @@ jobs:
webpackStatsFile: ./apps/client/dist/webpack-stats.json
key: ${{ secrets.RELATIVE_CI_CLIENT_KEY }}
- name: Trigger server build
run: pnpm nx run server:build
run: pnpm run server:build
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
@@ -95,7 +73,6 @@ jobs:
runs-on: ubuntu-latest
needs:
- build_docker
- check-affected
strategy:
matrix:
include:
@@ -112,7 +89,7 @@ jobs:
- name: Update build info
run: pnpm run chore:update-build-info
- name: Trigger build
run: pnpm nx run server:build
run: pnpm server:build
- name: Set IMAGE_NAME to lowercase
run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV

View File

@@ -44,7 +44,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 22
cache: "pnpm"
@@ -82,7 +82,7 @@ jobs:
require-healthy: true
- name: Run Playwright tests
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm exec nx run server-e2e:e2e
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm --filter=server-e2e e2e
- name: Upload Playwright trace
if: failure()
@@ -144,7 +144,7 @@ jobs:
uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 22
cache: 'pnpm'

View File

@@ -27,7 +27,7 @@ permissions:
jobs:
nightly-electron:
if: github.repository == 'TriliumNext/Trilium'
if: github.repository == ${{ vars.REPO_MAIN }}
name: Deploy nightly
strategy:
fail-fast: false
@@ -51,13 +51,12 @@ jobs:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- uses: nrwl/nx-set-shas@v4
- name: Update nightly version
run: npm run chore:ci-update-nightly-version
- name: Run the build
@@ -76,9 +75,10 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
- name: Publish release
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
if: ${{ github.event_name != 'pull_request' }}
with:
make_latest: false
@@ -97,7 +97,7 @@ jobs:
path: apps/desktop/upload
nightly-server:
if: github.repository == 'TriliumNext/Trilium'
if: github.repository == ${{ vars.REPO_MAIN }}
name: Deploy server nightly
strategy:
fail-fast: false
@@ -119,7 +119,7 @@ jobs:
arch: ${{ matrix.arch }}
- name: Publish release
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
if: ${{ github.event_name != 'pull_request' }}
with:
make_latest: false

View File

@@ -19,14 +19,8 @@ jobs:
filter: tree:0
fetch-depth: 0
# This enables task distribution via Nx Cloud
# Run this command as early as possible, before dependencies are installed
# Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
# Connect your workspace by running "nx connect" and uncomment this line to enable task distribution
# - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="e2e-ci"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: 22
cache: 'pnpm'
@@ -34,10 +28,12 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps
- uses: nrwl/nx-set-shas@v4
# Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
# - run: npx nx-cloud record -- echo Hello World
# Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected
# When you enable task distribution, run the e2e-ci task instead of e2e
- run: pnpm exec nx affected -t e2e --exclude desktop-e2e
- run: pnpm --filter server-e2e e2e
- name: Upload test report
if: failure()
uses: actions/upload-artifact@v4
with:
name: e2e report
path: apps/server-e2e/test-output

View File

@@ -35,13 +35,12 @@ jobs:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- uses: nrwl/nx-set-shas@v4
- name: Run the build
uses: ./.github/actions/build-electron
with:
@@ -58,6 +57,7 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGN_KEY }}
- name: Upload the artifact
uses: actions/upload-artifact@v4
@@ -114,7 +114,7 @@ jobs:
path: upload
- name: Publish stable release
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
with:
draft: false
body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md

11
.gitignore vendored
View File

@@ -1,4 +1,5 @@
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
/.cache
# compiled output
dist
@@ -32,14 +33,11 @@ testem.log
.DS_Store
Thumbs.db
.nx/cache
.nx/workspace-data
vite.config.*.timestamp*
vitest.config.*.timestamp*
test-output
apps/*/data
apps/*/data*
apps/*/out
upload
@@ -47,4 +45,7 @@ upload
*.tsbuildinfo
/result
.svelte-kit
.svelte-kit
# docs
site/

2
.nvmrc
View File

@@ -1 +1 @@
22.18.0
22.19.0

View File

@@ -5,7 +5,6 @@
"lokalise.i18n-ally",
"ms-azuretools.vscode-docker",
"ms-playwright.playwright",
"nrwl.angular-console",
"redhat.vscode-yaml",
"tobermory.es6-string-html",
"vitest.explorer",

8
.vscode/mcp.json vendored
View File

@@ -1,8 +0,0 @@
{
"servers": {
"nx-mcp": {
"type": "http",
"url": "http://localhost:9461/mcp"
}
}
}

View File

@@ -35,6 +35,5 @@
"docs/**/*.png": true,
"apps/server/src/assets/doc_notes/**": true,
"apps/edit-docs/demo/**": true
},
"nxConsole.generateAiAgentRules": true
}
}

View File

@@ -20,5 +20,10 @@
"scope": "typescript",
"prefix": "jqf",
"body": ["private $${1:name}!: JQuery<HTMLElement>;"]
},
"region": {
"scope": "css",
"prefix": "region",
"body": ["/* #region ${1:name} */\n$0\n/* #endregion */"]
}
}

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Overview
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. It's built as a TypeScript monorepo using NX, with multiple applications and shared packages.
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. It's built as a TypeScript monorepo using pnpm, with multiple applications and shared packages.
## Development Commands
@@ -14,12 +14,9 @@ Trilium Notes is a hierarchical note-taking application with advanced features l
### Running Applications
- `pnpm run server:start` - Start development server (http://localhost:8080)
- `pnpm nx run server:serve` - Alternative server start command
- `pnpm nx run desktop:serve` - Run desktop Electron app
- `pnpm run server:start-prod` - Run server in production mode
### Building
- `pnpm nx build <project>` - Build specific project (server, client, desktop, etc.)
- `pnpm run client:build` - Build client application
- `pnpm run server:build` - Build server application
- `pnpm run electron:build` - Build desktop application
@@ -28,13 +25,8 @@ Trilium Notes is a hierarchical note-taking application with advanced features l
- `pnpm test:all` - Run all tests (parallel + sequential)
- `pnpm test:parallel` - Run tests that can run in parallel
- `pnpm test:sequential` - Run tests that must run sequentially (server, ckeditor5-mermaid, ckeditor5-math)
- `pnpm nx test <project>` - Run tests for specific project
- `pnpm coverage` - Generate coverage reports
### Linting & Type Checking
- `pnpm nx run <project>:lint` - Lint specific project
- `pnpm nx run <project>:typecheck` - Type check specific project
## Architecture Overview
### Monorepo Structure
@@ -94,7 +86,6 @@ Frontend uses a widget system (`apps/client/src/widgets/`):
- `apps/server/src/assets/db/schema.sql` - Core database structure
4. **Configuration**:
- `nx.json` - NX workspace configuration
- `package.json` - Project dependencies and scripts
## Note Types and Features
@@ -154,7 +145,7 @@ Trilium provides powerful user scripting capabilities:
- Update schema in `apps/server/src/assets/db/schema.sql`
## Build System Notes
- Uses NX for monorepo management with build caching
- Uses pnpm for monorepo management
- Vite for fast development builds
- ESBuild for production optimization
- pnpm workspaces for dependency management

View File

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

View File

@@ -35,13 +35,13 @@
"chore:generate-openapi": "tsx bin/generate-openapi.js"
},
"devDependencies": {
"@playwright/test": "1.54.2",
"@stylistic/eslint-plugin": "5.2.3",
"@playwright/test": "1.55.0",
"@stylistic/eslint-plugin": "5.3.1",
"@types/express": "5.0.3",
"@types/node": "22.17.1",
"@types/node": "22.18.1",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.33.0",
"eslint": "9.35.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25",
"jsdoc": "4.0.4",
@@ -49,7 +49,7 @@
"rcedit": "4.0.1",
"rimraf": "6.0.1",
"tslib": "2.8.1",
"typedoc": "0.28.10",
"typedoc": "0.28.12",
"typedoc-plugin-missing-exports": "4.1.0"
},
"optionalDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@triliumnext/client",
"version": "0.97.2",
"version": "0.98.1",
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
"private": true,
"license": "AGPL-3.0-only",
@@ -9,8 +9,13 @@
"email": "contact@eliandoran.me",
"url": "https://github.com/TriliumNext/Notes"
},
"scripts": {
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build",
"test": "vitest",
"circular-deps": "dpdm -T src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
},
"dependencies": {
"@eslint/js": "9.33.0",
"@eslint/js": "9.35.0",
"@excalidraw/excalidraw": "0.18.0",
"@fullcalendar/core": "6.1.19",
"@fullcalendar/daygrid": "6.1.19",
@@ -19,7 +24,7 @@
"@fullcalendar/multimonth": "6.1.19",
"@fullcalendar/timegrid": "6.1.19",
"@maplibre/maplibre-gl-leaflet": "0.1.3",
"@mermaid-js/layout-elk": "0.1.8",
"@mermaid-js/layout-elk": "0.2.0",
"@mind-elixir/node-menu": "5.0.0",
"@popperjs/core": "2.11.8",
"@triliumnext/ckeditor5": "workspace:*",
@@ -28,15 +33,15 @@
"@triliumnext/highlightjs": "workspace:*",
"@triliumnext/share-theme": "workspace:*",
"autocomplete.js": "0.38.1",
"bootstrap": "5.3.7",
"bootstrap": "5.3.8",
"boxicons": "2.1.4",
"dayjs": "1.11.13",
"dayjs": "1.11.18",
"dayjs-plugin-utc": "0.1.2",
"debounce": "2.2.0",
"draggabilly": "3.0.0",
"force-graph": "1.50.1",
"force-graph": "1.51.0",
"globals": "16.3.0",
"i18next": "25.3.4",
"i18next": "25.5.2",
"i18next-http-backend": "3.0.2",
"jquery": "3.7.1",
"jquery.fancytree": "2.38.5",
@@ -46,12 +51,13 @@
"leaflet": "1.9.4",
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
"marked": "16.1.2",
"mermaid": "11.9.0",
"mind-elixir": "5.0.5",
"marked": "16.2.1",
"mermaid": "11.11.0",
"mind-elixir": "5.1.1",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.27.0",
"preact": "10.27.1",
"react-i18next": "15.7.3",
"split.js": "1.6.5",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",
@@ -61,27 +67,14 @@
"@ckeditor/ckeditor5-inspector": "5.0.0",
"@preact/preset-vite": "2.10.2",
"@types/bootstrap": "5.2.10",
"@types/jquery": "3.5.32",
"@types/jquery": "3.5.33",
"@types/leaflet": "1.9.20",
"@types/leaflet-gpx": "1.3.7",
"@types/leaflet-gpx": "1.3.8",
"@types/mark.js": "8.11.12",
"@types/tabulator-tables": "6.2.10",
"copy-webpack-plugin": "13.0.1",
"happy-dom": "18.0.1",
"script-loader": "0.7.2",
"vite-plugin-static-copy": "3.1.1"
},
"nx": {
"name": "client",
"targets": {
"serve": {
"dependsOn": [
"^build"
]
},
"circular-deps": {
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
}
}
"vite-plugin-static-copy": "3.1.2"
}
}

View File

@@ -1,6 +1,6 @@
import froca from "../services/froca.js";
import RootCommandExecutor from "./root_command_executor.js";
import Entrypoints, { type SqlExecuteResults } from "./entrypoints.js";
import Entrypoints from "./entrypoints.js";
import options from "../services/options.js";
import utils, { hasTouchBar } from "../services/utils.js";
import zoomComponent from "./zoom.js";
@@ -31,16 +31,14 @@ import { StartupChecks } from "./startup_checks.js";
import type { CreateNoteOpts } from "../services/note_create.js";
import { ColumnComponent } from "tabulator-tables";
import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx";
import type RootContainer from "../widgets/containers/root_container.js";
import { SqlExecuteResults } from "@triliumnext/commons";
interface Layout {
getRootWidget: (appContext: AppContext) => RootWidget;
getRootWidget: (appContext: AppContext) => RootContainer;
}
interface RootWidget extends Component {
render: () => JQuery<HTMLElement>;
}
interface BeforeUploadListener extends Component {
export interface BeforeUploadListener extends Component {
beforeUnloadEvent(): boolean;
}
@@ -85,7 +83,6 @@ export type CommandMappings = {
focusTree: CommandData;
focusOnTitle: CommandData;
focusOnDetail: CommandData;
focusOnSearchDefinition: Required<CommandData>;
searchNotes: CommandData & {
searchString?: string;
ancestorNoteId?: string | null;
@@ -93,6 +90,11 @@ export type CommandMappings = {
closeTocCommand: CommandData;
closeHlt: CommandData;
showLaunchBarSubtree: CommandData;
showHiddenSubtree: CommandData;
showSQLConsoleHistory: CommandData;
logout: CommandData;
switchToMobileVersion: CommandData;
switchToDesktopVersion: CommandData;
showRevisions: CommandData & {
noteId?: string | null;
};
@@ -138,6 +140,7 @@ export type CommandMappings = {
showLeftPane: CommandData;
showAttachments: CommandData;
showSearchHistory: CommandData;
showShareSubtree: CommandData;
hoistNote: CommandData & { noteId: string };
leaveProtectedSession: CommandData;
enterProtectedSession: CommandData;
@@ -323,6 +326,7 @@ export type CommandMappings = {
printActiveNote: CommandData;
exportAsPdf: CommandData;
openNoteExternally: CommandData;
openNoteCustom: CommandData;
renderActiveNote: CommandData;
unhoist: CommandData;
reloadFrontendApp: CommandData;
@@ -526,7 +530,7 @@ export type FilteredCommandNames<T extends CommandData> = keyof Pick<CommandMapp
export class AppContext extends Component {
isMainWindow: boolean;
components: Component[];
beforeUnloadListeners: WeakRef<BeforeUploadListener>[];
beforeUnloadListeners: (WeakRef<BeforeUploadListener> | (() => boolean))[];
tabManager!: TabManager;
layout?: Layout;
noteTreeWidget?: NoteTreeWidget;
@@ -619,7 +623,7 @@ export class AppContext extends Component {
component.triggerCommand(commandName, { $el: $(this) });
});
this.child(rootWidget);
this.child(rootWidget as Component);
this.triggerEvent("initialRenderComplete", {});
}
@@ -649,13 +653,17 @@ export class AppContext extends Component {
return $(el).closest(".component").prop("component");
}
addBeforeUnloadListener(obj: BeforeUploadListener) {
addBeforeUnloadListener(obj: BeforeUploadListener | (() => boolean)) {
if (typeof WeakRef !== "function") {
// older browsers don't support WeakRef
return;
}
this.beforeUnloadListeners.push(new WeakRef<BeforeUploadListener>(obj));
if (typeof obj === "object") {
this.beforeUnloadListeners.push(new WeakRef<BeforeUploadListener>(obj));
} else {
this.beforeUnloadListeners.push(obj);
}
}
}
@@ -665,25 +673,29 @@ const appContext = new AppContext(window.glob.isMainWindow);
$(window).on("beforeunload", () => {
let allSaved = true;
appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter((wr) => !!wr.deref());
appContext.beforeUnloadListeners = appContext.beforeUnloadListeners.filter((wr) => typeof wr === "function" || !!wr.deref());
for (const weakRef of appContext.beforeUnloadListeners) {
const component = weakRef.deref();
for (const listener of appContext.beforeUnloadListeners) {
if (typeof listener === "object") {
const component = listener.deref();
if (!component) {
continue;
}
if (!component) {
continue;
}
if (!component.beforeUnloadEvent()) {
console.log(`Component ${component.componentId} is not finished saving its state.`);
toast.showMessage(t("app_context.please_wait_for_save"), 10000);
allSaved = false;
if (!component.beforeUnloadEvent()) {
console.log(`Component ${component.componentId} is not finished saving its state.`);
allSaved = false;
}
} else {
if (!listener()) {
allSaved = false;
}
}
}
if (!allSaved) {
toast.showMessage(t("app_context.please_wait_for_save"), 10000);
return "some string";
}
});

View File

@@ -1,6 +1,8 @@
import utils from "../services/utils.js";
import type { CommandMappings, CommandNames, EventData, EventNames } from "./app_context.js";
type EventHandler = ((data: any) => void);
/**
* Abstract class for all components in the Trilium's frontend.
*
@@ -19,6 +21,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
initialized: Promise<void> | null;
parent?: TypedComponent<any>;
_position!: number;
private listeners: Record<string, EventHandler[]> | null = {};
constructor() {
this.componentId = `${this.sanitizedClassName}-${utils.randomString(8)}`;
@@ -76,6 +79,14 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
const promises: Promise<unknown>[] = [];
// Handle React children.
if (this.listeners?.[name]) {
for (const listener of this.listeners[name]) {
listener(data);
}
}
// Handle legacy children.
for (const child of this.children) {
const ret = child.handleEvent(name, data) as Promise<void>;
@@ -120,6 +131,35 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
return promise;
}
registerHandler<T extends EventNames>(name: T, handler: EventHandler) {
if (!this.listeners) {
this.listeners = {};
}
if (!this.listeners[name]) {
this.listeners[name] = [];
}
if (this.listeners[name].includes(handler)) {
return;
}
this.listeners[name].push(handler);
}
removeHandler<T extends EventNames>(name: T, handler: EventHandler) {
if (!this.listeners?.[name]?.includes(handler)) {
return;
}
this.listeners[name] = this.listeners[name]
.filter(listener => listener !== handler);
if (!this.listeners[name].length) {
delete this.listeners[name];
}
}
}
export default class Component extends TypedComponent<Component> {}

View File

@@ -10,22 +10,7 @@ import bundleService from "../services/bundle.js";
import froca from "../services/froca.js";
import linkService from "../services/link.js";
import { t } from "../services/i18n.js";
import type FNote from "../entities/fnote.js";
// TODO: Move somewhere else nicer.
export type SqlExecuteResults = string[][][];
// TODO: Deduplicate with server.
interface SqlExecuteResponse {
success: boolean;
error?: string;
results: SqlExecuteResults;
}
// TODO: Deduplicate with server.
interface CreateChildrenResponse {
note: FNote;
}
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
export default class Entrypoints extends Component {
constructor() {
@@ -34,7 +19,7 @@ export default class Entrypoints extends Component {
openDevToolsCommand() {
if (utils.isElectron()) {
utils.dynamicRequire("@electron/remote").getCurrentWindow().toggleDevTools();
utils.dynamicRequire("@electron/remote").getCurrentWindow().webContents.toggleDevTools();
}
}
@@ -124,7 +109,7 @@ export default class Entrypoints extends Component {
if (utils.isElectron()) {
// standard JS version does not work completely correctly in electron
const webContents = utils.dynamicRequire("@electron/remote").getCurrentWebContents();
const activeIndex = parseInt(webContents.navigationHistory.getActiveIndex());
const activeIndex = webContents.navigationHistory.getActiveIndex();
webContents.goToIndex(activeIndex - 1);
} else {
@@ -136,7 +121,7 @@ export default class Entrypoints extends Component {
if (utils.isElectron()) {
// standard JS version does not work completely correctly in electron
const webContents = utils.dynamicRequire("@electron/remote").getCurrentWebContents();
const activeIndex = parseInt(webContents.navigationHistory.getActiveIndex());
const activeIndex = webContents.navigationHistory.getActiveIndex();
webContents.goToIndex(activeIndex + 1);
} else {

View File

@@ -43,8 +43,6 @@ export default class RootCommandExecutor extends Component {
const noteContext = await appContext.tabManager.openTabWithNoteWithHoisting(searchNote.noteId, {
activate: true
});
appContext.triggerCommand("focusOnSearchDefinition", { ntxId: noteContext.ntxId });
}
async searchInSubtreeCommand({ notePath }: CommandListenerData<"searchInSubtree">) {

View File

@@ -8,10 +8,9 @@ import electronContextMenu from "./menus/electron_context_menu.js";
import glob from "./services/glob.js";
import { t } from "./services/i18n.js";
import options from "./services/options.js";
import server from "./services/server.js";
import type ElectronRemote from "@electron/remote";
import type Electron from "electron";
import "./stylesheets/bootstrap.scss";
import "bootstrap/dist/css/bootstrap.min.css";
import "boxicons/css/boxicons.min.css";
import "autocomplete.js/index_jquery.js";

View File

@@ -64,7 +64,7 @@ export interface NoteMetaData {
/**
* Note is the main node and concept in Trilium.
*/
class FNote {
export default class FNote {
private froca: Froca;
noteId!: string;
@@ -1020,6 +1020,14 @@ class FNote {
return this.noteId.startsWith("_options");
}
isTriliumSqlite() {
return this.mime === "text/x-sqlite;schema=trilium";
}
isTriliumScript() {
return this.mime.startsWith("application/javascript");
}
/**
* Provides note's date metadata.
*/
@@ -1027,5 +1035,3 @@ class FNote {
return await server.get<NoteMetaData>(`notes/${this.noteId}/metadata`);
}
}
export default FNote;

View File

@@ -1,78 +1,47 @@
import FlexContainer from "../widgets/containers/flex_container.js";
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
import TabRowWidget from "../widgets/tab_row.js";
import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
import LeftPaneContainer from "../widgets/containers/left_pane_container.js";
import NoteTreeWidget from "../widgets/note_tree.js";
import NoteTitleWidget from "../widgets/note_title.js";
import OwnedAttributeListWidget from "../widgets/ribbon_widgets/owned_attribute_list.js";
import NoteActionsWidget from "../widgets/buttons/note_actions.js";
import NoteTitleWidget from "../widgets/note_title.jsx";
import NoteDetailWidget from "../widgets/note_detail.js";
import RibbonContainer from "../widgets/containers/ribbon_container.js";
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
import InheritedAttributesWidget from "../widgets/ribbon_widgets/inherited_attribute_list.js";
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
import NoteListWidget from "../widgets/note_list.js";
import SearchDefinitionWidget from "../widgets/ribbon_widgets/search_definition.js";
import SqlResultWidget from "../widgets/sql_result.js";
import SqlTableSchemasWidget from "../widgets/sql_table_schemas.js";
import FilePropertiesWidget from "../widgets/ribbon_widgets/file_properties.js";
import ImagePropertiesWidget from "../widgets/ribbon_widgets/image_properties.js";
import NotePropertiesWidget from "../widgets/ribbon_widgets/note_properties.js";
import NoteIconWidget from "../widgets/note_icon.js";
import SearchResultWidget from "../widgets/search_result.js";
import NoteIconWidget from "../widgets/note_icon.jsx";
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
import RootContainer from "../widgets/containers/root_container.js";
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
import SpacerWidget from "../widgets/spacer.js";
import QuickSearchWidget from "../widgets/quick_search.js";
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
import LeftPaneToggleWidget from "../widgets/buttons/left_pane_toggle.js";
import CreatePaneButton from "../widgets/buttons/create_pane_button.js";
import ClosePaneButton from "../widgets/buttons/close_pane_button.js";
import BasicPropertiesWidget from "../widgets/ribbon_widgets/basic_properties.js";
import NoteInfoWidget from "../widgets/ribbon_widgets/note_info_widget.js";
import BookPropertiesWidget from "../widgets/ribbon_widgets/book_properties.js";
import NoteMapRibbonWidget from "../widgets/ribbon_widgets/note_map.js";
import NotePathsWidget from "../widgets/ribbon_widgets/note_paths.js";
import SimilarNotesWidget from "../widgets/ribbon_widgets/similar_notes.js";
import RightPaneContainer from "../widgets/containers/right_pane_container.js";
import EditButton from "../widgets/floating_buttons/edit_button.js";
import EditedNotesWidget from "../widgets/ribbon_widgets/edited_notes.js";
import ShowTocWidgetButton from "../widgets/buttons/show_toc_widget_button.js";
import ShowHighlightsListWidgetButton from "../widgets/buttons/show_highlights_list_widget_button.js";
import NoteWrapperWidget from "../widgets/note_wrapper.js";
import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
import HighlightsListWidget from "../widgets/highlights_list.js";
import PasswordNoteSetDialog from "../widgets/dialogs/password_not_set.js";
import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import SvgExportButton from "../widgets/floating_buttons/svg_export_button.js";
import LauncherContainer from "../widgets/containers/launcher_container.js";
import RevisionsButton from "../widgets/buttons/revisions_button.js";
import CodeButtonsWidget from "../widgets/floating_buttons/code_buttons.js";
import ApiLogWidget from "../widgets/api_log.js";
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js";
import MovePaneButton from "../widgets/buttons/move_pane_button.js";
import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js";
import CopyImageReferenceButton from "../widgets/floating_buttons/copy_image_reference_button.js";
import ScrollPaddingWidget from "../widgets/scroll_padding.js";
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
import ScrollPadding from "../widgets/scroll_padding.js";
import options from "../services/options.js";
import utils from "../services/utils.js";
import GeoMapButtons from "../widgets/floating_buttons/geo_map_button.js";
import ContextualHelpButton from "../widgets/floating_buttons/help_button.js";
import CloseZenButton from "../widgets/close_zen_button.js";
import type { AppContext } from "../components/app_context.js";
import type { WidgetsByParent } from "../services/bundle.js";
import SwitchSplitOrientationButton from "../widgets/floating_buttons/switch_layout_button.js";
import ToggleReadOnlyButton from "../widgets/floating_buttons/toggle_read_only_button.js";
import PngExportButton from "../widgets/floating_buttons/png_export_button.js";
import RefreshButton from "../widgets/floating_buttons/refresh_button.js";
import { applyModals } from "./layout_commons.js";
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
import FloatingButtons from "../widgets/FloatingButtons.jsx";
import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
import SearchResult from "../widgets/search_result.jsx";
import GlobalMenu from "../widgets/buttons/global_menu.jsx";
import SqlResults from "../widgets/sql_result.js";
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
import TitleBarButtons from "../widgets/title_bar_buttons.jsx";
import LeftPaneToggle from "../widgets/buttons/left_pane_toggle.js";
import ApiLog from "../widgets/api_log.jsx";
import CloseZenModeButton from "../widgets/close_zen_button.jsx";
import SharedInfo from "../widgets/shared_info.jsx";
export default class DesktopLayout {
@@ -107,9 +76,9 @@ export default class DesktopLayout {
new FlexContainer("row")
.class("tab-row-container")
.child(new FlexContainer("row").id("tab-row-left-spacer"))
.optChild(launcherPaneIsHorizontal, new LeftPaneToggleWidget(true))
.optChild(launcherPaneIsHorizontal, <LeftPaneToggle isHorizontalLayout={true} />)
.child(new TabRowWidget().class("full-width"))
.optChild(customTitleBarButtons, new TitleBarButtonsWidget())
.optChild(customTitleBarButtons, <TitleBarButtons />)
.css("height", "40px")
.css("background-color", "var(--launcher-pane-background-color)")
.setParent(appContext)
@@ -130,7 +99,7 @@ export default class DesktopLayout {
new FlexContainer("column")
.id("rest-pane")
.css("flex-grow", "1")
.optChild(!fullWidthTabBar, new FlexContainer("row").child(new TabRowWidget()).optChild(customTitleBarButtons, new TitleBarButtonsWidget()).css("height", "40px"))
.optChild(!fullWidthTabBar, new FlexContainer("row").child(new TabRowWidget()).optChild(customTitleBarButtons, <TitleBarButtons />).css("height", "40px"))
.child(
new FlexContainer("row")
.filling()
@@ -151,69 +120,30 @@ export default class DesktopLayout {
.css("min-height", "50px")
.css("align-items", "center")
.cssBlock(".title-row > * { margin: 5px; }")
.child(new NoteIconWidget())
.child(new NoteTitleWidget())
.child(<NoteIconWidget />)
.child(<NoteTitleWidget />)
.child(new SpacerWidget(0, 1))
.child(new MovePaneButton(true))
.child(new MovePaneButton(false))
.child(new ClosePaneButton())
.child(new CreatePaneButton())
)
.child(
new RibbonContainer()
// the order of the widgets matter. Some of these want to "activate" themselves
// when visible. When this happens to multiple of them, the first one "wins".
// promoted attributes should always win.
.ribbon(new ClassicEditorToolbar())
.ribbon(new ScriptExecutorWidget())
.ribbon(new SearchDefinitionWidget())
.ribbon(new EditedNotesWidget())
.ribbon(new BookPropertiesWidget())
.ribbon(new NotePropertiesWidget())
.ribbon(new FilePropertiesWidget())
.ribbon(new ImagePropertiesWidget())
.ribbon(new BasicPropertiesWidget())
.ribbon(new OwnedAttributeListWidget())
.ribbon(new InheritedAttributesWidget())
.ribbon(new NotePathsWidget())
.ribbon(new NoteMapRibbonWidget())
.ribbon(new SimilarNotesWidget())
.ribbon(new NoteInfoWidget())
.button(new RevisionsButton())
.button(new NoteActionsWidget())
)
.child(new SharedInfoWidget())
.child(<Ribbon />)
.child(<SharedInfo />)
.child(new WatchedFileUpdateStatusWidget())
.child(
new FloatingButtons()
.child(new RefreshButton())
.child(new SwitchSplitOrientationButton())
.child(new ToggleReadOnlyButton())
.child(new EditButton())
.child(new ShowTocWidgetButton())
.child(new ShowHighlightsListWidgetButton())
.child(new CodeButtonsWidget())
.child(new RelationMapButtons())
.child(new GeoMapButtons())
.child(new CopyImageReferenceButton())
.child(new SvgExportButton())
.child(new PngExportButton())
.child(new BacklinksWidget())
.child(new ContextualHelpButton())
.child(new HideFloatingButtonsButton())
)
.child(<FloatingButtons items={DESKTOP_FLOATING_BUTTONS} />)
.child(
new ScrollingContainer()
.filling()
.child(new PromotedAttributesWidget())
.child(new SqlTableSchemasWidget())
.child(<SqlTableSchemas />)
.child(new NoteDetailWidget())
.child(new NoteListWidget(false))
.child(new SearchResultWidget())
.child(new SqlResultWidget())
.child(new ScrollPaddingWidget())
.child(<SearchResult />)
.child(<SqlResults />)
.child(<ScrollPadding />)
)
.child(new ApiLogWidget())
.child(<ApiLog />)
.child(new FindWidget())
.child(
...this.customWidgets.get("node-detail-pane"), // typo, let's keep it for a while as BC
@@ -232,11 +162,11 @@ export default class DesktopLayout {
)
)
)
.child(new CloseZenButton())
.child(<CloseZenModeButton />)
// Desktop-specific dialogs.
.child(new PasswordNoteSetDialog())
.child(new UploadAttachmentsDialog());
.child(<PasswordNoteSetDialog />)
.child(<UploadAttachmentsDialog />);
applyModals(rootContainer);
return rootContainer;
@@ -246,14 +176,18 @@ export default class DesktopLayout {
let launcherPane;
if (isHorizontal) {
launcherPane = new FlexContainer("row").css("height", "53px").class("horizontal").child(new LauncherContainer(true)).child(new GlobalMenuWidget(true));
launcherPane = new FlexContainer("row")
.css("height", "53px")
.class("horizontal")
.child(new LauncherContainer(true))
.child(<GlobalMenu isHorizontalLayout={true} />);
} else {
launcherPane = new FlexContainer("column")
.css("width", "53px")
.class("vertical")
.child(new GlobalMenuWidget(false))
.child(<GlobalMenu isHorizontalLayout={false} />)
.child(new LauncherContainer(false))
.child(new LeftPaneToggleWidget(false));
.child(<LeftPaneToggle isHorizontalLayout={false} />);
}
launcherPane.id("launcher-pane");

View File

@@ -24,48 +24,48 @@ import InfoDialog from "../widgets/dialogs/info.js";
import IncorrectCpuArchDialog from "../widgets/dialogs/incorrect_cpu_arch.js";
import PopupEditorDialog from "../widgets/dialogs/popup_editor.js";
import FlexContainer from "../widgets/containers/flex_container.js";
import NoteIconWidget from "../widgets/note_icon.js";
import NoteTitleWidget from "../widgets/note_title.js";
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
import NoteIconWidget from "../widgets/note_icon";
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
import NoteDetailWidget from "../widgets/note_detail.js";
import NoteListWidget from "../widgets/note_list.js";
import { CallToActionDialog } from "../widgets/dialogs/call_to_action.jsx";
import CallToActionDialog from "../widgets/dialogs/call_to_action.jsx";
import NoteTitleWidget from "../widgets/note_title.jsx";
import { PopupEditorFormattingToolbar } from "../widgets/ribbon/FormattingToolbar.js";
export function applyModals(rootContainer: RootContainer) {
rootContainer
.child(new BulkActionsDialog())
.child(new AboutDialog())
.child(new HelpDialog())
.child(new RecentChangesDialog())
.child(new BranchPrefixDialog())
.child(new SortChildNotesDialog())
.child(new IncludeNoteDialog())
.child(new NoteTypeChooserDialog())
.child(new JumpToNoteDialog())
.child(new AddLinkDialog())
.child(new CloneToDialog())
.child(new MoveToDialog())
.child(new ImportDialog())
.child(new ExportDialog())
.child(new MarkdownImportDialog())
.child(new ProtectedSessionPasswordDialog())
.child(new RevisionsDialog())
.child(new DeleteNotesDialog())
.child(new InfoDialog())
.child(new ConfirmDialog())
.child(new PromptDialog())
.child(new IncorrectCpuArchDialog())
.child(<BulkActionsDialog />)
.child(<AboutDialog />)
.child(<HelpDialog />)
.child(<RecentChangesDialog />)
.child(<BranchPrefixDialog />)
.child(<SortChildNotesDialog />)
.child(<IncludeNoteDialog />)
.child(<NoteTypeChooserDialog />)
.child(<JumpToNoteDialog />)
.child(<AddLinkDialog />)
.child(<CloneToDialog />)
.child(<MoveToDialog />)
.child(<ImportDialog />)
.child(<ExportDialog />)
.child(<MarkdownImportDialog />)
.child(<ProtectedSessionPasswordDialog />)
.child(<RevisionsDialog />)
.child(<DeleteNotesDialog />)
.child(<InfoDialog />)
.child(<ConfirmDialog />)
.child(<PromptDialog />)
.child(<IncorrectCpuArchDialog />)
.child(new PopupEditorDialog()
.child(new FlexContainer("row")
.class("title-row")
.css("align-items", "center")
.cssBlock(".title-row > * { margin: 5px; }")
.child(new NoteIconWidget())
.child(new NoteTitleWidget()))
.child(new ClassicEditorToolbar())
.child(<NoteIconWidget />)
.child(<NoteTitleWidget />))
.child(<PopupEditorFormattingToolbar />)
.child(new PromotedAttributesWidget())
.child(new NoteDetailWidget())
.child(new NoteListWidget(true)))
.child(new CallToActionDialog());
.child(<CallToActionDialog />);
}

View File

@@ -3,30 +3,27 @@ import NoteTitleWidget from "../widgets/note_title.js";
import NoteDetailWidget from "../widgets/note_detail.js";
import QuickSearchWidget from "../widgets/quick_search.js";
import NoteTreeWidget from "../widgets/note_tree.js";
import ToggleSidebarButtonWidget from "../widgets/mobile_widgets/toggle_sidebar_button.js";
import MobileDetailMenuWidget from "../widgets/mobile_widgets/mobile_detail_menu.js";
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
import FilePropertiesWidget from "../widgets/ribbon_widgets/file_properties.js";
import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import EditButton from "../widgets/floating_buttons/edit_button.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import SvgExportButton from "../widgets/floating_buttons/svg_export_button.js";
import BacklinksWidget from "../widgets/floating_buttons/zpetne_odkazy.js";
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
import NoteListWidget from "../widgets/note_list.js";
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
import LauncherContainer from "../widgets/containers/launcher_container.js";
import RootContainer from "../widgets/containers/root_container.js";
import SharedInfoWidget from "../widgets/shared_info.js";
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
import type AppContext from "../components/app_context.js";
import TabRowWidget from "../widgets/tab_row.js";
import RefreshButton from "../widgets/floating_buttons/refresh_button.js";
import MobileEditorToolbar from "../widgets/ribbon_widgets/mobile_editor_toolbar.js";
import MobileEditorToolbar from "../widgets/type_widgets/ckeditor/mobile_editor_toolbar.js";
import { applyModals } from "./layout_commons.js";
import CloseZenButton from "../widgets/close_zen_button.js";
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
import { useNoteContext } from "../widgets/react/hooks.jsx";
import FloatingButtons from "../widgets/FloatingButtons.jsx";
import { MOBILE_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button.jsx";
import CloseZenModeButton from "../widgets/close_zen_button.js";
import NoteWrapperWidget from "../widgets/note_wrapper.js";
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
const MOBILE_CSS = `
<style>
@@ -135,38 +132,33 @@ export default class MobileLayout {
.child(new FlexContainer("column").filling().id("mobile-sidebar-wrapper").child(new QuickSearchWidget()).child(new NoteTreeWidget().cssBlock(FANCYTREE_CSS)))
)
.child(
new ScreenContainer("detail", "column")
new ScreenContainer("detail", "row")
.id("detail-container")
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-9")
.child(
new FlexContainer("row")
.contentSized()
.css("font-size", "larger")
.css("align-items", "center")
.child(new ToggleSidebarButtonWidget().contentSized())
.child(new NoteTitleWidget().contentSized().css("position", "relative").css("padding-left", "0.5em"))
.child(new MobileDetailMenuWidget(true).contentSized())
new NoteWrapperWidget()
.child(
new FlexContainer("row")
.contentSized()
.css("font-size", "larger")
.css("align-items", "center")
.child(<ToggleSidebarButton />)
.child(<NoteTitleWidget />)
.child(<MobileDetailMenu />)
)
.child(<SharedInfoWidget />)
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
.child(new PromotedAttributesWidget())
.child(
new ScrollingContainer()
.filling()
.contentSized()
.child(new NoteDetailWidget())
.child(new NoteListWidget(false))
.child(<FilePropertiesWrapper />)
)
.child(<MobileEditorToolbar />)
)
.child(new SharedInfoWidget())
.child(
new FloatingButtons()
.child(new RefreshButton())
.child(new EditButton())
.child(new RelationMapButtons())
.child(new SvgExportButton())
.child(new BacklinksWidget())
.child(new HideFloatingButtonsButton())
)
.child(new PromotedAttributesWidget())
.child(
new ScrollingContainer()
.filling()
.contentSized()
.child(new NoteDetailWidget())
.child(new NoteListWidget(false))
.child(new FilePropertiesWidget().css("font-size", "smaller"))
)
.child(new MobileEditorToolbar())
)
)
.child(
@@ -174,10 +166,25 @@ export default class MobileLayout {
.contentSized()
.id("mobile-bottom-bar")
.child(new TabRowWidget().css("height", "40px"))
.child(new FlexContainer("row").class("horizontal").css("height", "53px").child(new LauncherContainer(true)).child(new GlobalMenuWidget(true)).id("launcher-pane"))
.child(new FlexContainer("row")
.class("horizontal")
.css("height", "53px")
.child(new LauncherContainer(true))
.child(<GlobalMenuWidget isHorizontalLayout />)
.id("launcher-pane"))
)
.child(new CloseZenButton());
.child(<CloseZenModeButton />);
applyModals(rootContainer);
return rootContainer;
}
}
function FilePropertiesWrapper() {
const { note } = useNoteContext();
return (
<div>
{note?.type === "file" && <FilePropertiesTab note={note} />}
</div>
);
}

View File

@@ -1,4 +1,4 @@
import "./stylesheets/bootstrap.scss";
import "bootstrap/dist/css/bootstrap.min.css";
// @ts-ignore - module = undefined
// Required for correct loading of scripts in Electron

View File

@@ -1,7 +1,7 @@
import appContext from "./components/app_context.js";
import noteAutocompleteService from "./services/note_autocomplete.js";
import glob from "./services/glob.js";
import "./stylesheets/bootstrap.scss";
import "bootstrap/dist/css/bootstrap.min.css";
import "boxicons/css/boxicons.min.css";
import "autocomplete.js/index_jquery.js";

View File

@@ -2,6 +2,7 @@ import server from "./server.js";
import froca from "./froca.js";
import type FNote from "../entities/fnote.js";
import type { AttributeRow } from "./load_results.js";
import { AttributeType } from "@triliumnext/commons";
async function addLabel(noteId: string, name: string, value: string = "", isInheritable = false) {
await server.put(`notes/${noteId}/attribute`, {
@@ -25,6 +26,14 @@ async function removeAttributeById(noteId: string, attributeId: string) {
await server.remove(`notes/${noteId}/attributes/${attributeId}`);
}
export async function removeOwnedAttributesByNameOrType(note: FNote, type: AttributeType, name: string) {
for (const attr of note.getOwnedAttributes()) {
if (attr.type === type && attr.name === name) {
await server.remove(`notes/${note.noteId}/attributes/${attr.attributeId}`);
}
}
}
/**
* Removes a label identified by its name from the given note, if it exists. Note that the label must be owned, i.e.
* it will not remove inherited attributes.
@@ -52,7 +61,7 @@ function removeOwnedLabelByName(note: FNote, labelName: string) {
* @param value the value of the attribute to set.
*/
export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) {
if (value) {
if (value !== null && value !== undefined) {
// Create or update the attribute.
await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value });
} else {

View File

@@ -18,7 +18,7 @@ import type FNote from "../entities/fnote.js";
import toast from "./toast.js";
import { BulkAction } from "@triliumnext/commons";
const ACTION_GROUPS = [
export const ACTION_GROUPS = [
{
title: t("bulk_actions.labels"),
actions: [AddLabelBulkAction, UpdateLabelValueBulkAction, RenameLabelBulkAction, DeleteLabelBulkAction]

View File

@@ -48,6 +48,6 @@ function getUrl(docNameValue: string, language: string) {
// Cannot have spaces in the URL due to how JQuery.load works.
docNameValue = docNameValue.replaceAll(" ", "%20");
const basePath = window.glob.isDev ? new URL(window.glob.assetPath).pathname : window.glob.assetPath;
const basePath = window.glob.isDev ? window.glob.assetPath + "/.." : window.glob.assetPath;
return `${basePath}/doc_notes/${language}/${docNameValue}.html`;
}

View File

@@ -35,8 +35,10 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
loadResults.addOption(attributeEntity.name);
} else if (ec.entityName === "attachments") {
processAttachment(loadResults, ec);
} else if (ec.entityName === "blobs" || ec.entityName === "etapi_tokens") {
} else if (ec.entityName === "blobs") {
// NOOP - these entities are handled at the backend level and don't require frontend processing
} else if (ec.entityName === "etapi_tokens") {
loadResults.hasEtapiTokenChanges = true;
} else {
throw new Error(`Unknown entityName '${ec.entityName}'`);
}
@@ -77,9 +79,7 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
noteAttributeCache.invalidate();
}
// TODO: Remove after porting the file
// @ts-ignore
const appContext = (await import("../components/app_context.js")).default as any;
const appContext = (await import("../components/app_context.js")).default;
await appContext.triggerEvent("entitiesReloaded", { loadResults });
}
}

View File

@@ -3,6 +3,7 @@ import i18next from "i18next";
import i18nextHttpBackend from "i18next-http-backend";
import server from "./server.js";
import type { Locale } from "@triliumnext/commons";
import { initReactI18next } from "react-i18next";
let locales: Locale[] | null;
@@ -16,6 +17,7 @@ export async function initLocale() {
locales = await server.get<Locale[]>("options/locales");
i18next.use(initReactI18next);
await i18next.use(i18nextHttpBackend).init({
lng: locale,
fallbackLng: "en",

View File

@@ -1,7 +1,7 @@
import { t } from "./i18n.js";
import toastService, { showError } from "./toast.js";
function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
export function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
try {
$imageWrapper.attr("contenteditable", "true");
selectImage($imageWrapper.get(0));

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { byBookType, byNoteType } from "./help_button.js";
import { byBookType, byNoteType } from "./in_app_help.js";
import fs from "fs";
import type { HiddenSubtreeItem } from "@triliumnext/commons";
import path from "path";
@@ -25,7 +25,7 @@ describe("Help button", () => {
...Object.values(byBookType)
].filter((noteId) => noteId) as string[];
const metaPath = path.resolve(path.join(__dirname, "../../../../server/src/assets/doc_notes/en/User Guide/!!!meta.json"));
const metaPath = path.resolve(path.join(__dirname, "../../../server/src/assets/doc_notes/en/User Guide/!!!meta.json"));
const meta: HiddenSubtreeItem[] = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
const allNoteIds = new Set(getNoteIds(meta));

View File

@@ -0,0 +1,43 @@
import { NoteType } from "@triliumnext/commons";
import { ViewTypeOptions } from "./note_list_renderer";
import FNote from "../entities/fnote";
export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
canvas: null,
code: null,
contentWidget: null,
doc: null,
file: null,
image: null,
launcher: null,
mermaid: null,
mindMap: null,
noteMap: null,
relationMap: null,
render: null,
search: null,
text: null,
webView: null,
aiChat: null
};
export const byBookType: Record<ViewTypeOptions, string | null> = {
list: "mULW0Q3VojwY",
grid: "8QqnMzx393bx",
calendar: "xWbu3jpNWapp",
table: "2FvYrpmOXm29",
geoMap: "81SGnPGMk7Xc",
board: "CtBQqbwXDx1w"
};
export function getHelpUrlForNote(note: FNote | null | undefined) {
if (note && note.type !== "book" && byNoteType[note.type]) {
return byNoteType[note.type];
} else if (note?.hasLabel("calendarRoot")) {
return "l0tKav7yLHGF";
} else if (note?.hasLabel("textSnippet")) {
return "pwc194wlRzcH";
} else if (note && note.type === "book") {
return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
}
}

View File

@@ -62,6 +62,10 @@ async function getAction(actionName: string, silent = false) {
return action;
}
export function getActionSync(actionName: string) {
return keyboardActionRepo[actionName];
}
function updateDisplayedShortcuts($container: JQuery<HTMLElement>) {
//@ts-ignore
//TODO: each() does not support async callbacks.

View File

@@ -1,4 +1,4 @@
import type { AttachmentRow } from "@triliumnext/commons";
import type { AttachmentRow, EtapiTokenRow } from "@triliumnext/commons";
import type { AttributeType } from "../entities/fattribute.js";
import type { EntityChange } from "../server_types.js";
@@ -53,6 +53,7 @@ type EntityRowMappings = {
options: OptionRow;
revisions: RevisionRow;
note_reordering: NoteReorderingRow;
etapi_tokens: EtapiTokenRow;
};
export type EntityRowNames = keyof EntityRowMappings;
@@ -68,6 +69,7 @@ export default class LoadResults {
private contentNoteIdToComponentId: ContentNoteIdToComponentIdRow[];
private optionNames: string[];
private attachmentRows: AttachmentRow[];
public hasEtapiTokenChanges: boolean = false;
constructor(entityChanges: EntityChange[]) {
const entities: Record<string, Record<string, any>> = {};
@@ -215,7 +217,8 @@ export default class LoadResults {
this.revisionRows.length === 0 &&
this.contentNoteIdToComponentId.length === 0 &&
this.optionNames.length === 0 &&
this.attachmentRows.length === 0
this.attachmentRows.length === 0 &&
!this.hasEtapiTokenChanges
);
}

View File

@@ -36,6 +36,8 @@ export interface Suggestion {
commandId?: string;
commandDescription?: string;
commandShortcut?: string;
attributeSnippet?: string;
highlightedAttributeSnippet?: string;
}
export interface Options {
@@ -323,7 +325,33 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
html += '</div>';
return html;
}
return `<span class="${suggestion.icon ?? "bx bx-note"}"></span> ${suggestion.highlightedNotePathTitle}`;
// Add special class for search-notes action
const actionClass = suggestion.action === "search-notes" ? "search-notes-action" : "";
// Choose appropriate icon based on action
let iconClass = suggestion.icon ?? "bx bx-note";
if (suggestion.action === "search-notes") {
iconClass = "bx bx-search";
} else if (suggestion.action === "create-note") {
iconClass = "bx bx-plus";
} else if (suggestion.action === "external-link") {
iconClass = "bx bx-link-external";
}
// Simplified HTML structure without nested divs
let html = `<div class="note-suggestion ${actionClass}">`;
html += `<span class="icon ${iconClass}"></span>`;
html += `<span class="text">`;
html += `<span class="search-result-title">${suggestion.highlightedNotePathTitle}</span>`;
// Add attribute snippet inline if available
if (suggestion.highlightedAttributeSnippet) {
html += `<span class="search-result-attributes">${suggestion.highlightedAttributeSnippet}</span>`;
}
html += `</span>`;
html += `</div>`;
return html;
}
},
// we can't cache identical searches because notes can be created / renamed, new recent notes can be added

View File

@@ -35,7 +35,7 @@ function download(url: string) {
}
}
function downloadFileNote(noteId: string) {
export function downloadFileNote(noteId: string) {
const url = `${getFileUrl("notes", noteId)}?${Date.now()}`; // don't use cache
download(url);
@@ -163,7 +163,7 @@ async function openExternally(type: string, entityId: string, mime: string) {
}
}
const openNoteExternally = async (noteId: string, mime: string) => await openExternally("notes", noteId, mime);
export const openNoteExternally = async (noteId: string, mime: string) => await openExternally("notes", noteId, mime);
const openAttachmentExternally = async (attachmentId: string, mime: string) => await openExternally("attachments", attachmentId, mime);
function getHost() {

View File

@@ -1,7 +1,8 @@
import { OptionNames } from "@triliumnext/commons";
import server from "./server.js";
import { isShare } from "./utils.js";
type OptionValue = number | string;
export type OptionValue = number | string;
class Options {
initializedPromise: Promise<void>;
@@ -76,6 +77,14 @@ class Options {
await server.put(`options`, payload);
}
/**
* Saves multiple options at once, by supplying a record where the keys are the option names and the values represent the stringified value to set.
* @param newValues the record of keys and values.
*/
async saveMany<T extends OptionNames>(newValues: Record<T, OptionValue>) {
await server.put<void>("options", newValues);
}
async toggle(key: string) {
await this.save(key, (!this.is(key)).toString());
}

View File

@@ -10,6 +10,10 @@ let leftInstance: ReturnType<typeof Split> | null;
let rightPaneWidth: number;
let rightInstance: ReturnType<typeof Split> | null;
const noteSplitMap = new Map<string[], ReturnType<typeof Split> | undefined>(); // key: a group of ntxIds, value: the corresponding Split instance
const noteSplitRafMap = new Map<string[], number>();
let splitNoteContainer: HTMLElement | undefined;
function setupLeftPaneResizer(leftPaneVisible: boolean) {
if (leftInstance) {
leftInstance.destroy();
@@ -83,7 +87,86 @@ function setupRightPaneResizer() {
}
}
function findKeyByNtxId(ntxId: string): string[] | undefined {
// Find the corresponding key in noteSplitMap based on ntxId
for (const key of noteSplitMap.keys()) {
if (key.includes(ntxId)) return key;
}
return undefined;
}
function setupNoteSplitResizer(ntxIds: string[]) {
let targetNtxIds: string[] | undefined;
for (const ntxId of ntxIds) {
targetNtxIds = findKeyByNtxId(ntxId);
if (targetNtxIds) break;
}
if (targetNtxIds) {
noteSplitMap.get(targetNtxIds)?.destroy();
for (const id of ntxIds) {
if (!targetNtxIds.includes(id)) {
targetNtxIds.push(id)
};
}
} else {
targetNtxIds = [...ntxIds];
}
noteSplitMap.set(targetNtxIds, undefined);
createSplitInstance(targetNtxIds);
}
function delNoteSplitResizer(ntxIds: string[]) {
let targetNtxIds = findKeyByNtxId(ntxIds[0]);
if (!targetNtxIds) {
return;
}
noteSplitMap.get(targetNtxIds)?.destroy();
noteSplitMap.delete(targetNtxIds);
targetNtxIds = targetNtxIds.filter(id => !ntxIds.includes(id));
if (targetNtxIds.length >= 2) {
noteSplitMap.set(targetNtxIds, undefined);
createSplitInstance(targetNtxIds);
}
}
function moveNoteSplitResizer(ntxId: string) {
const targetNtxIds = findKeyByNtxId(ntxId);
if (!targetNtxIds) {
return;
}
noteSplitMap.get(targetNtxIds)?.destroy();
noteSplitMap.set(targetNtxIds, undefined);
createSplitInstance(targetNtxIds);
}
function createSplitInstance(targetNtxIds: string[]) {
const prevRafId = noteSplitRafMap.get(targetNtxIds);
if (prevRafId) {
cancelAnimationFrame(prevRafId);
}
const rafId = requestAnimationFrame(() => {
splitNoteContainer = splitNoteContainer ?? $("#center-pane").find(".split-note-container-widget")[0];
const splitPanels = [...splitNoteContainer.querySelectorAll<HTMLElement>(':scope > .note-split')]
.filter(el => targetNtxIds.includes(el.getAttribute('data-ntx-id') ?? ""));
const splitInstance = Split(splitPanels, {
gutterSize: DEFAULT_GUTTER_SIZE,
minSize: 150,
});
noteSplitMap.set(targetNtxIds, splitInstance);
noteSplitRafMap.delete(targetNtxIds);
});
noteSplitRafMap.set(targetNtxIds, rafId);
}
export default {
setupLeftPaneResizer,
setupRightPaneResizer
setupRightPaneResizer,
setupNoteSplitResizer,
delNoteSplitResizer,
moveNoteSplitResizer
};

View File

@@ -218,7 +218,7 @@ function ajax(url: string, method: string, data: unknown, headers: Headers, sile
if (utils.isElectron()) {
const ipc = utils.dynamicRequire("electron").ipcRenderer;
ipc.on("server-response", async (event: string, arg: Arg) => {
ipc.on("server-response", async (_, arg: Arg) => {
if (arg.statusCode >= 200 && arg.statusCode < 300) {
handleSuccessfulResponse(arg);
} else {

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import shortcuts, { keyMatches, matchesShortcut } from "./shortcuts.js";
import shortcuts, { keyMatches, matchesShortcut, isIMEComposing } from "./shortcuts.js";
// Mock utils module
vi.mock("./utils.js", () => ({
@@ -320,4 +320,36 @@ describe("shortcuts", () => {
expect(event.preventDefault).not.toHaveBeenCalled();
});
});
describe('isIMEComposing', () => {
it('should return true when event.isComposing is true', () => {
const event = { isComposing: true, keyCode: 65 } as KeyboardEvent;
expect(isIMEComposing(event)).toBe(true);
});
it('should return true when keyCode is 229', () => {
const event = { isComposing: false, keyCode: 229 } as KeyboardEvent;
expect(isIMEComposing(event)).toBe(true);
});
it('should return true when both isComposing is true and keyCode is 229', () => {
const event = { isComposing: true, keyCode: 229 } as KeyboardEvent;
expect(isIMEComposing(event)).toBe(true);
});
it('should return false for normal keys', () => {
const event = { isComposing: false, keyCode: 65 } as KeyboardEvent;
expect(isIMEComposing(event)).toBe(false);
});
it('should return false when isComposing is undefined and keyCode is not 229', () => {
const event = { keyCode: 13 } as KeyboardEvent;
expect(isIMEComposing(event)).toBe(false);
});
it('should handle null/undefined events gracefully', () => {
expect(isIMEComposing(null as any)).toBe(false);
expect(isIMEComposing(undefined as any)).toBe(false);
});
});
});

View File

@@ -14,6 +14,50 @@ interface ShortcutBinding {
// Store all active shortcut bindings for management
const activeBindings: Map<string, ShortcutBinding[]> = new Map();
// Handle special key mappings and aliases
const keyMap: { [key: string]: string[] } = {
'return': ['Enter'],
'enter': ['Enter'], // alias for return
'del': ['Delete'],
'delete': ['Delete'], // alias for del
'esc': ['Escape'],
'escape': ['Escape'], // alias for esc
'space': [' ', 'Space'],
'tab': ['Tab'],
'backspace': ['Backspace'],
'home': ['Home'],
'end': ['End'],
'pageup': ['PageUp'],
'pagedown': ['PageDown'],
'up': ['ArrowUp'],
'down': ['ArrowDown'],
'left': ['ArrowLeft'],
'right': ['ArrowRight']
};
// Function keys
for (let i = 1; i <= 19; i++) {
keyMap[`f${i}`] = [`F${i}`];
}
/**
* Check if IME (Input Method Editor) is composing
* This is used to prevent keyboard shortcuts from firing during IME composition
* @param e - The keyboard event to check
* @returns true if IME is currently composing, false otherwise
*/
export function isIMEComposing(e: KeyboardEvent): boolean {
// Handle null/undefined events gracefully
if (!e) {
return false;
}
// Standard check for composition state
// e.isComposing is true when IME is actively composing
// e.keyCode === 229 is a fallback for older browsers where 229 indicates IME processing
return e.isComposing || e.keyCode === 229;
}
function removeGlobalShortcut(namespace: string) {
bindGlobalShortcut("", null, namespace);
}
@@ -42,6 +86,13 @@ function bindElShortcut($el: JQuery<ElementType | Element>, keyboardShortcut: st
}
const e = evt as KeyboardEvent;
// Skip processing if IME is composing to prevent shortcuts from
// interfering with text input in CJK languages
if (isIMEComposing(e)) {
return;
}
if (matchesShortcut(e, keyboardShortcut)) {
e.preventDefault();
e.stopPropagation();
@@ -124,32 +175,6 @@ export function keyMatches(e: KeyboardEvent, key: string): boolean {
return false;
}
// Handle special key mappings and aliases
const keyMap: { [key: string]: string[] } = {
'return': ['Enter'],
'enter': ['Enter'], // alias for return
'del': ['Delete'],
'delete': ['Delete'], // alias for del
'esc': ['Escape'],
'escape': ['Escape'], // alias for esc
'space': [' ', 'Space'],
'tab': ['Tab'],
'backspace': ['Backspace'],
'home': ['Home'],
'end': ['End'],
'pageup': ['PageUp'],
'pagedown': ['PageDown'],
'up': ['ArrowUp'],
'down': ['ArrowDown'],
'left': ['ArrowLeft'],
'right': ['ArrowRight']
};
// Function keys
for (let i = 1; i <= 19; i++) {
keyMap[`f${i}`] = [`F${i}`];
}
const mappedKeys = keyMap[key.toLowerCase()];
if (mappedKeys) {
return mappedKeys.includes(e.key) || mappedKeys.includes(e.code);
@@ -163,7 +188,7 @@ export function keyMatches(e: KeyboardEvent, key: string): boolean {
// For letter keys, use the physical key code for consistency
if (key.length === 1 && key >= 'a' && key <= 'z') {
return e.code === `Key${key.toUpperCase()}`;
return e.key.toLowerCase() === key.toLowerCase();
}
// For regular keys, check both key and code as fallback

View File

@@ -1,11 +1,12 @@
import dayjs from "dayjs";
import type { ViewScope } from "./link.js";
import FNote from "../entities/fnote";
const SVG_MIME = "image/svg+xml";
export const isShare = !window.glob;
function reloadFrontendApp(reason?: string) {
export function reloadFrontendApp(reason?: string) {
if (reason) {
logInfo(`Frontend app reload: ${reason}`);
}
@@ -13,7 +14,7 @@ function reloadFrontendApp(reason?: string) {
window.location.reload();
}
function restartDesktopApp() {
export function restartDesktopApp() {
if (!isElectron()) {
reloadFrontendApp();
return;
@@ -125,7 +126,7 @@ function formatDateISO(date: Date) {
return `${date.getFullYear()}-${padNum(date.getMonth() + 1)}-${padNum(date.getDate())}`;
}
function formatDateTime(date: Date, userSuppliedFormat?: string): string {
export function formatDateTime(date: Date, userSuppliedFormat?: string): string {
if (userSuppliedFormat?.trim()) {
return dayjs(date).format(userSuppliedFormat);
} else {
@@ -144,11 +145,11 @@ function now() {
/**
* Returns `true` if the client is currently running under Electron, or `false` if running in a web browser.
*/
function isElectron() {
export function isElectron() {
return !!(window && window.process && window.process.type);
}
function isMac() {
export function isMac() {
return navigator.platform.indexOf("Mac") > -1;
}
@@ -185,7 +186,11 @@ export function escapeQuotes(value: string) {
return value.replaceAll('"', "&quot;");
}
function formatSize(size: number) {
export function formatSize(size: number | null | undefined) {
if (size === null || size === undefined) {
return "";
}
size = Math.max(Math.round(size / 1024), 1);
if (size < 1024) {
@@ -218,7 +223,7 @@ function randomString(len: number) {
return text;
}
function isMobile() {
export function isMobile() {
return (
window.glob?.device === "mobile" ||
// window.glob.device is not available in setup
@@ -292,7 +297,55 @@ function isHtmlEmpty(html: string) {
);
}
async function clearBrowserCache() {
function formatHtml(html: string) {
let indent = "\n";
const tab = "\t";
let i = 0;
let pre: { indent: string; tag: string }[] = [];
html = html
.replace(new RegExp("<pre>([\\s\\S]+?)?</pre>"), function (x) {
pre.push({ indent: "", tag: x });
return "<--TEMPPRE" + i++ + "/-->";
})
.replace(new RegExp("<[^<>]+>[^<]?", "g"), function (x) {
let ret;
const tagRegEx = /<\/?([^\s/>]+)/.exec(x);
let tag = tagRegEx ? tagRegEx[1] : "";
let p = new RegExp("<--TEMPPRE(\\d+)/-->").exec(x);
if (p) {
const pInd = parseInt(p[1]);
pre[pInd].indent = indent;
}
if (["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"].indexOf(tag) >= 0) {
// self closing tag
ret = indent + x;
} else {
if (x.indexOf("</") < 0) {
//open tag
if (x.charAt(x.length - 1) !== ">") ret = indent + x.substr(0, x.length - 1) + indent + tab + x.substr(x.length - 1, x.length);
else ret = indent + x;
!p && (indent += tab);
} else {
//close tag
indent = indent.substr(0, indent.length - 1);
if (x.charAt(x.length - 1) !== ">") ret = indent + x.substr(0, x.length - 1) + indent + x.substr(x.length - 1, x.length);
else ret = indent + x;
}
}
return ret;
});
for (i = pre.length; i--;) {
html = html.replace("<--TEMPPRE" + i + "/-->", pre[i].tag.replace("<pre>", "<pre>\n").replace("</pre>", pre[i].indent + "</pre>"));
}
return html.charAt(0) === "\n" ? html.substr(1, html.length - 1) : html;
}
export async function clearBrowserCache() {
if (isElectron()) {
const win = dynamicRequire("@electron/remote").getCurrentWindow();
await win.webContents.session.clearCache();
@@ -306,7 +359,13 @@ function copySelectionToClipboard() {
}
}
function dynamicRequire(moduleName: string) {
type dynamicRequireMappings = {
"@electron/remote": typeof import("@electron/remote"),
"electron": typeof import("electron"),
"child_process": typeof import("child_process")
};
export function dynamicRequire<T extends keyof dynamicRequireMappings>(moduleName: T): Awaited<dynamicRequireMappings[T]>{
if (typeof __non_webpack_require__ !== "undefined") {
return __non_webpack_require__(moduleName);
} else {
@@ -374,33 +433,42 @@ async function openInAppHelp($button: JQuery<HTMLElement>) {
const inAppHelpPage = $button.attr("data-in-app-help");
if (inAppHelpPage) {
// Dynamic import to avoid import issues in tests.
const appContext = (await import("../components/app_context.js")).default;
const activeContext = appContext.tabManager.getActiveContext();
if (!activeContext) {
return;
}
const subContexts = activeContext.getSubContexts();
const targetNote = `_help_${inAppHelpPage}`;
const helpSubcontext = subContexts.find((s) => s.viewScope?.viewMode === "contextual-help");
const viewScope: ViewScope = {
viewMode: "contextual-help",
};
if (!helpSubcontext) {
// The help is not already open, open a new split with it.
const { ntxId } = subContexts[subContexts.length - 1];
appContext.triggerCommand("openNewNoteSplit", {
ntxId,
notePath: targetNote,
hoistedNoteId: "_help",
viewScope
})
} else {
// There is already a help window open, make sure it opens on the right note.
helpSubcontext.setNote(targetNote, { viewScope });
}
openInAppHelpFromUrl(inAppHelpPage);
}
}
/**
* Opens the in-app help at the given page in a split note. If there already is a split note open with a help page, it will be replaced by this one.
*
* @param inAppHelpPage the ID of the help note (excluding the `_help_` prefix).
* @returns a promise that resolves once the help has been opened.
*/
export async function openInAppHelpFromUrl(inAppHelpPage: string) {
// Dynamic import to avoid import issues in tests.
const appContext = (await import("../components/app_context.js")).default;
const activeContext = appContext.tabManager.getActiveContext();
if (!activeContext) {
return;
}
const subContexts = activeContext.getSubContexts();
const targetNote = `_help_${inAppHelpPage}`;
const helpSubcontext = subContexts.find((s) => s.viewScope?.viewMode === "contextual-help");
const viewScope: ViewScope = {
viewMode: "contextual-help",
};
if (!helpSubcontext) {
// The help is not already open, open a new split with it.
const { ntxId } = subContexts[subContexts.length - 1];
appContext.triggerCommand("openNewNoteSplit", {
ntxId,
notePath: targetNote,
hoistedNoteId: "_help",
viewScope
})
} else {
// There is already a help window open, make sure it opens on the right note.
helpSubcontext.setNote(targetNote, { viewScope });
}
}
function initHelpButtons($el: JQuery<HTMLElement> | JQuery<Window>) {
@@ -561,8 +629,7 @@ function copyHtmlToClipboard(content: string) {
document.removeEventListener("copy", listener);
}
// TODO: Set to FNote once the file is ported.
function createImageSrcUrl(note: { noteId: string; title: string }) {
export function createImageSrcUrl(note: FNote) {
return `api/images/${note.noteId}/${encodeURIComponent(note.title)}?timestamp=${Date.now()}`;
}
@@ -731,10 +798,86 @@ function isUpdateAvailable(latestVersion: string | null | undefined, currentVers
return compareVersions(latestVersion, currentVersion) > 0;
}
function isLaunchBarConfig(noteId: string) {
export function isLaunchBarConfig(noteId: string) {
return ["_lbRoot", "_lbAvailableLaunchers", "_lbVisibleLaunchers", "_lbMobileRoot", "_lbMobileAvailableLaunchers", "_lbMobileVisibleLaunchers"].includes(noteId);
}
/**
* Adds a class to the <body> of the page, where the class name is formed via a prefix and a value.
* Useful for configurable options such as `heading-style-markdown`, where `heading-style` is the prefix and `markdown` is the dynamic value.
* There is no separator between the prefix and the value, if needed it has to be supplied manually to the prefix.
*
* @param prefix the prefix.
* @param value the value to be appended to the prefix.
*/
export function toggleBodyClass(prefix: string, value: string) {
const $body = $("body");
for (const clazz of Array.from($body[0].classList)) {
// create copy to safely iterate over while removing classes
if (clazz.startsWith(prefix)) {
$body.removeClass(clazz);
}
}
$body.addClass(prefix + value);
}
/**
* Basic comparison for equality between the two arrays. The values are strictly checked via `===`.
*
* @param a the first array to compare.
* @param b the second array to compare.
* @returns `true` if both arrays are equals, `false` otherwise.
*/
export function arrayEqual<T>(a: T[], b: T[]) {
if (a === b) {
return true;
}
if (a.length !== b.length) {
return false;
}
for (let i=0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
type Indexed<T extends object> = T & { index: number };
/**
* Given an object array, alters every object in the array to have an index field assigned to it.
*
* @param items the objects to be numbered.
* @returns the same object for convenience, with the type changed to indicate the new index field.
*/
export function numberObjectsInPlace<T extends object>(items: T[]): Indexed<T>[] {
let index = 0;
for (const item of items) {
(item as Indexed<T>).index = index++;
}
return items as Indexed<T>[];
}
export function mapToKeyValueArray<K extends string | number | symbol, V>(map: Record<K, V>) {
const values: { key: K, value: V }[] = [];
for (const [ key, value ] of Object.entries(map)) {
values.push({ key: key as K, value: value as V });
}
return values;
}
export function getErrorMessage(e: unknown) {
if (e && typeof e === "object" && "message" in e && typeof e.message === "string") {
return e.message;
} else {
return "Unknown error";
}
}
export default {
reloadFrontendApp,
restartDesktopApp,
@@ -760,6 +903,7 @@ export default {
getNoteTypeClass,
getMimeTypeClass,
isHtmlEmpty,
formatHtml,
clearBrowserCache,
copySelectionToClipboard,
dynamicRequire,

View File

@@ -1,4 +1,4 @@
import "./stylesheets/bootstrap.scss";
import "bootstrap/dist/css/bootstrap.min.css";
import "./stylesheets/auth.css";
// @TriliumNextTODO: is this even needed anymore?

View File

@@ -1,7 +1,7 @@
import "jquery";
import utils from "./services/utils.js";
import ko from "knockout";
import "./stylesheets/bootstrap.scss";
import "bootstrap/dist/css/bootstrap.min.css";
// TriliumNextTODO: properly make use of below types
// type SetupModelSetupType = "new-document" | "sync-from-desktop" | "sync-from-server" | "";

View File

@@ -1,6 +1,6 @@
import "normalize.css";
import "boxicons/css/boxicons.min.css";
import "@triliumnext/ckeditor5/content.css";
import "@triliumnext/ckeditor5/src/theme/ck-content.css";
import "@triliumnext/share-theme/styles/index.css";
import "@triliumnext/share-theme/scripts/index.js";

View File

@@ -1,2 +0,0 @@
/* Import all of Bootstrap's CSS */
@use "bootstrap/scss/bootstrap";

View File

@@ -28,6 +28,28 @@
--ck-mention-list-max-height: 500px;
}
body#trilium-app.motion-disabled *,
body#trilium-app.motion-disabled *::before,
body#trilium-app.motion-disabled *::after {
/* Disable transitions and animations */
transition: none !important;
animation: none !important;
}
body#trilium-app.shadows-disabled *,
body#trilium-app.shadows-disabled *::before,
body#trilium-app.shadows-disabled *::after {
/* Disable shadows */
box-shadow: none !important;
}
body#trilium-app.backdrop-effects-disabled *,
body#trilium-app.backdrop-effects-disabled *::before,
body#trilium-app.backdrop-effects-disabled *::after {
/* Disable backdrop effects */
backdrop-filter: none !important;
}
.table {
--bs-table-bg: transparent !important;
}
@@ -139,12 +161,26 @@ textarea,
color: var(--muted-text-color);
}
.form-group.disabled {
opacity: 0.5;
pointer-events: none;
}
.form-group {
margin-bottom: 15px;
}
/* Add a gap between consecutive radios / check boxes */
label.tn-radio + label.tn-radio,
label.tn-checkbox + label.tn-checkbox {
margin-left: 12px;
}
label.tn-radio input[type="radio"],
label.tn-checkbox input[type="checkbox"] {
margin-right: .5em;
}
#left-pane input,
#left-pane select,
#left-pane textarea {
@@ -346,7 +382,7 @@ body.desktop .tabulator-popup-container {
@supports (animation-fill-mode: forwards) {
/* Delay the opening of submenus */
body.desktop .dropdown-submenu .dropdown-menu {
body.desktop:not(.motion-disabled) .dropdown-submenu .dropdown-menu {
opacity: 0;
animation-fill-mode: forwards;
animation-delay: var(--submenu-opening-delay);
@@ -406,14 +442,20 @@ body #context-menu-container .dropdown-item > span {
align-items: center;
}
.dropdown-menu kbd {
.dropdown-item span.keyboard-shortcut {
flex-grow: 1;
text-align: right;
}
.dropdown-menu kbd {
color: var(--muted-text-color);
border: none;
background-color: transparent;
box-shadow: none;
padding-bottom: 0;
padding: 0;
flex-grow: 1;
text-align: right;
}
.dropdown-item,
@@ -831,10 +873,34 @@ table.promoted-attributes-in-tooltip th {
.aa-dropdown-menu .aa-suggestion {
cursor: pointer;
padding: 5px;
padding: 6px 16px;
margin: 0;
}
.aa-dropdown-menu .aa-suggestion .icon {
display: inline-block;
line-height: inherit;
vertical-align: top;
}
.aa-dropdown-menu .aa-suggestion .text {
display: inline-block;
width: calc(100% - 20px);
padding-left: 4px;
}
.aa-dropdown-menu .aa-suggestion .search-result-title {
display: block;
}
.aa-dropdown-menu .aa-suggestion .search-result-attributes {
display: block;
font-size: 0.8em;
color: var(--muted-text-color);
opacity: 0.6;
line-height: 1;
}
.aa-dropdown-menu .aa-suggestion p {
padding: 0;
margin: 0;
@@ -922,6 +988,11 @@ div[data-notify="container"] {
font-family: var(--monospace-font-family);
}
svg.ck-icon .note-icon {
color: var(--main-text-color);
font-size: 20px;
}
.ck-content {
--ck-content-font-family: var(--detail-font-family);
--ck-content-font-size: 1.1em;
@@ -1063,6 +1134,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
.toast-body {
white-space: preserve-breaks;
overflow: hidden;
}
.ck-mentions .ck-button {
@@ -1171,6 +1243,10 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
cursor: row-resize;
}
.hidden-ext.note-split + .gutter {
display: none;
}
#context-menu-cover.show {
position: fixed;
top: 0;
@@ -1392,7 +1468,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
cursor: pointer;
border: none;
color: var(--launcher-pane-text-color);
background-color: var(--launcher-pane-background-color);
background: transparent;
flex-shrink: 0;
}
@@ -1700,7 +1776,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
}
.note-split {
flex-basis: 0; /* so that each split has same width */
margin-left: auto;
margin-right: auto;
}
@@ -1738,16 +1813,12 @@ button.close:hover {
margin-bottom: 10px;
}
.options-number-input {
.options-section input[type="number"] {
/* overriding settings from .form-control */
width: 10em !important;
flex-grow: 0 !important;
}
.options-mime-types {
column-width: 250px;
}
textarea {
cursor: auto;
}
@@ -1768,20 +1839,42 @@ textarea {
font-size: 1em;
}
.jump-to-note-dialog .modal-dialog {
max-width: 900px;
width: 90%;
}
.jump-to-note-dialog .modal-header {
align-items: center;
}
.jump-to-note-dialog .modal-body {
padding: 0;
min-height: 200px;
}
.jump-to-note-results .aa-dropdown-menu {
max-height: 40vh;
max-height: calc(80vh - 200px);
width: 100%;
max-width: none;
overflow-y: auto;
overflow-x: hidden;
text-overflow: ellipsis;
box-shadow: none;
}
.jump-to-note-results {
width: 100%;
}
.jump-to-note-results .aa-suggestions {
padding: 1rem;
padding: 0;
width: 100%;
}
.jump-to-note-results .aa-dropdown-menu .aa-suggestion:hover,
.jump-to-note-results .aa-dropdown-menu .aa-cursor {
background-color: var(--hover-item-background-color, #f8f9fa);
}
/* Command palette styling */
@@ -1799,8 +1892,24 @@ textarea {
.jump-to-note-dialog .aa-cursor .command-suggestion,
.jump-to-note-dialog .aa-suggestion:hover .command-suggestion {
border-left-color: var(--link-color);
background-color: var(--hover-background-color);
background-color: transparent;
}
.jump-to-note-dialog .show-in-full-search,
.jump-to-note-results .show-in-full-search {
border-top: 1px solid var(--main-border-color);
padding-top: 12px;
margin-top: 12px;
}
.jump-to-note-results .aa-suggestion .search-notes-action {
border-top: 1px solid var(--main-border-color);
margin-top: 8px;
padding-top: 8px;
}
.jump-to-note-results .aa-suggestion:has(.search-notes-action)::after {
display: none;
}
.jump-to-note-dialog .command-icon {
@@ -2255,16 +2364,27 @@ footer.webview-footer button {
padding: 1px 10px 1px 10px;
}
/* Search result highlighting */
.search-result-title b,
.search-result-content b {
font-weight: 900;
color: var(--admonition-warning-accent-color);
}
/* Customized icons */
.bx-tn-toc::before {
content: "\ec24";
transform: rotate(180deg);
}
/* CK Edito */
/* Insert text snippet: limit the width of the listed items to avoid overly long names */
:root body.desktop div.ck-template-form li.ck-list__item .ck-template-form__text-part > span {
max-width: 25vw;
overflow: hidden;
text-overflow: ellipsis;
}
.revision-diff-added {
background: rgba(100, 200, 100, 0.5);
}
.revision-diff-removed {
background: rgba(255, 100, 100, 0.5);
text-decoration: line-through;
}

View File

@@ -13,12 +13,13 @@
--theme-style: dark;
--native-titlebar-background: #00000000;
--window-background-color-bgfx: transparent; /* When background effects enabled */
--main-background-color: #272727;
--main-text-color: #ccc;
--main-border-color: #454545;
--subtle-border-color: #313131;
--dropdown-border-color: #292929;
--dropdown-border-color: #404040;
--dropdown-shadow-opacity: 0.6;
--dropdown-item-icon-destructive-color: #de6e5b;
--disabled-tooltip-icon-color: #7fd2ef;
@@ -89,6 +90,7 @@
--menu-text-color: #e3e3e3;
--menu-background-color: #222222d9;
--menu-background-color-no-backdrop: #1b1b1b;
--menu-item-icon-color: #8c8c8c;
--menu-item-disabled-opacity: 0.5;
--menu-item-keyboard-shortcut-color: #ffffff8f;
@@ -120,6 +122,8 @@
--quick-search-focus-border: #80808095;
--quick-search-focus-background: #ffffff1f;
--quick-search-focus-color: white;
--quick-search-result-content-background: #0000004d;
--quick-search-result-highlight-color: #a4d995;
--left-pane-collapsed-border-color: #0009;
--left-pane-background-color: #1f1f1f;
@@ -144,6 +148,7 @@
--launcher-pane-vert-button-hover-background: #ffffff1c;
--launcher-pane-vert-button-hover-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
--launcher-pane-vert-button-focus-outline-color: var(--input-focus-outline-color);
--launcher-pane-vert-background-color-bgfx: #00000026; /* When background effects enabled */
--launcher-pane-horiz-border-color: rgb(22, 22, 22);
--launcher-pane-horiz-background-color: #282828;
@@ -152,6 +157,8 @@
--launcher-pane-horiz-button-hover-background: #ffffff1c;
--launcher-pane-horiz-button-hover-shadow: unset;
--launcher-pane-horiz-button-focus-outline-color: var(--input-focus-outline-color);
--launcher-pane-horiz-background-color-bgfx: #ffffff17; /* When background effects enabled */
--launcher-pane-horiz-border-color-bgfx: #00000080; /* When background effects enabled */
--protected-session-active-icon-color: #8edd8e;
--sync-status-error-pulse-color: #f47871;

View File

@@ -13,6 +13,7 @@
--theme-style: light;
--native-titlebar-background: #ffffff00;
--window-background-color-bgfx: transparent; /* When background effects enabled */
--main-background-color: white;
--main-text-color: black;
@@ -83,6 +84,7 @@
--menu-text-color: #272727;
--menu-background-color: #ffffffd9;
--menu-background-color-no-backdrop: #fdfdfd;
--menu-item-icon-color: #727272;
--menu-item-disabled-opacity: 0.6;
--menu-item-keyboard-shortcut-color: #666666a8;
@@ -114,15 +116,17 @@
--quick-search-focus-border: #00000029;
--quick-search-focus-background: #ffffff80;
--quick-search-focus-color: #000;
--quick-search-result-content-background: #0000000f;
--quick-search-result-highlight-color: #c65050;
--left-pane-collapsed-border-color: #0000000d;
--left-pane-background-color: #f2f2f2;
--left-pane-text-color: #383838;
--left-pane-item-hover-background: #eaeaea;
--left-pane-item-hover-background: rgba(0, 0, 0, 0.032);
--left-pane-item-selected-background: white;
--left-pane-item-selected-color: black;
--left-pane-item-selected-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
--left-pane-item-action-button-background: #d7d7d7;
--left-pane-item-action-button-background: rgba(0, 0, 0, 0.11);
--left-pane-item-action-button-color: inherit;
--left-pane-item-action-button-hover-background: white;
--left-pane-item-action-button-hover-shadow: 2px 2px 3px rgba(0, 0, 0, 0.15);
@@ -138,6 +142,7 @@
--launcher-pane-vert-button-hover-background: white;
--launcher-pane-vert-button-hover-shadow: 4px 4px 4px rgba(0, 0, 0, 0.075);
--launcher-pane-vert-button-focus-outline-color: var(--input-focus-outline-color);
--launcher-pane-vert-background-color-bgfx: #00000009; /* When background effects enabled */
--launcher-pane-horiz-border-color: rgba(0, 0, 0, 0.1);
--launcher-pane-horiz-background-color: #fafafa;
@@ -145,6 +150,8 @@
--launcher-pane-horiz-button-hover-background: var(--icon-button-hover-background);
--launcher-pane-horiz-button-hover-shadow: unset;
--launcher-pane-horiz-button-focus-outline-color: var(--input-focus-outline-color);
--launcher-pane-horiz-background-color-bgfx: #ffffffb3; /* When background effects enabled */
--launcher-pane-horiz-border-color-bgfx: #00000026; /* When background effects enabled */
--protected-session-active-icon-color: #16b516;
--sync-status-error-pulse-color: #ff5528;

View File

@@ -83,6 +83,12 @@
--tab-note-icons: true;
}
body.backdrop-effects-disabled {
/* Backdrop effects are disabled, replace the menu background color with the
* no-backdrop fallback color */
--menu-background-color: var(--menu-background-color-no-backdrop);
}
/*
* MENUS
*
@@ -191,13 +197,17 @@ html body .dropdown-item[disabled] {
/* Menu item keyboard shortcut */
.dropdown-item kbd {
margin-left: 16px;
font-family: unset !important;
font-size: unset !important;
color: var(--menu-item-keyboard-shortcut-color) !important;
padding-top: 0;
}
.dropdown-item span.keyboard-shortcut {
color: var(--menu-item-keyboard-shortcut-color) !important;
margin-left: 16px;
}
.dropdown-divider {
position: relative;
border-color: transparent !important;
@@ -319,6 +329,8 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
#toast-container .toast .toast-body {
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
}
/*
@@ -530,10 +542,9 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
}
/* List item */
.jump-to-note-dialog .aa-suggestions div,
.note-detail-empty .aa-suggestions div {
.jump-to-note-dialog .aa-suggestion,
.note-detail-empty .aa-suggestion {
border-radius: 6px;
padding: 6px 12px;
color: var(--menu-text-color);
cursor: default;
}

View File

@@ -5,7 +5,8 @@
button.btn.btn-primary,
button.btn.btn-secondary,
button.btn.btn-sm:not(.select-button),
button.btn.btn-success {
button.btn.btn-success,
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -21,7 +22,8 @@ button.btn.btn-success {
button.btn.btn-primary:hover,
button.btn.btn-secondary:hover,
button.btn.btn-sm:not(.select-button):hover,
button.btn.btn-success:hover {
button.btn.btn-success:hover,
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text:not(.ck-disabled):hover {
background: var(--cmd-button-hover-background-color);
color: var(--cmd-button-hover-text-color);
}
@@ -29,7 +31,8 @@ button.btn.btn-success:hover {
button.btn.btn-primary:active,
button.btn.btn-secondary:active,
button.btn.btn-sm:not(.select-button):active,
button.btn.btn-success:active {
button.btn.btn-success:active,
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text:not(.ck-disabled):active {
opacity: 0.85;
box-shadow: unset;
background: var(--cmd-button-background-color) !important;
@@ -40,14 +43,16 @@ button.btn.btn-success:active {
button.btn.btn-primary:disabled,
button.btn.btn-secondary:disabled,
button.btn.btn-sm:not(.select-button):disabled,
button.btn.btn-success:disabled {
button.btn.btn-success:disabled,
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text.ck-disabled {
opacity: var(--cmd-button-disabled-opacity);
}
button.btn.btn-primary:focus-visible,
button.btn.btn-secondary:focus-visible,
button.btn.btn-sm:not(.select-button):focus-visible,
button.btn.btn-success:focus-visible {
button.btn.btn-success:focus-visible,
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel, .ck-button-replaceall, .ck-button-replace).ck-button_with-text:not(.ck-disabled):focus-visible {
outline: 2px solid var(--input-focus-outline-color);
}
@@ -149,8 +154,11 @@ input[type="password"],
input[type="date"],
input[type="time"],
input[type="datetime-local"],
:root input.ck.ck-input-text,
:root input.ck.ck-input-number,
textarea.form-control,
textarea,
:root textarea.ck.ck-textarea,
.tn-input-field {
outline: 3px solid transparent;
outline-offset: 6px;
@@ -167,8 +175,11 @@ input[type="password"]:hover,
input[type="date"]:hover,
input[type="time"]:hover,
input[type="datetime-local"]:hover,
:root input.ck.ck-input-text:not([readonly="true"]):hover,
:root input.ck.ck-input-number:not([readonly="true"]):hover,
textarea.form-control:hover,
textarea:hover,
:root textarea.ck.ck-textarea:hover,
.tn-input-field:hover {
background: var(--input-hover-background);
color: var(--input-hover-color);
@@ -181,8 +192,11 @@ input[type="password"]:focus,
input[type="date"]:focus,
input[type="time"]:focus,
input[type="datetime-local"]:focus,
:root input.ck.ck-input-text:focus,
:root input.ck.ck-input-number:focus,
textarea.form-control:focus,
textarea:focus,
:root textarea.ck.ck-textarea:focus,
.tn-input-field:focus,
.tn-input-field:focus-within {
box-shadow: unset;
@@ -455,6 +469,7 @@ optgroup {
left: 0;
width: var(--box-size);
height: 100%;
margin: unset;
opacity: 0 !important;
}

View File

@@ -4,6 +4,7 @@
:root {
--ck-font-face: var(--main-font-family);
--ck-input-label-height: 1.5em;
}
/*
@@ -307,6 +308,11 @@
fill: black !important;
}
/* Hex color input box prefix */
:root .ck.ck-color-selector .ck-color-picker__hash-view {
margin-top: var(--ck-input-label-height);
}
/* Numbered list */
:root .ck.ck-list-properties_with-numbered-properties .ck.ck-list-styles-list {
@@ -363,19 +369,86 @@
color: var(--accent);
}
/* Action buttons */
/* Text snippet dropdown */
:root .ck-link-actions button.ck-button,
:root .ck-link-form button.ck-button {
--ck-border-radius: 6px;
background: transparent;
box-shadow: unset;
div.ck-template-form {
padding: 8px;
}
:root .ck-link-actions button.ck-button:hover,
:root .ck-link-form button.ck-button:hover {
background: var(--hover-item-background-color);
div.ck-template-form .ck-labeled-field-view {
margin-bottom: 8px;
}
/* Template item */
:root div.ck-template-form li.ck-list__item button.ck-template-button {
padding: 4px 8px;
}
/* Template icon */
:root .ck-template-form .ck-button__icon {
--ck-spacing-medium: 2px;
}
:root div.ck-template-form .note-icon {
color: var(--menu-item-icon-color);
}
/* Template name */
div.ck-template-form .ck-template-form__text-part {
color: var(--hover-item-text-color);
font-size: .9rem;
}
div.ck-template-form .ck-template-form__text-part mark {
background: unset;
color: var(--quick-search-result-highlight-color);
font-weight: bold;
}
/* Template description */
:root div.ck-template-form .ck-template-form__description {
opacity: .5;
font-size: .9em;
}
/* Messages */
div.ck-template-form .ck-search__info > span {
line-height: initial;
color: var(--muted-text-color);
}
div.ck-template-form .ck-search__info span:nth-child(2) {
display: block;
opacity: .5;
margin-top: 8px;
font-size: .9em;
}
/* Link dropdown */
:root .ck.ck-form.ck-link-form ul.ck-link-form__providers-list {
border-top: none;
}
/* Math popup */
.ck-math-form .ck-labeled-field-view {
--ck-input-label-height: 0;
margin-inline-end: 8px;
}
/* Emoji dropdown */
.ck-emoji-picker-form .ck-emoji__search .ck-button_with-text:not(.ck-list-item-button) {
margin-top: var(--ck-input-label-height);
}
/* Find and replace dialog */
.ck-find-and-replace-form .ck-find-and-replace-form__inputs button {
margin-top: var(--ck-input-label-height);
}
/* Mention list (the autocompletion list for emojis, labels and relations) */
@@ -392,6 +465,58 @@
background: transparent;
}
/*
* FORMS
*/
/*
* Buttons
*/
button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck-button_with-text {
--ck-color-text: var(--cmd-button-text-color);
min-width: 60px;
font-weight: 500;
}
/*
* Text boxes
*/
.ck.ck-labeled-field-view {
padding-top: var(--ck-input-label-height) !important; /* Create space for the label */
}
.ck.ck-labeled-field-view > .ck.ck-labeled-field-view__input-wrapper > label.ck.ck-label {
/* Move the label above the text box regardless of the text box state */
transform: translate(0, calc(-.2em - var(--ck-input-label-height))) !important;
padding-left: 0 !important;
background: transparent;
font-size: .85em;
font-weight: 600;
}
:root input.ck.ck-input-text[readonly="true"] {
cursor: not-allowed;
background: var(--input-background-color);
}
/* Forms */
:root .ck.ck-form__row.ck-form__row_with-submit > :not(:first-child) {
margin-inline-start: 16px;
}
.ck.ck-form__row_with-submit button {
margin-top: var(--ck-input-label-height);
}
.ck.ck-form__header {
border-bottom: none;
}
/*
* EDITOR'S CONTENT
*/

View File

@@ -96,7 +96,6 @@
background: var(--background) !important;
color: var(--color) !important;
line-height: unset;
cursor: help;
}
.sql-table-schemas-widget .sql-table-schemas button:hover,
@@ -106,18 +105,6 @@
--color: var(--main-text-color);
}
/* Tooltip */
.tooltip .table-schema {
font-family: var(--monospace-font-family);
font-size: .85em;
}
/* Data type */
.tooltip .table-schema td:nth-child(2) {
color: var(--muted-text-color);
}
/*
* NOTE MAP
*/
@@ -181,9 +168,7 @@ div.note-detail-empty {
}
.options-section:not(.tn-no-card) {
margin: auto;
min-width: var(--options-card-min-width);
max-width: var(--options-card-max-width);
margin: auto;
border-radius: 12px;
border: 1px solid var(--card-border-color) !important;
box-shadow: var(--card-box-shadow);
@@ -192,6 +177,11 @@ div.note-detail-empty {
margin-bottom: calc(var(--options-title-offset) + 26px) !important;
}
body.desktop .option-section:not(.tn-no-card) {
min-width: var(--options-card-min-width);
max-width: var(--options-card-max-width);
}
.note-detail-content-widget-content.options {
--default-padding: 15px;
padding-top: calc(var(--default-padding) + var(--options-title-offset) + var(--options-title-font-size));
@@ -233,11 +223,6 @@ div.note-detail-empty {
margin-bottom: 0;
}
.options-section .options-mime-types {
padding: 0;
margin: 0;
}
.options-section .form-group {
margin-bottom: 1em;
}

View File

@@ -36,32 +36,23 @@ body.mobile {
/* #region Mica */
body.background-effects.platform-win32 {
--launcher-pane-horiz-border-color: rgba(0, 0, 0, 0.15);
--launcher-pane-horiz-background-color: rgba(255, 255, 255, 0.7);
--launcher-pane-vert-background-color: rgba(255, 255, 255, 0.055);
--tab-background-color: transparent;
--new-tab-button-background: transparent;
--active-tab-background-color: var(--launcher-pane-horiz-background-color);
--background-material: tabbed;
}
@media (prefers-color-scheme: dark) {
body.background-effects.platform-win32 {
--launcher-pane-horiz-border-color: rgba(0, 0, 0, 0.5);
--launcher-pane-horiz-background-color: rgba(255, 255, 255, 0.09);
}
--launcher-pane-horiz-border-color: var(--launcher-pane-horiz-border-color-bgfx);
--launcher-pane-horiz-background-color: var(--launcher-pane-horiz-background-color-bgfx);
--launcher-pane-vert-background-color: var(--launcher-pane-vert-background-color-bgfx);
--tab-background-color: var(--window-background-color-bgfx);
--new-tab-button-background: var(--window-background-color-bgfx);
--active-tab-background-color: var(--launcher-pane-horiz-background-color);
}
body.background-effects.platform-win32.layout-vertical {
--left-pane-background-color: transparent;
--left-pane-item-hover-background: rgba(127, 127, 127, 0.05);
--left-pane-background-color: var(--window-background-color-bgfx);
--background-material: mica;
}
body.background-effects.platform-win32,
body.background-effects.platform-win32 #root-widget,
body.background-effects.platform-win32 #launcher-pane .launcher-button {
background: transparent !important;
body.background-effects.platform-win32 #root-widget {
background: var(--window-background-color-bgfx) !important;
}
body.background-effects.platform-win32.layout-horizontal #horizontal-main-container,
@@ -90,7 +81,7 @@ body.background-effects.zen #root-widget {
* Gutter
*/
.gutter {
.gutter {
background: var(--gutter-color) !important;
transition: background 150ms ease-out;
}
@@ -330,7 +321,6 @@ body.layout-horizontal > .horizontal {
*/
.calendar-dropdown-widget {
width: unset !important;
padding: 12px;
color: var(--calendar-color);
user-select: none;
@@ -576,25 +566,18 @@ div.quick-search .search-button.show {
* Quick search results
*/
div.quick-search .dropdown-menu {
--quick-search-item-delimiter-color: transparent;
--menu-item-icon-vert-offset: -.065em;
}
/* Item */
.quick-search .dropdown-menu *.dropdown-item {
padding: 8px 12px !important;
}
/* Note icon */
.quick-search .dropdown-menu .dropdown-item > .bx {
position: relative;
top: 1px;
}
/* Note title */
.quick-search .dropdown-menu .dropdown-item > a {
color: var(--menu-text-color);
}
.quick-search .dropdown-menu .dropdown-item > a:hover {
--hover-item-background-color: transparent;
text-decoration: underline;
.quick-search .quick-search-item-icon {
vertical-align: text-bottom;
}
/* Note path */
@@ -605,6 +588,24 @@ div.quick-search .search-button.show {
overflow: hidden;
}
/* Note content snippet */
:root .quick-search .search-result-content {
background-color: var(--quick-search-result-content-background);
border-radius: 4px;
}
/* Highlighted search terms */
:root .quick-search .search-result-title b,
:root .quick-search .search-result-content b,
:root .quick-search .search-result-attributes b {
color: var(--quick-search-result-highlight-color);
font-weight: 600;
}
.quick-search div.dropdown-divider {
margin: 8px 0;
}
/*
* TREE PANE
*/
@@ -877,6 +878,80 @@ body.layout-horizontal .tab-row-container {
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
body.electron.background-effects.layout-horizontal .tab-row-container {
border-bottom: unset !important;
}
body.electron.background-effects.layout-horizontal .note-tab-wrapper {
top: 1px;
}
body.electron.background-effects.layout-horizontal .tab-row-container .toggle-button {
position: relative;
}
body.electron.background-effects.layout-horizontal .tab-row-container .toggle-button:after {
content: "";
position: absolute;
bottom: 0;
left: -10px;
right: -10px;
top: 29px;
height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-left,
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-right {
position: relative;
}
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-left:after,
body.electron.background-effects.layout-horizontal .tab-row-container .tab-scroll-button-right:after {
content: "";
position: absolute;
bottom: 0;
left: 0px;
right: 0px;
height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
body.electron.background-effects.layout-horizontal .tab-row-container .note-tab[active]:before {
content: "";
position: absolute;
bottom: 0;
left: -32768px;
top: var(--tab-height);
right: calc(100% - 1px);
height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
body.electron.background-effects.layout-horizontal .tab-row-container .note-tab[active]:after {
content: "";
position: absolute;
bottom: 0;
left: 100%;
top: var(--tab-height);
right: 0;
width: 100vw;
height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
body.electron.background-effects.layout-horizontal .tab-row-container .note-new-tab:before {
content: "";
position: absolute;
bottom: 0;
left: -4px;
top: calc(var(--tab-height), -1);
right: 0;
width: 100vw;
height: 1px;
border-bottom: 1px solid var(--launcher-pane-horiz-border-color);
}
body.layout-vertical.electron.platform-darwin .tab-row-container {
border-bottom: 1px solid var(--subtle-border-color);
}
@@ -1092,6 +1167,11 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
/* will-change: opacity; -- causes some weird artifacts to the note menu in split view */
}
.split-note-container-widget > .gutter {
background: var(--root-background) !important;
transition: background 150ms ease-out;
}
/*
* Ribbon & note header
*/
@@ -1100,10 +1180,6 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
margin-bottom: 0 !important;
}
.note-split:not(.hidden-ext) + .note-split:not(.hidden-ext) {
border-left: 4px solid var(--root-background);
}
@keyframes note-entrance {
from {
opacity: 0;
@@ -1375,7 +1451,7 @@ div.floating-buttons-children .floating-button:active {
}
/* The first visible floating button */
div.floating-buttons-children > *:nth-child(1 of .visible) {
div.floating-buttons-children > *:first-child {
--border-radius: var(--border-radius-size) 0 0 var(--border-radius-size);
border-radius: var(--border-radius);
}
@@ -1477,13 +1553,6 @@ div.floating-buttons-children .close-floating-buttons:has(.close-floating-button
padding-inline-start: 8px;
}
/* Copy image reference */
.floating-buttons .copy-image-reference-button .hidden-image-copy {
/* Take out of the the hidden image from flexbox to prevent the layout being affected */
position: absolute;
}
/* Code, relation map buttons */
.floating-buttons .code-buttons-widget,

View File

@@ -732,7 +732,8 @@
"note_type": "笔记类型",
"editable": "可编辑",
"basic_properties": "基本属性",
"language": "语言"
"language": "语言",
"configure_code_notes": "配置代码注释..."
},
"book_properties": {
"view_type": "视图类型",
@@ -848,7 +849,7 @@
"debug": "调试",
"debug_description": "调试将打印额外的调试信息到控制台,以帮助调试复杂查询",
"action": "操作",
"search_button": "搜索 <kbd>回车</kbd>",
"search_button": "搜索",
"search_execute": "搜索并执行操作",
"save_to_note": "保存到笔记",
"search_parameters": "搜索参数",
@@ -967,7 +968,7 @@
},
"protected_session": {
"enter_password_instruction": "显示受保护的笔记需要输入您的密码:",
"start_session_button": "开始受保护的会话",
"start_session_button": "开始受保护的会话 <kbd>Enter</kbd>",
"started": "受保护的会话已启动。",
"wrong_password": "密码错误。",
"protecting-finished-successfully": "保护操作已成功完成。",
@@ -1028,7 +1029,7 @@
"error_creating_anonymized_database": "无法创建匿名化数据库,请检查后端日志以获取详细信息",
"successfully_created_fully_anonymized_database": "成功创建完全匿名化的数据库,路径为 {{anonymizedFilePath}}",
"successfully_created_lightly_anonymized_database": "成功创建轻度匿名化的数据库,路径为 {{anonymizedFilePath}}",
"no_anonymized_database_yet": "尚无匿名化数据库"
"no_anonymized_database_yet": "尚无匿名化数据库"
},
"database_integrity_check": {
"title": "数据库完整性检查",
@@ -1165,7 +1166,7 @@
},
"revisions_snapshot_interval": {
"note_revisions_snapshot_interval_title": "笔记修订快照间隔",
"note_revisions_snapshot_description": "笔记修订快照间隔是创建新笔记修订的时间。有关更多信息,请参见 <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a>。",
"note_revisions_snapshot_description": "笔记修订快照间隔是创建新笔记修订的时间。有关更多信息,请参见 <doc>wiki</doc>。",
"snapshot_time_interval_label": "笔记修订快照时间间隔:"
},
"revisions_snapshot_limit": {
@@ -1333,9 +1334,9 @@
"oauth_title": "OAuth/OpenID 认证",
"oauth_description": "OpenID 是一种标准化方式,允许您使用其他服务(如 Google的账号登录网站来验证您的身份。默认的身份提供者是 Google但您可以更改为任何其他 OpenID 提供者。点击<a href=\"#root/_hidden/_help/_help_Otzi9La2YAUX/_help_WOcw2SLH6tbX/_help_7DAiwaf8Z7Rz\">这里</a>了解更多信息。请参阅这些 <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">指南</a> 通过 Google 设置 OpenID 服务。",
"oauth_description_warning": "要启用 OAuth/OpenID您需要设置 config.ini 文件中的 OAuth/OpenID 基础 URL、客户端 ID 和客户端密钥,并重新启动应用程序。如果要从环境变量设置,请设置 TRILIUM_OAUTH_BASE_URL、TRILIUM_OAUTH_CLIENT_ID 和 TRILIUM_OAUTH_CLIENT_SECRET 环境变量。",
"oauth_missing_vars": "缺少以下设置项: {{missingVars}}",
"oauth_user_account": "用户账号:",
"oauth_user_email": "用户邮箱:",
"oauth_missing_vars": "缺少以下设置项{{variables}}",
"oauth_user_account": "用户账号: ",
"oauth_user_email": "用户邮箱: ",
"oauth_user_not_logged_in": "未登录!"
},
"shortcuts": {
@@ -1357,7 +1358,7 @@
"enable": "启用拼写检查",
"language_code_label": "语言代码",
"language_code_placeholder": "例如 \"en-US\", \"de-AT\"",
"multiple_languages_info": "多种语言可以用逗号分隔,例如 \"en-US, de-DE, cs\"。",
"multiple_languages_info": "多种语言可以用逗号分隔,例如 \"en-US, de-DE, cs\"。 ",
"available_language_codes_label": "可用的语言代码:",
"restart-required": "拼写检查选项的更改将在应用重启后生效。"
},
@@ -1439,9 +1440,9 @@
"open-in-popup": "快速编辑"
},
"shared_info": {
"shared_publicly": "此笔记已公开分享于",
"shared_locally": "笔记已在本地分享",
"help_link": "访问 <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a> 获取帮助。"
"help_link": "访问 <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a> 获取帮助。",
"shared_publicly": "笔记已在 {{- link}} 上公开分享",
"shared_locally": "此笔记在本地通过 {{- link}} 进行共享。"
},
"note_types": {
"text": "文本",
@@ -1519,7 +1520,8 @@
"hoist-this-note-workspace": "聚焦此笔记(工作区)",
"refresh-saved-search-results": "刷新保存的搜索结果",
"create-child-note": "创建子笔记",
"unhoist": "取消聚焦"
"unhoist": "取消聚焦",
"toggle-sidebar": "切换侧边栏"
},
"title_bar_buttons": {
"window-on-top": "保持此窗口置顶"
@@ -1871,14 +1873,19 @@
"selected_provider": "已选提供商",
"selected_provider_description": "选择用于聊天和补全功能的AI提供商",
"select_model": "选择模型...",
"select_provider": "选择提供商..."
"select_provider": "选择提供商...",
"ai_enabled": "已启用 AI 功能",
"ai_disabled": "已禁用 AI 功能",
"no_models_found_online": "找不到模型。请检查您的 API 密钥及设置。",
"no_models_found_ollama": "找不到 Ollama 模型。请确认 Ollama 是否正在运行。",
"error_fetching": "获取模型失败:{{error}}"
},
"code-editor-options": {
"title": "编辑器"
},
"custom_date_time_format": {
"title": "自定义日期/时间格式",
"description": "通过<kbd></kbd>或工具栏的方式可自定义日期和时间格式,有关日期/时间格式字符串中各个字符的含义,请参阅<a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">Day.js docs</a>。",
"description": "通过<shortcut />或工具栏的方式可自定义日期和时间格式,有关日期/时间格式字符串中各个字符的含义,请参阅<doc>Day.js docs</doc>。",
"format_string": "日期/时间格式字符串:",
"formatted_time": "格式化后日期/时间:"
},
@@ -1992,11 +1999,28 @@
"help_title": "显示关于此画面的更多信息"
},
"call_to_action": {
"next_theme_title": "新的 Trilium 主题已进入稳定版",
"next_theme_message": "有一段时间,我们一直在设计新的主题,为了让应用程序看起来更加现代。",
"next_theme_button": "切换至新的 Trilium 主题",
"background_effects_title": "背景效果现已推出稳定版本",
"background_effects_message": "在 Windows 装置上,背景效果现在已完全稳定。背景效果通过模糊背后的背景,为使用者界面增添一抹色彩。此技术也用于其他应用程序,例如 Windows 资源管理器。",
"background_effects_button": "启用背景效果"
"background_effects_button": "启用背景效果",
"next_theme_title": "试用新 Trilium 主题",
"next_theme_message": "当前使用旧版主题,要试用新主题吗?",
"next_theme_button": "试用新主题",
"dismiss": "关闭"
},
"settings": {
"related_settings": "相关设置"
},
"settings_appearance": {
"related_code_blocks": "文本笔记中代码块的色彩方案",
"related_code_notes": "代码笔记的色彩方案"
},
"units": {
"percentage": "%"
},
"ui-performance": {
"title": "性能",
"enable-motion": "启用过渡和动画",
"enable-shadows": "启用阴影",
"enable-backdrop-effects": "启用菜单、弹窗和面板的背景效果"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -263,6 +263,11 @@
"confirm_delete_all": "Do you want to delete all revisions of this note?",
"no_revisions": "No revisions for this note yet...",
"restore_button": "Restore",
"diff_on": "Show diff",
"diff_off": "Show content",
"diff_on_hint": "Click to show note source diff",
"diff_off_hint": "Click to show note content",
"diff_not_available": "Diff isn't available.",
"confirm_restore": "Do you want to restore this revision? This will overwrite the current title and content of the note with this revision.",
"delete_button": "Delete",
"confirm_delete": "Do you want to delete this revision?",
@@ -732,7 +737,8 @@
"note_type": "Note type",
"editable": "Editable",
"basic_properties": "Basic Properties",
"language": "Language"
"language": "Language",
"configure_code_notes": "Configure code notes..."
},
"book_properties": {
"view_type": "View type",
@@ -848,7 +854,7 @@
"debug": "debug",
"debug_description": "Debug will print extra debugging information into the console to aid in debugging complex queries",
"action": "action",
"search_button": "Search <kbd>enter</kbd>",
"search_button": "Search",
"search_execute": "Search & Execute actions",
"save_to_note": "Save to note",
"search_parameters": "Search Parameters",
@@ -1113,6 +1119,14 @@
"layout-vertical-description": "launcher bar is on the left (default)",
"layout-horizontal-description": "launcher bar is underneath the tab bar, the tab bar is now full width."
},
"ui-performance": {
"title": "Performance",
"enable-motion": "Enable transitions and animations",
"enable-shadows": "Enable shadows",
"enable-backdrop-effects": "Enable background effects for menus, popups and panels",
"enable-smooth-scroll": "Enable smooth scrolling",
"app-restart-required": "(a restart of the application is required for the change to take effect)"
},
"ai_llm": {
"not_started": "Not started",
"title": "AI Settings",
@@ -1253,7 +1267,12 @@
"selected_provider": "Selected Provider",
"selected_provider_description": "Choose the AI provider for chat and completion features",
"select_model": "Select model...",
"select_provider": "Select provider..."
"select_provider": "Select provider...",
"ai_enabled": "AI features enabled",
"ai_disabled": "AI features disabled",
"no_models_found_online": "No models found. Please check your API key and settings.",
"no_models_found_ollama": "No Ollama models found. Please check if Ollama is running.",
"error_fetching": "Error fetching models: {{error}}"
},
"zoom_factor": {
"title": "Zoom Factor (desktop build only)",
@@ -1310,7 +1329,7 @@
},
"revisions_snapshot_interval": {
"note_revisions_snapshot_interval_title": "Note Revision Snapshot Interval",
"note_revisions_snapshot_description": "The Note revision snapshot interval is the time after which a new note revision will be created for the note. See <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> for more info.",
"note_revisions_snapshot_description": "The Note revision snapshot interval is the time after which a new note revision will be created for the note. See <doc>wiki</doc> for more info.",
"snapshot_time_interval_label": "Note revision snapshot time interval:"
},
"revisions_snapshot_limit": {
@@ -1372,7 +1391,7 @@
},
"custom_date_time_format": {
"title": "Custom Date/Time Format",
"description": "Customize the format of the date and time inserted via <kbd></kbd> or the toolbar. See <a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">Day.js docs</a> for available format tokens.",
"description": "Customize the format of the date and time inserted via <shortcut /> or the toolbar. See <doc>Day.js docs</doc> for available format tokens.",
"format_string": "Format string:",
"formatted_time": "Formatted date/time:"
},
@@ -1590,8 +1609,8 @@
"open-in-popup": "Quick edit"
},
"shared_info": {
"shared_publicly": "This note is shared publicly on",
"shared_locally": "This note is shared locally on",
"shared_publicly": "This note is shared publicly on {{- link}}.",
"shared_locally": "This note is shared locally on {{- link}}.",
"help_link": "For help visit <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a>."
},
"note_types": {
@@ -1670,7 +1689,8 @@
"hoist-this-note-workspace": "Hoist this note (workspace)",
"refresh-saved-search-results": "Refresh saved search results",
"create-child-note": "Create child note",
"unhoist": "Unhoist"
"unhoist": "Unhoist",
"toggle-sidebar": "Toggle sidebar"
},
"title_bar_buttons": {
"window-on-top": "Keep Window on Top"
@@ -1994,11 +2014,22 @@
"help_title": "Display more information about this screen"
},
"call_to_action": {
"next_theme_title": "The new Trilium theme is now stable",
"next_theme_message": "For a while now, we've been working on a new theme to give the application a more modern look.",
"next_theme_button": "Switch to the new Trilium theme",
"next_theme_title": "Try the new Trilium theme",
"next_theme_message": "You are currently using the legacy theme, would you like to try the new theme?",
"next_theme_button": "Try the new theme",
"background_effects_title": "Background effects are now stable",
"background_effects_message": "On Windows devices, background effects are now fully stable. The background effects adds a touch of color to the user interface by blurring the background behind it. This technique is also used in other applications such as Windows Explorer.",
"background_effects_button": "Enable background effects"
"background_effects_button": "Enable background effects",
"dismiss": "Dismiss"
},
"settings": {
"related_settings": "Related settings"
},
"settings_appearance": {
"related_code_blocks": "Color scheme for code blocks in text notes",
"related_code_notes": "Color scheme for code notes"
},
"units": {
"percentage": "%"
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
{}

View File

@@ -1,345 +1,353 @@
{
"about": {
"app_version": "Versione dell'app:",
"db_version": "Versione DB:",
"sync_version": "Versione Sync:",
"data_directory": "Cartella dati:",
"title": "Informazioni su Trilium Notes",
"build_date": "Data della build:",
"build_revision": "Revisione della build:",
"homepage": "Homepage:"
"about": {
"app_version": "Versione dell'app:",
"db_version": "Versione DB:",
"sync_version": "Versione Sync:",
"data_directory": "Cartella dati:",
"title": "Informazioni su Trilium Notes",
"build_date": "Data della build:",
"build_revision": "Revisione della build:",
"homepage": "Homepage:"
},
"toast": {
"critical-error": {
"title": "Errore critico",
"message": "Si è verificato un errore critico che impedisce l'avvio dell'applicazione client:\n\n{{message}}\n\nQuesto è probabilmente causato da un errore di script inaspettato. Prova a avviare l'applicazione in modo sicuro e controlla il problema."
},
"toast": {
"critical-error": {
"title": "Errore critico",
"message": "Si è verificato un errore critico che impedisce l'avvio dell'applicazione client:\n\n{{message}}\n\nQuesto è probabilmente causato da un errore di script inaspettato. Prova a avviare l'applicazione in modo sicuro e controlla il problema."
},
"bundle-error": {
"title": "Non si è riusciti a caricare uno script personalizzato",
"message": "Lo script della nota con ID \"{{id}}\", dal titolo \"{{title}}\" non è stato inizializzato a causa di:\n\n{{message}}"
},
"widget-error": {
"title": "Impossibile inizializzare un widget",
"message-custom": "Il widget personalizzato della nota con ID \"{{id}}\", dal titolo \"{{title}}\" non è stato inizializzato a causa di:\n\n{{message}}",
"message-unknown": "Un widget sconosciuto non è stato inizializzato a causa di:\n\n{{message}}"
}
"bundle-error": {
"title": "Non si è riusciti a caricare uno script personalizzato",
"message": "Lo script della nota con ID \"{{id}}\", dal titolo \"{{title}}\" non è stato inizializzato a causa di:\n\n{{message}}"
},
"add_link": {
"add_link": "Aggiungi un collegamento",
"note": "Nota",
"search_note": "cerca una nota per nome",
"link_title_mirrors": "il titolo del collegamento rispecchia il titolo della nota corrente",
"link_title_arbitrary": "il titolo del collegamento può essere modificato arbitrariamente",
"link_title": "Titolo del collegamento",
"button_add_link": "Aggiungi il collegamento <kbd>invio</kbd>",
"help_on_links": "Aiuto sui collegamenti"
},
"branch_prefix": {
"edit_branch_prefix": "Modifica il prefisso del ramo",
"help_on_tree_prefix": "Aiuto sui prefissi dell'Albero",
"prefix": "Prefisso: ",
"save": "Salva",
"branch_prefix_saved": "Il prefisso del ramo è stato salvato."
},
"bulk_actions": {
"bulk_actions": "Azioni massive",
"affected_notes": "Note influenzate",
"include_descendants": "Includi i discendenti della nota selezionata",
"available_actions": "Azioni disponibili",
"chosen_actions": "Azioni scelte",
"execute_bulk_actions": "Esegui le azioni massive",
"bulk_actions_executed": "Le azioni massive sono state eseguite con successo.",
"none_yet": "Ancora nessuna... aggiungi una azione cliccando su una di quelle disponibili sopra.",
"labels": "Etichette",
"relations": "Relazioni",
"notes": "Note",
"other": "Altro"
},
"clone_to": {
"clone_notes_to": "Clona note in...",
"help_on_links": "Aiuto sui collegamenti",
"notes_to_clone": "Note da clonare",
"target_parent_note": "Nodo padre obiettivo",
"search_for_note_by_its_name": "cerca una nota per nome",
"cloned_note_prefix_title": "Le note clonate saranno mostrate nell'albero delle note con il dato prefisso",
"prefix_optional": "Prefisso (opzionale)",
"clone_to_selected_note": "Clona sotto la nota selezionata <kbd>invio</kbd>",
"no_path_to_clone_to": "Nessun percorso per clonare dentro.",
"note_cloned": "La nota \"{{clonedTitle}}\" è stata clonata in \"{{targetTitle}}\""
},
"confirm": {
"cancel": "Annulla",
"ok": "OK",
"confirmation": "Conferma",
"are_you_sure_remove_note": "Sei sicuro di voler rimuovere la nota \"{{title}}\" dalla mappa delle relazioni? ",
"if_you_dont_check": "Se non lo selezioni, la nota sarà rimossa solamente dalla mappa delle relazioni.",
"also_delete_note": "Rimuove anche la nota"
},
"delete_notes": {
"ok": "OK",
"close": "Chiudi",
"delete_notes_preview": "Anteprima di eliminazione delle note",
"delete_all_clones_description": "Elimina anche tutti i cloni (può essere disfatto tramite i cambiamenti recenti)",
"erase_notes_description": "L'eliminazione normale (soft) marca le note come eliminate e potranno essere recuperate entro un certo lasso di tempo (dalla finestra dei cambiamenti recenti). Selezionando questa opzione le note si elimineranno immediatamente e non sarà possibile recuperarle.",
"erase_notes_warning": "Elimina le note in modo permanente (non potrà essere disfatto), compresi tutti i cloni. Ciò forzerà un nuovo caricamento dell'applicazione.",
"cancel": "Annulla",
"notes_to_be_deleted": "Le seguenti note saranno eliminate ({{- noteCount}})",
"no_note_to_delete": "Nessuna nota sarà eliminata (solo i cloni).",
"broken_relations_to_be_deleted": "Le seguenti relazioni saranno interrotte ed eliminate ({{- relationCount}})",
"deleted_relation_text": "La nota {{- note}} (da eliminare) è referenziata dalla relazione {{- relation}} originata da {{- source}}."
},
"info": {
"okButton": "OK",
"closeButton": "Chiudi"
},
"export": {
"close": "Chiudi",
"export_note_title": "Esporta la nota",
"export_status": "Stato dell'esportazione",
"export": "Esporta",
"choose_export_type": "Scegli prima il tipo di esportazione, per favore",
"export_in_progress": "Esportazione in corso: {{progressCount}}",
"export_finished_successfully": "Esportazione terminata con successo.",
"format_pdf": "PDF- allo scopo di stampa o esportazione.",
"export_type_subtree": "Questa nota e tutti i suoi discendenti",
"format_html": "HTML - raccomandato in quanto mantiene tutti i formati",
"format_html_zip": "HTML in archivio ZIP - questo è raccomandato in quanto conserva tutta la formattazione.",
"format_markdown": "MArkdown - questo conserva la maggior parte della formattazione."
},
"password_not_set": {
"body1": "Le note protette sono crittografate utilizzando una password utente, ma la password non è stata ancora impostata.",
"body2": "Per proteggere le note, fare clic su <a class=\"open-password-options-button\" href=\"javascript:\">qui</a> per aprire la finestra di dialogo Opzioni e impostare la password."
},
"protected_session_password": {
"close_label": "Chiudi"
},
"abstract_bulk_action": {
"remove_this_search_action": "Rimuovi questa azione di ricerca"
},
"etapi": {
"new_token_title": "Nuovo token ETAPI",
"new_token_message": "Inserire il nuovo nome del token"
},
"electron_integration": {
"zoom-factor": "Fattore di ingrandimento",
"desktop-application": "Applicazione Desktop"
},
"note_autocomplete": {
"search-for": "Cerca \"{{term}}\"",
"create-note": "Crea e collega la nota figlia \"{{term}}\"",
"insert-external-link": "Inserisci il collegamento esterno a \"{{term}}\"",
"clear-text-field": "Pulisci il campo di testo",
"show-recent-notes": "Mostra le note recenti",
"full-text-search": "Ricerca full text"
},
"note_tooltip": {
"note-has-been-deleted": "La nota è stata eliminata.",
"quick-edit": "Modifica veloce"
},
"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."
},
"geo-map-context": {
"open-location": "Apri la posizione",
"remove-from-map": "Rimuovi dalla mappa",
"add-note": "Aggiungi un marcatore in questa posizione"
},
"debug": {
"debug": "Debug"
},
"database_anonymization": {
"light_anonymization": "Anonimizzazione parziale",
"title": "Anonimizzazione del Database",
"full_anonymization": "Anonimizzazione completa",
"full_anonymization_description": "Questa azione creerà una nuova copia del database e lo anonimizzerà (rimuove tutti i contenuti delle note, lasciando solo la struttura e qualche metadato non sensibile) per condividerlo online allo scopo di debugging, senza paura di far trapelare i tuoi dati personali.",
"save_fully_anonymized_database": "Salva il database completamente anonimizzato",
"light_anonymization_description": "Questa azione creerà una nuova copia del database e lo anonimizzerà in parzialmente — in particolare, solo il contenuto delle note sarà rimosso, ma i titoli e gli attributi rimarranno. Inoltre, note con script personalizzati JS di frontend/backend e widget personalizzati lasciando rimarranno. Ciò mette a disposizione più contesto per il debug dei problemi.",
"choose_anonymization": "Puoi decidere da solo se fornire un database completamente o parzialmente anonimizzato. Anche un database completamente anonimizzato è molto utile, sebbene in alcuni casi i database parzialmente anonimizzati possono accelerare il processo di identificazione dei bug e la loro correzione.",
"no_anonymized_database_yet": "Nessun database ancora anonimizzato.",
"save_lightly_anonymized_database": "Salva il database parzialmente anonimizzato",
"successfully_created_fully_anonymized_database": "Database completamente anonimizzato creato in {{anonymizedFilePath}}",
"successfully_created_lightly_anonymized_database": "Database parzialmente anonimizzato creato in {{anonymizedFilePath}}"
},
"cpu_arch_warning": {
"title": "Per favore scarica la versione ARM64",
"continue_anyway": "Continua Comunque",
"dont_show_again": "Non mostrare più questo avviso",
"download_link": "Scarica la Versione Nativa"
},
"editorfeatures": {
"title": "Caratteristiche",
"emoji_completion_enabled": "Abilita il completamento automatico delle Emoji",
"note_completion_enabled": "Abilita il completamento automatico delle note"
},
"table_view": {
"new-row": "Nuova riga",
"new-column": "Nuova colonna",
"sort-column-by": "Ordina per \"{{title}}\"",
"sort-column-ascending": "Ascendente",
"sort-column-descending": "Discendente",
"sort-column-clear": "Cancella l'ordinamento",
"hide-column": "Nascondi la colonna \"{{title}}\"",
"show-hide-columns": "Mostra/nascondi le colonne",
"row-insert-above": "Inserisci una riga sopra",
"row-insert-below": "Inserisci una riga sotto"
},
"abstract_search_option": {
"remove_this_search_option": "Rimuovi questa opzione di ricerca",
"failed_rendering": "Opzione di ricerca di rendering non riuscita: {{dto}} con errore: {{error}} {{stack}}"
},
"ancestor": {
"label": "Antenato"
},
"add_label": {
"add_label": "Aggiungi etichetta",
"label_name_placeholder": "nome dell'etichetta",
"new_value_placeholder": "nuovo valore",
"to_value": "al valore"
},
"update_label_value": {
"to_value": "al valore",
"label_name_placeholder": "nome dell'etichetta"
},
"delete_label": {
"delete_label": "Elimina etichetta",
"label_name_placeholder": "nome dell'etichetta",
"label_name_title": "Sono ammessi i caratteri alfanumerici, il carattere di sottolineato e i due punti."
},
"tree-context-menu": {
"move-to": "Muovi in...",
"cut": "Taglia"
},
"electron_context_menu": {
"cut": "Taglia",
"copy": "Copia",
"paste": "Incolla",
"copy-link": "Copia collegamento",
"paste-as-plain-text": "Incolla come testo semplice"
},
"editing": {
"editor_type": {
"multiline-toolbar": "Mostra la barra degli strumenti su più linee se non entra."
}
},
"edit_button": {
"edit_this_note": "Modifica questa nota"
},
"shortcuts": {
"shortcuts": "Scorciatoie"
},
"shared_switch": {
"toggle-on-title": "Condividi la nota",
"toggle-off-title": "Non condividere la nota"
},
"search_string": {
"search_prefix": "Cerca:"
},
"attachment_detail": {
"open_help_page": "Apri la pagina di aiuto sugli allegati"
},
"search_definition": {
"ancestor": "antenato",
"debug": "debug",
"action": "azione",
"add_search_option": "Aggiungi un opzione di ricerca:",
"search_string": "cerca la stringa",
"limit": "limite"
},
"modal": {
"close": "Chiudi"
},
"board_view": {
"insert-below": "Inserisci sotto",
"delete-column": "Elimina la colonna",
"delete-column-confirmation": "Sei sicuro di vole eliminare questa colonna? Il corrispondente attributo sarà eliminato anche nelle note sotto questa colonna."
},
"backup": {
"enable_weekly_backup": "Abilita le archiviazioni settimanali",
"enable_monthly_backup": "Abilita le archiviazioni mensili",
"backup_recommendation": "Si raccomanda di mantenere attive le archiviazioni, sebbene ciò possa rendere l'avvio dell'applicazione lento con database grandi e/o dispositivi di archiviazione lenti.",
"backup_now": "Archivia adesso",
"backup_database_now": "Archivia il database adesso",
"existing_backups": "Backup esistenti",
"date-and-time": "Data e ora",
"path": "Percorso",
"database_backed_up_to": "Il database è stato archiviato in {{backupFilePath}}",
"enable_daily_backup": "Abilita le archiviazioni giornaliere",
"no_backup_yet": "Ancora nessuna archiviazione"
},
"backend_log": {
"refresh": "Aggiorna"
},
"consistency_checks": {
"find_and_fix_button": "Trova e correggi i problemi di coerenza",
"finding_and_fixing_message": "In cerca e correzione dei problemi di coerenza...",
"issues_fixed_message": "Qualsiasi problema di coerenza che possa essere stato trovato ora è corretto."
},
"database_integrity_check": {
"check_button": "Controllo dell'integrità del database",
"checking_integrity": "Controllo dell'integrità del database in corso...",
"title": "Controllo di Integrità del database",
"description": "Controllerà che il database non sia corrotto a livello SQLite. Può durare un po' di tempo, a seconda della grandezza del DB.",
"integrity_check_failed": "Controllo di integrità fallito: {{results}}"
},
"sync": {
"title": "Sincronizza",
"force_full_sync_button": "Forza una sincronizzazione completa",
"failed": "Sincronizzazione fallita: {{message}}"
},
"sync_2": {
"config_title": "Configurazione per la Sincronizzazione",
"proxy_label": "Server Proxy per la sincronizzazione (opzionale)",
"test_title": "Test di sincronizzazione",
"timeout": "Timeout per la sincronizzazione",
"timeout_unit": "millisecondi",
"save": "Salva",
"help": "Aiuto"
},
"search_engine": {
"save_button": "Salva"
},
"sql_table_schemas": {
"tables": "Tabelle"
},
"tab_row": {
"close_tab": "Chiudi la scheda",
"add_new_tab": "Aggiungi una nuova scheda",
"close": "Chiudi",
"close_other_tabs": "Chiudi le altre schede",
"close_right_tabs": "Chiudi le schede a destra",
"close_all_tabs": "Chiudi tutte le schede",
"reopen_last_tab": "Riapri l'ultima scheda chiusa",
"move_tab_to_new_window": "Sposta questa scheda in una nuova finestra",
"copy_tab_to_new_window": "Copia questa scheda in una nuova finestra",
"new_tab": "Nuova scheda"
},
"toc": {
"table_of_contents": "Sommario"
},
"table_of_contents": {
"title": "Sommario"
},
"tray": {
"title": "Vassoio di Sistema",
"enable_tray": "Abilita il vassoio (Trilium necessita di essere riavviato affinché la modifica abbia effetto)"
},
"heading_style": {
"title": "Stile dell'Intestazione",
"plain": "Normale",
"underline": "Sottolineato",
"markdown": "Stile Markdown"
},
"highlights_list": {
"title": "Punti salienti"
},
"highlights_list_2": {
"title": "Punti salienti",
"options": "Opzioni"
},
"quick-search": {
"placeholder": "Ricerca rapida",
"searching": "Ricerca in corso..."
"widget-error": {
"title": "Impossibile inizializzare un widget",
"message-custom": "Il widget personalizzato della nota con ID \"{{id}}\", dal titolo \"{{title}}\" non è stato inizializzato a causa di:\n\n{{message}}",
"message-unknown": "Un widget sconosciuto non è stato inizializzato a causa di:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Aggiungi un collegamento",
"note": "Nota",
"search_note": "cerca una nota per nome",
"link_title_mirrors": "il titolo del collegamento rispecchia il titolo della nota corrente",
"link_title_arbitrary": "il titolo del collegamento può essere modificato arbitrariamente",
"link_title": "Titolo del collegamento",
"button_add_link": "Aggiungi il collegamento <kbd>invio</kbd>",
"help_on_links": "Aiuto sui collegamenti"
},
"branch_prefix": {
"edit_branch_prefix": "Modifica il prefisso del ramo",
"help_on_tree_prefix": "Aiuto sui prefissi dell'Albero",
"prefix": "Prefisso: ",
"save": "Salva",
"branch_prefix_saved": "Il prefisso del ramo è stato salvato."
},
"bulk_actions": {
"bulk_actions": "Azioni massive",
"affected_notes": "Note influenzate",
"include_descendants": "Includi i discendenti della nota selezionata",
"available_actions": "Azioni disponibili",
"chosen_actions": "Azioni scelte",
"execute_bulk_actions": "Esegui le azioni massive",
"bulk_actions_executed": "Le azioni massive sono state eseguite con successo.",
"none_yet": "Ancora nessuna... aggiungi una azione cliccando su una di quelle disponibili sopra.",
"labels": "Etichette",
"relations": "Relazioni",
"notes": "Note",
"other": "Altro"
},
"clone_to": {
"clone_notes_to": "Clona note in...",
"help_on_links": "Aiuto sui collegamenti",
"notes_to_clone": "Note da clonare",
"target_parent_note": "Nodo padre obiettivo",
"search_for_note_by_its_name": "cerca una nota per nome",
"cloned_note_prefix_title": "Le note clonate saranno mostrate nell'albero delle note con il dato prefisso",
"prefix_optional": "Prefisso (opzionale)",
"clone_to_selected_note": "Clona sotto la nota selezionata <kbd>invio</kbd>",
"no_path_to_clone_to": "Nessun percorso per clonare dentro.",
"note_cloned": "La nota \"{{clonedTitle}}\" è stata clonata in \"{{targetTitle}}\""
},
"confirm": {
"cancel": "Annulla",
"ok": "OK",
"confirmation": "Conferma",
"are_you_sure_remove_note": "Sei sicuro di voler rimuovere la nota \"{{title}}\" dalla mappa delle relazioni? ",
"if_you_dont_check": "Se non lo selezioni, la nota sarà rimossa solamente dalla mappa delle relazioni.",
"also_delete_note": "Rimuove anche la nota"
},
"delete_notes": {
"ok": "OK",
"close": "Chiudi",
"delete_notes_preview": "Anteprima di eliminazione delle note",
"delete_all_clones_description": "Elimina anche tutti i cloni (può essere disfatto tramite i cambiamenti recenti)",
"erase_notes_description": "L'eliminazione normale (soft) marca le note come eliminate e potranno essere recuperate entro un certo lasso di tempo (dalla finestra dei cambiamenti recenti). Selezionando questa opzione le note si elimineranno immediatamente e non sarà possibile recuperarle.",
"erase_notes_warning": "Elimina le note in modo permanente (non potrà essere disfatto), compresi tutti i cloni. Ciò forzerà un nuovo caricamento dell'applicazione.",
"cancel": "Annulla",
"notes_to_be_deleted": "Le seguenti note saranno eliminate ({{- noteCount}})",
"no_note_to_delete": "Nessuna nota sarà eliminata (solo i cloni).",
"broken_relations_to_be_deleted": "Le seguenti relazioni saranno interrotte ed eliminate ({{- relationCount}})",
"deleted_relation_text": "La nota {{- note}} (da eliminare) è referenziata dalla relazione {{- relation}} originata da {{- source}}."
},
"info": {
"okButton": "OK",
"closeButton": "Chiudi"
},
"export": {
"close": "Chiudi",
"export_note_title": "Esporta la nota",
"export_status": "Stato dell'esportazione",
"export": "Esporta",
"choose_export_type": "Scegli prima il tipo di esportazione, per favore",
"export_in_progress": "Esportazione in corso: {{progressCount}}",
"export_finished_successfully": "Esportazione terminata con successo.",
"format_pdf": "PDF- allo scopo di stampa o esportazione.",
"export_type_subtree": "Questa nota e tutti i suoi discendenti",
"format_html": "HTML - raccomandato in quanto mantiene tutti i formati",
"format_html_zip": "HTML in archivio ZIP - questo è raccomandato in quanto conserva tutta la formattazione.",
"format_markdown": "MArkdown - questo conserva la maggior parte della formattazione.",
"export_type_single": "Solo questa nota, senza le sottostanti"
},
"password_not_set": {
"body1": "Le note protette sono crittografate utilizzando una password utente, ma la password non è stata ancora impostata.",
"body2": "Per proteggere le note, fare clic su <a class=\"open-password-options-button\" href=\"javascript:\">qui</a> per aprire la finestra di dialogo Opzioni e impostare la password."
},
"protected_session_password": {
"close_label": "Chiudi"
},
"abstract_bulk_action": {
"remove_this_search_action": "Rimuovi questa azione di ricerca"
},
"etapi": {
"new_token_title": "Nuovo token ETAPI",
"new_token_message": "Inserire il nuovo nome del token"
},
"electron_integration": {
"zoom-factor": "Fattore di ingrandimento",
"desktop-application": "Applicazione Desktop"
},
"note_autocomplete": {
"search-for": "Cerca \"{{term}}\"",
"create-note": "Crea e collega la nota figlia \"{{term}}\"",
"insert-external-link": "Inserisci il collegamento esterno a \"{{term}}\"",
"clear-text-field": "Pulisci il campo di testo",
"show-recent-notes": "Mostra le note recenti",
"full-text-search": "Ricerca full text"
},
"note_tooltip": {
"note-has-been-deleted": "La nota è stata eliminata.",
"quick-edit": "Modifica veloce"
},
"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."
},
"geo-map-context": {
"open-location": "Apri la posizione",
"remove-from-map": "Rimuovi dalla mappa",
"add-note": "Aggiungi un marcatore in questa posizione"
},
"debug": {
"debug": "Debug"
},
"database_anonymization": {
"light_anonymization": "Anonimizzazione parziale",
"title": "Anonimizzazione del Database",
"full_anonymization": "Anonimizzazione completa",
"full_anonymization_description": "Questa azione creerà una nuova copia del database e lo anonimizzerà (rimuove tutti i contenuti delle note, lasciando solo la struttura e qualche metadato non sensibile) per condividerlo online allo scopo di debugging, senza paura di far trapelare i tuoi dati personali.",
"save_fully_anonymized_database": "Salva il database completamente anonimizzato",
"light_anonymization_description": "Questa azione creerà una nuova copia del database e lo anonimizzerà in parzialmente — in particolare, solo il contenuto delle note sarà rimosso, ma i titoli e gli attributi rimarranno. Inoltre, note con script personalizzati JS di frontend/backend e widget personalizzati lasciando rimarranno. Ciò mette a disposizione più contesto per il debug dei problemi.",
"choose_anonymization": "Puoi decidere da solo se fornire un database completamente o parzialmente anonimizzato. Anche un database completamente anonimizzato è molto utile, sebbene in alcuni casi i database parzialmente anonimizzati possono accelerare il processo di identificazione dei bug e la loro correzione.",
"no_anonymized_database_yet": "Nessun database ancora anonimizzato.",
"save_lightly_anonymized_database": "Salva il database parzialmente anonimizzato",
"successfully_created_fully_anonymized_database": "Database completamente anonimizzato creato in {{anonymizedFilePath}}",
"successfully_created_lightly_anonymized_database": "Database parzialmente anonimizzato creato in {{anonymizedFilePath}}"
},
"cpu_arch_warning": {
"title": "Per favore scarica la versione ARM64",
"continue_anyway": "Continua Comunque",
"dont_show_again": "Non mostrare più questo avviso",
"download_link": "Scarica la Versione Nativa"
},
"editorfeatures": {
"title": "Caratteristiche",
"emoji_completion_enabled": "Abilita il completamento automatico delle Emoji",
"note_completion_enabled": "Abilita il completamento automatico delle note"
},
"table_view": {
"new-row": "Nuova riga",
"new-column": "Nuova colonna",
"sort-column-by": "Ordina per \"{{title}}\"",
"sort-column-ascending": "Ascendente",
"sort-column-descending": "Discendente",
"sort-column-clear": "Cancella l'ordinamento",
"hide-column": "Nascondi la colonna \"{{title}}\"",
"show-hide-columns": "Mostra/nascondi le colonne",
"row-insert-above": "Inserisci una riga sopra",
"row-insert-below": "Inserisci una riga sotto"
},
"abstract_search_option": {
"remove_this_search_option": "Rimuovi questa opzione di ricerca",
"failed_rendering": "Opzione di ricerca di rendering non riuscita: {{dto}} con errore: {{error}} {{stack}}"
},
"ancestor": {
"label": "Antenato"
},
"add_label": {
"add_label": "Aggiungi etichetta",
"label_name_placeholder": "nome dell'etichetta",
"new_value_placeholder": "nuovo valore",
"to_value": "al valore"
},
"update_label_value": {
"to_value": "al valore",
"label_name_placeholder": "nome dell'etichetta"
},
"delete_label": {
"delete_label": "Elimina etichetta",
"label_name_placeholder": "nome dell'etichetta",
"label_name_title": "Sono ammessi i caratteri alfanumerici, il carattere di sottolineato e i due punti."
},
"tree-context-menu": {
"move-to": "Muovi in...",
"cut": "Taglia"
},
"electron_context_menu": {
"cut": "Taglia",
"copy": "Copia",
"paste": "Incolla",
"copy-link": "Copia collegamento",
"paste-as-plain-text": "Incolla come testo semplice"
},
"editing": {
"editor_type": {
"multiline-toolbar": "Mostra la barra degli strumenti su più linee se non entra."
}
},
"edit_button": {
"edit_this_note": "Modifica questa nota"
},
"shortcuts": {
"shortcuts": "Scorciatoie"
},
"shared_switch": {
"toggle-on-title": "Condividi la nota",
"toggle-off-title": "Non condividere la nota"
},
"search_string": {
"search_prefix": "Cerca:"
},
"attachment_detail": {
"open_help_page": "Apri la pagina di aiuto sugli allegati"
},
"search_definition": {
"ancestor": "antenato",
"debug": "debug",
"action": "azione",
"add_search_option": "Aggiungi un opzione di ricerca:",
"search_string": "cerca la stringa",
"limit": "limite"
},
"modal": {
"close": "Chiudi"
},
"board_view": {
"insert-below": "Inserisci sotto",
"delete-column": "Elimina la colonna",
"delete-column-confirmation": "Sei sicuro di vole eliminare questa colonna? Il corrispondente attributo sarà eliminato anche nelle note sotto questa colonna."
},
"backup": {
"enable_weekly_backup": "Abilita le archiviazioni settimanali",
"enable_monthly_backup": "Abilita le archiviazioni mensili",
"backup_recommendation": "Si raccomanda di mantenere attive le archiviazioni, sebbene ciò possa rendere l'avvio dell'applicazione lento con database grandi e/o dispositivi di archiviazione lenti.",
"backup_now": "Archivia adesso",
"backup_database_now": "Archivia il database adesso",
"existing_backups": "Backup esistenti",
"date-and-time": "Data e ora",
"path": "Percorso",
"database_backed_up_to": "Il database è stato archiviato in {{backupFilePath}}",
"enable_daily_backup": "Abilita le archiviazioni giornaliere",
"no_backup_yet": "Ancora nessuna archiviazione"
},
"backend_log": {
"refresh": "Aggiorna"
},
"consistency_checks": {
"find_and_fix_button": "Trova e correggi i problemi di coerenza",
"finding_and_fixing_message": "In cerca e correzione dei problemi di coerenza...",
"issues_fixed_message": "Qualsiasi problema di coerenza che possa essere stato trovato ora è corretto."
},
"database_integrity_check": {
"check_button": "Controllo dell'integrità del database",
"checking_integrity": "Controllo dell'integrità del database in corso...",
"title": "Controllo di Integrità del database",
"description": "Controllerà che il database non sia corrotto a livello SQLite. Può durare un po' di tempo, a seconda della grandezza del DB.",
"integrity_check_failed": "Controllo di integrità fallito: {{results}}"
},
"sync": {
"title": "Sincronizza",
"force_full_sync_button": "Forza una sincronizzazione completa",
"failed": "Sincronizzazione fallita: {{message}}"
},
"sync_2": {
"config_title": "Configurazione per la Sincronizzazione",
"proxy_label": "Server Proxy per la sincronizzazione (opzionale)",
"test_title": "Test di sincronizzazione",
"timeout": "Timeout per la sincronizzazione",
"timeout_unit": "millisecondi",
"save": "Salva",
"help": "Aiuto"
},
"search_engine": {
"save_button": "Salva"
},
"sql_table_schemas": {
"tables": "Tabelle"
},
"tab_row": {
"close_tab": "Chiudi la scheda",
"add_new_tab": "Aggiungi una nuova scheda",
"close": "Chiudi",
"close_other_tabs": "Chiudi le altre schede",
"close_right_tabs": "Chiudi le schede a destra",
"close_all_tabs": "Chiudi tutte le schede",
"reopen_last_tab": "Riapri l'ultima scheda chiusa",
"move_tab_to_new_window": "Sposta questa scheda in una nuova finestra",
"copy_tab_to_new_window": "Copia questa scheda in una nuova finestra",
"new_tab": "Nuova scheda"
},
"toc": {
"table_of_contents": "Sommario"
},
"table_of_contents": {
"title": "Sommario"
},
"tray": {
"title": "Vassoio di Sistema",
"enable_tray": "Abilita il vassoio (Trilium necessita di essere riavviato affinché la modifica abbia effetto)"
},
"heading_style": {
"title": "Stile dell'Intestazione",
"plain": "Normale",
"underline": "Sottolineato",
"markdown": "Stile Markdown"
},
"highlights_list": {
"title": "Punti salienti"
},
"highlights_list_2": {
"title": "Punti salienti",
"options": "Opzioni"
},
"quick-search": {
"placeholder": "Ricerca rapida",
"searching": "Ricerca in corso..."
},
"help": {
"goUpDown": "su/giù nella lista delle note",
"collapseExpand": "collassa/espande il nodo",
"notSet": "non impostato",
"goBackForwards": "indietro/avanti nella cronologia",
"showJumpToNoteDialog": "mostra <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">la finestra di dialogo \"Salta alla nota\"<a>"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
{
"about": {
"title": "Trilium Notes에 대해서",
"homepage": "홈페이지:",
"app_version": "앱 버전:",
"db_version": "DB 버전:",
"sync_version": "동기화 버전:",
"build_date": "빌드 날짜:",
"build_revision": "빌드 리비전:",
"data_directory": "데이터 경로:"
},
"toast": {
"critical-error": {
"title": "심각한 오류",
"message": "클라이언트 애플리케이션 시작 도중 심각한 오류가 발생했습니다:\n\n{{message}}\n\n이는 스크립트가 예기치 않게 실패하면서 발생한 것일 수 있습니다. 애플리케이션을 안전 모드로 시작한 뒤 문제를 해결해 보세요."
},
"widget-error": {
"title": "위젯 초기화 실패"
}
},
"add_link": {
"add_link": "링크 추가",
"note": "노트",
"search_note": "이름으로 노트 검색하기"
},
"branch_prefix": {
"save": "저장"
}
}

View File

@@ -0,0 +1,9 @@
{
"about": {
"title": "Over Trilium Notes",
"homepage": "Homepagina:",
"app_version": "App versie:",
"db_version": "DB Versie:",
"sync_version": "Sync Versie:"
}
}

View File

@@ -0,0 +1,143 @@
{
"about": {
"title": "O notatkach Trilium",
"homepage": "Strona główna:",
"app_version": "Wersja aplikacji:",
"db_version": "Wersja bazy danych:",
"sync_version": "Wersja synchronizacji:",
"build_date": "Zbudowano:",
"build_revision": "Rewizja zbudowania:",
"data_directory": "Katalog z danymi:"
},
"toast": {
"critical-error": {
"title": "Błąd krytyczny",
"message": "Wystąpił krytyczny błąd uniemożliwiający uruchomienie aplikacji:\n\n{{message}}\n\nJest to spowodowane najprawdopodobniej niespodziewanym błędem skryptu. Spróbuj uruchomić aplikację ponownie w trybie bezpiecznym i zaadresuj problem."
},
"widget-error": {
"title": "Nie udało się zainicjować widżetu",
"message-custom": "Niestandardowy widżet z notatki o identyfikatorze \"{{id}}\", i tytule \"{{title}}\" nie mógł zostać zainicjowany z powodu:\n\n{{message}}",
"message-unknown": "Nieznany widżet nie mógł być zainicjowany z powodu:\n\n{{message}}"
},
"bundle-error": {
"title": "Nie udało się załadować niestandardowego skryptu",
"message": "Skrypt z notatki o identyfikatorze \"{{id}}\", tytule \"{{title}}: nie został uruchomiony z powodu:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Dodaj link",
"note": "Notatka",
"search_note": "Wyszukaj notatkę po nazwie",
"link_title_arbitrary": "Tytuł linku można dowolnie zmieniać",
"link_title": "Tytuł linku",
"button_add_link": "Dodaj link",
"help_on_links": "Pomoc dotycząca linków",
"link_title_mirrors": "tytuł linku odzwierciedla tytuł obecnej notatki"
},
"branch_prefix": {
"save": "Zapisz",
"edit_branch_prefix": "Edytuj prefiks gałęzi",
"prefix": "Prefiks: ",
"branch_prefix_saved": "Zapisano prefiks gałęzi.",
"help_on_tree_prefix": "Pomoc dotycząca prefiksu drzewa"
},
"bulk_actions": {
"labels": "Etykiety",
"notes": "Notatki",
"other": "Inne",
"relations": "Powiązania",
"bulk_actions": "Działania zbiorcze",
"include_descendants": "Uwzględnia rozwinięcia wybranych notatek",
"available_actions": "Dostępne działania",
"chosen_actions": "Wybrane działania",
"execute_bulk_actions": "Wykonaj zbiór działań",
"bulk_actions_executed": "Zbiór działań został wykonany prawidłowo.",
"none_yet": "Brak zaznaczonych działań... dodaj działanie poprzez kliknięcie jednej z dostępnych opcji powyżej."
},
"confirm": {
"ok": "OK",
"cancel": "Anuluj",
"confirmation": "Potwierdzenie",
"are_you_sure_remove_note": "Czy napewno chcesz usunąć notatkę \"{{title}}\" z mapy powiązań? ",
"if_you_dont_check": "Jeśli nie zaznaczysz tej opcji, notatka zostanie usunięta jedynie z mapy powiązań.",
"also_delete_note": "Usuń dodatkowo notatkę"
},
"delete_notes": {
"cancel": "Anuluj",
"close": "Zamknij",
"delete_notes_preview": "Usuń podgląd notatek",
"delete_all_clones_description": "Usuń również wszystkie sklonowania (działanie może zostać cofnięte w ostatnich zmianach)",
"erase_notes_description": "Normalne (miękkie) usuwanie zaznacza jedynie notatki jako usunięte i można je przywrócić (w oknie dialogowym ostatnich zmian) przez wyznaczony okres czasu. Zaznaczenie tej opcji spowoduje natychmiastowe usunięcie notatek, bez możliwości ich przywrócenia.",
"erase_notes_warning": "Usuń notatki permanentnie (bez opcji ich przywrócenia), włączając wszystkie kopie. Działanie to wymaga ponownego uruchomienia aplikacji.",
"notes_to_be_deleted": "Następujące notatki zostaną usunięte ({{notesCount}})",
"no_note_to_delete": "Żadne notatki nie zostaną usunięte (jedynie kopie).",
"broken_relations_to_be_deleted": "Następujące powiązania zostaną uszkodzone i usunięte ({{ relationCount}})",
"ok": "OK",
"deleted_relation_text": "Notatka {{- note}} (do usunięcia) jest powiązana przez relację {{- relation}} pochodzącą z {{- source}}."
},
"export": {
"close": "Zamknij",
"export_note_title": "Eksportuj notatkę",
"export_type_subtree": "Ta notatka oraz wszystkie podrzędne",
"format_html": "HTML - rekomendowany jako zachowujący całość formatowania",
"format_html_zip": "HTML w archiwum ZIP - rekomendowany jako zachowujący całość formatowania.",
"format_markdown": "Markdown - zachowuje większość formatowania.",
"format_opml": "OPML - format wymiany danych dla outlinerów zawierający tylko tekst. Formatowanie, obrazy i pliki nie są uwzględnione.",
"opml_version_1": "OPML v1.0 - tylko zwykły tekst",
"opml_version_2": "OPML v2.0 - umożliwia również HTML",
"export_type_single": "Tylko ta notatka, bez elementów podrzędnych",
"export": "Eksportuj",
"choose_export_type": "Wybierz najpierw rodzaj pliku do eksportu",
"export_status": "Status eksportu",
"export_in_progress": "Postęp eksportowania: {{progressCount}}",
"export_finished_successfully": "Eksportowanie zakończone.",
"format_pdf": "PDF - w celu drukowania lub udostępniania."
},
"clone_to": {
"clone_notes_to": "Sklonuj notatki do...",
"notes_to_clone": "Notatki do sklonowania",
"search_for_note_by_its_name": "Wyszukaj notatkę po jej nazwie",
"cloned_note_prefix_title": "Sklonowana notatka zostanie wyświetlona w drzewie notatki z podanym prefiksem",
"prefix_optional": "Prefiks (opcjonalne)",
"clone_to_selected_note": "Sklonuj do wybranej notatki",
"no_path_to_clone_to": "Brak ścieżki do sklonowania.",
"note_cloned": "Notatka \"{{clonedTitle}}\" została sklonowana do \"{{targetTitle}}\"",
"help_on_links": "Pomoc dotycząca linków"
},
"help": {
"title": "Ściągawka",
"noteNavigation": "Nawigacja po notatkach",
"goUpDown": "przewijanie w górę/w dół w liście notatek",
"collapseExpand": "zwiń/rozwiń zbiór",
"notSet": "niezdefiniowany",
"goBackForwards": "przewijaj do tyłu/do przodu w historii",
"showJumpToNoteDialog": "pokaż <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"przejdź do dialogu</a>",
"scrollToActiveNote": "przewiń do aktywnej notatki",
"jumpToParentNote": "przejdź do głównej notatki",
"collapseWholeTree": "zwiń całe drzewko notatki",
"collapseSubTree": "zwiń gałąź notatki",
"tabShortcuts": "Skóry kart",
"newTabNoteLink": "link notatki otwiera notatkę w nowej karcie",
"newTabWithActivationNoteLink": "link notatki otwiera i aktywuje notatkę w nowej karcie",
"onlyInDesktop": "Tylko na komputerze stacjonarnym (wersja Electron)",
"openEmptyTab": "Otwórz pustą kartę",
"closeActiveTab": "zamknij aktywną kartę",
"activateNextTab": "aktywuj następną kartę",
"activatePreviousTab": "aktywuj poprzednią kartę",
"creatingNotes": "Tworzenie notatek",
"createNoteAfter": "Utwórz nową notatkę obok obecnie aktywnej",
"createNoteInto": "Utwórz nową podnotatkę w obecnie otwartej",
"editBranchPrefix": "edytuj <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/tree-concepts.html#prefix\">prefiks</a> aktywnej kopii notatki",
"movingCloningNotes": "Przenoszenie / kopiowanie notatek",
"moveNoteUpDown": "Przenieś notatkę w górę/w dół na liście notatek",
"moveNoteUpHierarchy": "Przenieś notatkę w górę w hierarchii",
"multiSelectNote": "Zaznacz wiele notatek powyżej/poniżej",
"selectAllNotes": "Wybierz wszystkie notatki na obecnym poziomie",
"selectNote": "Wybierz notatkę",
"copyNotes": "skopiuj obecną notatkę (lub obecną sekcję) do schowka (zastosowanie dla<a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">klonowania</a>)",
"cutNotes": "przytnij obecną notatkę (lub obecną sekcję) do schowka (zastosowanie dla przenoszenia notatek)",
"pasteNotes": "wklej notatkę jako podnotatka w obecnej notatce (rozumiane jako przenieś lub skopiuj, w zależności czy notatka była skopiowana czy wycięta)",
"deleteNotes": "usuń notatkę / gałąź",
"editingNotes": "Edytowanie notatek"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -113,7 +113,7 @@ declare namespace Fancytree {
generateFormElements(selected?: boolean, active?: boolean): void;
/** Return the currently active node or null. */
getActiveNode(): FancytreeNode;
getActiveNode(): FancytreeNode | null;
/** Return the first top level node if any (not the invisible root node). */
getFirstChild(): FancytreeNode;

View File

@@ -3,7 +3,11 @@ type DateTimeStyle = "full" | "long" | "medium" | "short" | "none" | undefined;
/**
* Formats the given date and time to a string based on the current locale.
*/
export function formatDateTime(date: string | Date | number, dateStyle: DateTimeStyle = "medium", timeStyle: DateTimeStyle = "medium") {
export function formatDateTime(date: string | Date | number | null | undefined, dateStyle: DateTimeStyle = "medium", timeStyle: DateTimeStyle = "medium") {
if (!date) {
return "";
}
const locale = navigator.language;
let parsedDate;

View File

@@ -0,0 +1,163 @@
/* #region Generic floating buttons styles */
.floating-buttons {
position: relative;
}
.floating-buttons-children,
.show-floating-buttons {
position: absolute;
top: 10px;
right: 10px;
display: flex;
flex-direction: row;
z-index: 100;
}
.note-split.rtl .floating-buttons-children,
.note-split.rtl .show-floating-buttons {
right: unset;
left: 10px;
}
.note-split.rtl .close-floating-buttons {
order: -1;
}
.note-split.rtl .close-floating-buttons,
.note-split.rtl .show-floating-buttons {
transform: rotate(180deg);
}
.type-canvas .floating-buttons-children {
top: 70px;
}
.type-canvas .floating-buttons-children > * {
--border-radius: 0; /* Overridden by themes */
}
.floating-buttons-children > *:not(.hidden-int):not(.no-content-hidden) {
margin: 2px;
}
.floating-buttons-children > *:not(.has-overflow) {
overflow: hidden;
}
.floating-buttons-children > button, .floating-buttons-children .floating-button {
font-size: 150%;
padding: 5px 10px 4px 10px;
width: 40px;
cursor: pointer;
color: var(--button-text-color);
background: var(--button-background-color);
border-radius: var(--button-border-radius);
border: 1px solid transparent;
display: flex;
justify-content: space-around;
}
.floating-buttons-children > button:hover, .floating-buttons-children .floating-button:hover {
text-decoration: none;
border-color: var(--button-border-color);
}
.floating-buttons .floating-buttons-children.temporarily-hidden {
display: none;
}
/* #endregion */
/* #region Show floating button */
.floating-buttons-children.temporarily-hidden+.show-floating-buttons {
display: block;
}
.show-floating-buttons {
/* display: none;*/
margin-left: 5px !important;
}
.show-floating-buttons-button {
border: 1px solid transparent;
color: var(--button-text-color);
padding: 6px;
border-radius: 100px !important;
}
.show-floating-buttons-button:hover {
border: 1px solid var(--button-border-color);
}
/* #endregion */
/* #region Geo map buttons */
.leaflet-pane {
z-index: 50;
}
/* #endregion */
/* #region Close floating buttons */
.close-floating-buttons {
margin-left: 5px !important;
}
.close-floating-buttons:first-child {
display: none !important;
}
.close-floating-buttons-button {
border: 1px solid transparent;
color: var(--button-text-color);
padding: 6px;
border-radius: 100px;
}
.close-floating-buttons-button:hover {
border: 1px solid var(--button-border-color);
}
/* #endregion */
/* #region Backlinks */
.backlinks-widget {
position: relative;
}
.backlinks-ticker {
border-radius: 10px;
border-color: var(--main-border-color);
background-color: var(--more-accented-background-color);
padding: 4px 10px 4px 10px;
opacity: 90%;
display: flex;
justify-content: space-between;
align-items: center;
}
.backlinks-count {
cursor: pointer;
}
.backlinks-items {
z-index: 10;
position: absolute;
top: 50px;
right: 10px;
width: 400px;
border-radius: 10px;
background-color: var(--accented-background-color);
color: var(--main-text-color);
padding: 20px;
overflow-y: auto;
}
.backlink-excerpt {
border-left: 2px solid var(--main-border-color);
padding-left: 10px;
opacity: 80%;
font-size: 90%;
}
.backlink-excerpt .backlink-link { /* the actual backlink */
font-weight: bold;
background-color: yellow;
}
/* #endregion */

View File

@@ -0,0 +1,94 @@
import { t } from "i18next";
import "./FloatingButtons.css";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean } from "./react/hooks";
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
import { ParentComponent } from "./react/react_utils";
import { EventData, EventNames } from "../components/app_context";
import { type FloatingButtonsList, type FloatingButtonContext } from "./FloatingButtonsDefinitions";
import ActionButton from "./react/ActionButton";
import { ViewTypeOptions } from "../services/note_list_renderer";
interface FloatingButtonsProps {
items: FloatingButtonsList;
}
/*
* Note:
*
* For floating button widgets that require content to overflow, the has-overflow CSS class should
* be applied to the root element of the widget. Additionally, this root element may need to
* properly handle rounded corners, as defined by the --border-radius CSS variable.
*/
export default function FloatingButtons({ items }: FloatingButtonsProps) {
const { note, noteContext } = useNoteContext();
const parentComponent = useContext(ParentComponent);
const [ viewType ] = useNoteLabel(note, "viewType");
const [ isReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const context = useMemo<FloatingButtonContext | null>(() => {
if (!note || !noteContext || !parentComponent) return null;
return {
note,
noteContext,
parentComponent,
isDefaultViewMode: noteContext.viewScope?.viewMode === "default",
viewType: viewType as ViewTypeOptions,
isReadOnly,
triggerEvent<T extends EventNames>(name: T, data?: Omit<EventData<T>, "ntxId">) {
parentComponent.triggerEvent(name, {
ntxId: noteContext.ntxId,
...data
} as EventData<T>);
}
};
}, [ note, noteContext, parentComponent, viewType, isReadOnly ]);
// Manage the user-adjustable visibility of the floating buttons.
const [ visible, setVisible ] = useState(true);
useEffect(() => setVisible(true), [ note ]);
return (
<div className="floating-buttons no-print">
<div className={`floating-buttons-children ${!visible ? "temporarily-hidden" : ""}`}>
{context && items.map((Component) => (
<Component {...context} />
))}
{visible && <CloseFloatingButton setVisible={setVisible} />}
</div>
{!visible && <ShowFloatingButton setVisible={setVisible} /> }
</div>
)
}
/**
* Show button that displays floating button after click on close button
*/
function ShowFloatingButton({ setVisible }: { setVisible(visible: boolean): void }) {
return (
<div className="show-floating-buttons">
<ActionButton
className="show-floating-buttons-button"
icon="bx bx-chevrons-left"
text={t("show_floating_buttons_button.button_title")}
onClick={() => setVisible(true)}
noIconActionClass
/>
</div>
);
}
function CloseFloatingButton({ setVisible }: { setVisible(visible: boolean): void }) {
return (
<div className="close-floating-buttons">
<ActionButton
className="close-floating-buttons-button"
icon="bx bx-chevrons-right"
text={t("hide_floating_buttons_button.button_title")}
onClick={() => setVisible(false)}
noIconActionClass
/>
</div>
);
}

View File

@@ -0,0 +1,398 @@
import { VNode } from "preact";
import appContext, { EventData, EventNames } from "../components/app_context";
import Component from "../components/component";
import NoteContext from "../components/note_context";
import FNote from "../entities/fnote";
import ActionButton, { ActionButtonProps } from "./react/ActionButton";
import { useNoteLabelBoolean, useTriliumEvent, useTriliumOption, useWindowSize } from "./react/hooks";
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils";
import server from "../services/server";
import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons";
import toast from "../services/toast";
import { t } from "../services/i18n";
import { copyImageReferenceToClipboard } from "../services/image";
import tree from "../services/tree";
import protected_session_holder from "../services/protected_session_holder";
import options from "../services/options";
import { getHelpUrlForNote } from "../services/in_app_help";
import froca from "../services/froca";
import NoteLink from "./react/NoteLink";
import RawHtml from "./react/RawHtml";
import { ViewTypeOptions } from "../services/note_list_renderer";
export interface FloatingButtonContext {
parentComponent: Component;
note: FNote;
noteContext: NoteContext;
isDefaultViewMode: boolean;
isReadOnly: boolean;
/** Shorthand for triggering an event from the parent component. The `ntxId` is automatically handled for convenience. */
triggerEvent<T extends EventNames>(name: T, data?: Omit<EventData<T>, "ntxId">): void;
viewType?: ViewTypeOptions | null;
}
function FloatingButton({ className, ...props }: ActionButtonProps) {
return <ActionButton
className={`floating-button ${className ?? ""}`}
noIconActionClass
{...props}
/>
}
export type FloatingButtonsList = ((context: FloatingButtonContext) => false | VNode)[];
export const DESKTOP_FLOATING_BUTTONS: FloatingButtonsList = [
RefreshBackendLogButton,
SwitchSplitOrientationButton,
ToggleReadOnlyButton,
EditButton,
ShowTocWidgetButton,
ShowHighlightsListWidgetButton,
RunActiveNoteButton,
OpenTriliumApiDocsButton,
SaveToNoteButton,
RelationMapButtons,
GeoMapButtons,
CopyImageReferenceButton,
ExportImageButtons,
InAppHelpButton,
Backlinks
];
export const MOBILE_FLOATING_BUTTONS: FloatingButtonsList = [
RefreshBackendLogButton,
EditButton,
RelationMapButtons,
ExportImageButtons,
Backlinks
]
function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefaultViewMode }: FloatingButtonContext) {
const isEnabled = note.noteId === "_backendLog" && isDefaultViewMode;
return isEnabled && <FloatingButton
text={t("backend_log.refresh")}
icon="bx bx-refresh"
onClick={() => parentComponent.triggerEvent("refreshData", { ntxId: noteContext.ntxId })}
/>
}
function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: FloatingButtonContext) {
const isEnabled = note.type === "mermaid" && note.isContentAvailable() && !isReadOnly && isDefaultViewMode;
const [ splitEditorOrientation, setSplitEditorOrientation ] = useTriliumOption("splitEditorOrientation");
const upcomingOrientation = splitEditorOrientation === "horizontal" ? "vertical" : "horizontal";
return isEnabled && <FloatingButton
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)}
/>
}
function ToggleReadOnlyButton({ note, viewType, isDefaultViewMode }: FloatingButtonContext) {
const [ isReadOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const isEnabled = (note.type === "mermaid" || viewType === "geoMap")
&& note.isContentAvailable() && isDefaultViewMode;
return isEnabled && <FloatingButton
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)}
/>
}
function EditButton({ note, noteContext, isDefaultViewMode }: FloatingButtonContext) {
const [ animationClass, setAnimationClass ] = useState("");
const [ isEnabled, setIsEnabled ] = useState(false);
useEffect(() => {
noteContext.isReadOnly().then(isReadOnly => {
setIsEnabled(
isDefaultViewMode
&& (!note.isProtected || protected_session_holder.isProtectedSessionAvailable())
&& !options.is("databaseReadonly")
&& isReadOnly
);
});
}, [ note ]);
useTriliumEvent("readOnlyTemporarilyDisabled", ({ noteContext: eventNoteContext }) => {
if (noteContext?.ntxId === eventNoteContext.ntxId) {
setIsEnabled(false);
}
});
// make the edit button stand out on the first display, otherwise
// it's difficult to notice that the note is readonly
useEffect(() => {
if (isEnabled) {
setAnimationClass("bx-tada bx-lg");
setTimeout(() => {
setAnimationClass("");
}, 1700);
}
}, [ isEnabled ]);
return isEnabled && <FloatingButton
text={t("edit_button.edit_this_note")}
icon="bx bx-pencil"
className={animationClass}
onClick={() => {
if (noteContext.viewScope) {
noteContext.viewScope.readOnlyTemporarilyDisabled = true;
appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext });
}
}}
/>
}
function ShowTocWidgetButton({ note, noteContext, isDefaultViewMode }: FloatingButtonContext) {
const [ isEnabled, setIsEnabled ] = useState(false);
useTriliumEvent("reEvaluateTocWidgetVisibility", () => {
setIsEnabled(note.type === "text" && isDefaultViewMode && !!noteContext.viewScope?.tocTemporarilyHidden);
});
return isEnabled && <FloatingButton
text={t("show_toc_widget_button.show_toc")}
icon="bx bx-tn-toc"
onClick={() => {
if (noteContext?.viewScope && noteContext.noteId) {
noteContext.viewScope.tocTemporarilyHidden = false;
appContext.triggerEvent("showTocWidget", { noteId: noteContext.noteId });
}
}}
/>
}
function ShowHighlightsListWidgetButton({ note, noteContext, isDefaultViewMode }: FloatingButtonContext) {
const [ isEnabled, setIsEnabled ] = useState(false);
useTriliumEvent("reEvaluateHighlightsListWidgetVisibility", () => {
setIsEnabled(note.type === "text" && isDefaultViewMode && !!noteContext.viewScope?.highlightsListTemporarilyHidden);
});
return isEnabled && <FloatingButton
text={t("show_highlights_list_widget_button.show_highlights_list")}
icon="bx bx-bookmarks"
onClick={() => {
if (noteContext?.viewScope && noteContext.noteId) {
noteContext.viewScope.highlightsListTemporarilyHidden = false;
appContext.triggerEvent("showHighlightsListWidget", { noteId: noteContext.noteId });
}
}}
/>
}
function RunActiveNoteButton({ note }: FloatingButtonContext) {
const isEnabled = note.mime.startsWith("application/javascript") || note.mime === "text/x-sqlite;schema=trilium";
return isEnabled && <FloatingButton
icon="bx bx-play"
text={t("code_buttons.execute_button_title")}
triggerCommand="runActiveNote"
/>
}
function OpenTriliumApiDocsButton({ note }: FloatingButtonContext) {
const isEnabled = note.mime.startsWith("application/javascript;env=");
return isEnabled && <FloatingButton
icon="bx bx-help-circle"
text={t("code_buttons.trilium_api_docs_button_title")}
onClick={() => openInAppHelpFromUrl(note.mime.endsWith("frontend") ? "Q2z6av6JZVWm" : "MEtfsqa5VwNi")}
/>
}
function SaveToNoteButton({ note }: FloatingButtonContext) {
const isEnabled = note.mime === "text/x-sqlite;schema=trilium" && note.isHiddenCompletely();
return isEnabled && <FloatingButton
icon="bx bx-save"
text={t("code_buttons.save_to_note_button_title")}
onClick={async (e) => {
e.preventDefault();
const { notePath } = await server.post<SaveSqlConsoleResponse>("special-notes/save-sql-console", { sqlConsoleNoteId: note.noteId });
if (notePath) {
toast.showMessage(t("code_buttons.sql_console_saved_message", { "note_path": await tree.getNotePathTitle(notePath) }));
// TODO: This hangs the navigation, for some reason.
//await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext()?.setNote(notePath);
}
}}
/>
}
function RelationMapButtons({ note, triggerEvent }: FloatingButtonContext) {
const isEnabled = (note.type === "relationMap");
return isEnabled && (
<>
<FloatingButton
icon="bx bx-folder-plus"
text={t("relation_map_buttons.create_child_note_title")}
onClick={() => triggerEvent("relationMapCreateChildNote")}
/>
<FloatingButton
icon="bx bx-crop"
text={t("relation_map_buttons.reset_pan_zoom_title")}
onClick={() => triggerEvent("relationMapResetPanZoom")}
/>
<div className="btn-group">
<FloatingButton
icon="bx bx-zoom-in"
text={t("relation_map_buttons.zoom_in_title")}
onClick={() => triggerEvent("relationMapResetZoomIn")}
/>
<FloatingButton
icon="bx bx-zoom-out"
text={t("relation_map_buttons.zoom_out_title")}
onClick={() => triggerEvent("relationMapResetZoomOut")}
/>
</div>
</>
)
}
function GeoMapButtons({ triggerEvent, viewType, isReadOnly }: FloatingButtonContext) {
const isEnabled = viewType === "geoMap" && !isReadOnly;
return isEnabled && (
<FloatingButton
icon="bx bx-plus-circle"
text={t("geo-map.create-child-note-title")}
onClick={() => triggerEvent("geoMapCreateChildNote")}
/>
);
}
function CopyImageReferenceButton({ note, isDefaultViewMode }: FloatingButtonContext) {
const hiddenImageCopyRef = useRef<HTMLDivElement>(null);
const isEnabled = ["mermaid", "canvas", "mindMap"].includes(note?.type ?? "")
&& note?.isContentAvailable() && isDefaultViewMode;
return isEnabled && (
<>
<FloatingButton
icon="bx bx-copy"
text={t("copy_image_reference_button.button_title")}
onClick={() => {
if (!hiddenImageCopyRef.current) return;
const imageEl = document.createElement("img");
imageEl.src = createImageSrcUrl(note);
hiddenImageCopyRef.current.replaceChildren(imageEl);
copyImageReferenceToClipboard($(hiddenImageCopyRef.current));
hiddenImageCopyRef.current.removeChild(imageEl);
}}
/>
<div ref={hiddenImageCopyRef} className="hidden-image-copy" style={{
position: "absolute" // Take out of the the hidden image from flexbox to prevent the layout being affected
}} />
</>
)
}
function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingButtonContext) {
const isEnabled = ["mermaid", "mindMap"].includes(note?.type ?? "")
&& note?.isContentAvailable() && isDefaultViewMode;
return isEnabled && (
<>
<FloatingButton
icon="bx bxs-file-image"
text={t("svg_export_button.button_title")}
onClick={() => triggerEvent("exportSvg")}
/>
<FloatingButton
icon="bx bxs-file-png"
text={t("png_export_button.button_title")}
onClick={() => triggerEvent("exportPng")}
/>
</>
)
}
function InAppHelpButton({ note }: FloatingButtonContext) {
const helpUrl = getHelpUrlForNote(note);
return !!helpUrl && (
<FloatingButton
icon="bx bx-help-circle"
text={t("help-button.title")}
onClick={() => helpUrl && openInAppHelpFromUrl(helpUrl)}
/>
)
}
function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
let [ backlinkCount, setBacklinkCount ] = useState(0);
let [ popupOpen, setPopupOpen ] = useState(false);
const backlinksContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!isDefaultViewMode) return;
server.get<BacklinkCountResponse>(`note-map/${note.noteId}/backlink-count`).then(resp => {
setBacklinkCount(resp.count);
});
}, [ note ]);
// Determine the max height of the container.
const { windowHeight } = useWindowSize();
useLayoutEffect(() => {
const el = backlinksContainerRef.current;
if (popupOpen && el) {
const box = el.getBoundingClientRect();
const maxHeight = windowHeight - box.top - 10;
el.style.maxHeight = `${maxHeight}px`;
}
}, [ popupOpen, windowHeight ]);
const isEnabled = isDefaultViewMode && backlinkCount > 0;
return (isEnabled &&
<div className="backlinks-widget has-overflow">
<div
className="backlinks-ticker"
onClick={() => setPopupOpen(!popupOpen)}
>
<span className="backlinks-count">{t("zpetne_odkazy.backlink", { count: backlinkCount })}</span>
</div>
{popupOpen && (
<div ref={backlinksContainerRef} className="backlinks-items dropdown-menu" style={{ display: "block" }}>
<BacklinksList noteId={note.noteId} />
</div>
)}
</div>
);
}
function BacklinksList({ noteId }: { noteId: string }) {
const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]);
useEffect(() => {
server.get<BacklinksResponse>(`note-map/${noteId}/backlinks`).then(async (backlinks) => {
// prefetch all
const noteIds = backlinks
.filter(bl => "noteId" in bl)
.map((bl) => bl.noteId);
await froca.getNotes(noteIds);
setBacklinks(backlinks);
});
}, [ noteId ]);
return backlinks.map(backlink => (
<div>
<NoteLink
notePath={backlink.noteId}
showNotePath showNoteIcon
noPreview
/>
{"relationName" in backlink ? (
<p>{backlink.relationName}</p>
) : (
backlink.excerpts.map(excerpt => (
<RawHtml html={excerpt} />
))
)}
</div>
));
}

View File

@@ -0,0 +1,28 @@
.api-log-widget {
flex-grow: 1;
max-height: 40%;
position: relative;
border-top: 1px solid var(--main-border-color);
background-color: var(--accented-background-color);
}
.api-log-container {
overflow: auto;
height: 100%;
font-family: var(--monospace-font-family);
font-size: 0.8em;
white-space: pre;
padding: 15px;
}
.close-api-log-button {
padding: 5px;
border: 1px solid var(--button-border-color);
background-color: var(--button-background-color);
border-radius: var(--button-border-radius);
color: var(--button-text-color);
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
}

View File

@@ -1,80 +0,0 @@
import type { EventData } from "../components/app_context.js";
import type FNote from "../entities/fnote.js";
import { t } from "../services/i18n.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
const TPL = /*html*/`
<div class="api-log-widget">
<style>
.api-log-widget {
padding: 15px;
flex-grow: 1;
max-height: 40%;
position: relative;
}
.hidden-api-log {
display: none;
}
.api-log-container {
overflow: auto;
height: 100%;
}
.close-api-log-button {
padding: 5px;
border: 1px solid var(--button-border-color);
background-color: var(--button-background-color);
border-radius: var(--button-border-radius);
color: var(--button-text-color);
position: absolute;
top: 10px;
right: 40px;
cursor: pointer;
}
</style>
<div class="bx bx-x close-api-log-button" title="${t("api_log.close")}"></div>
<div class="api-log-container"></div>
</div>`;
export default class ApiLogWidget extends NoteContextAwareWidget {
private $logContainer!: JQuery<HTMLElement>;
private $closeButton!: JQuery<HTMLElement>;
isEnabled() {
return !!this.note && this.note.mime.startsWith("application/javascript;env=") && super.isEnabled();
}
doRender() {
this.$widget = $(TPL);
this.toggle(false);
this.$logContainer = this.$widget.find(".api-log-container");
this.$closeButton = this.$widget.find(".close-api-log-button");
this.$closeButton.on("click", () => this.toggle(false));
}
async refreshWithNote(note: FNote) {
this.$logContainer.empty();
}
apiLogMessagesEvent({ messages, noteId }: EventData<"apiLogMessages">) {
if (!this.isNote(noteId)) {
return;
}
this.toggle(true);
for (const message of messages) {
this.$logContainer.append(message).append($("<br>"));
}
}
toggle(show: boolean) {
this.$widget.toggleClass("hidden-api-log", !show);
}
}

View File

@@ -0,0 +1,41 @@
import { useEffect, useState } from "preact/hooks";
import "./api_log.css";
import { useNoteContext, useTriliumEvent } from "./react/hooks";
import ActionButton from "./react/ActionButton";
import { t } from "../services/i18n";
/**
* Displays the messages that are logged by the current note via `api.log`, for frontend and backend scripts.
*/
export default function ApiLog() {
const { note, noteId } = useNoteContext();
const [ messages, setMessages ] = useState<string[]>();
useTriliumEvent("apiLogMessages", ({ messages, noteId: eventNoteId }) => {
if (eventNoteId !== noteId) return;
setMessages(messages);
});
// Clear when navigating away.
useEffect(() => setMessages(undefined), [ note ]);
const isEnabled = note?.mime.startsWith("application/javascript;env=") && messages?.length;
return (
<div className={`api-log-widget ${!isEnabled ? "hidden-ext" : ""}`}>
{isEnabled && (
<>
<ActionButton
icon="bx bx-x"
className="close-api-log-button"
text={t("api_log.close")}
onClick={() => setMessages(undefined)}
/>
<div className="api-log-container">
{messages.join("\n")}
</div>
</>
)}
</div>
)
}

View File

@@ -1,504 +0,0 @@
import { t } from "../../services/i18n.js";
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import noteAutocompleteService, { type Suggestion } from "../../services/note_autocomplete.js";
import server from "../../services/server.js";
import contextMenuService from "../../menus/context_menu.js";
import attributeParser, { type Attribute } from "../../services/attribute_parser.js";
import { AttributeEditor, type EditorConfig, type ModelElement, type MentionFeed, type ModelNode, type ModelPosition } from "@triliumnext/ckeditor5";
import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js";
import attributeService from "../../services/attributes.js";
import linkService from "../../services/link.js";
import type AttributeDetailWidget from "./attribute_detail.js";
import type { CommandData, EventData, EventListener, FilteredCommandNames } from "../../components/app_context.js";
import type { default as FAttribute, AttributeType } from "../../entities/fattribute.js";
import type FNote from "../../entities/fnote.js";
import { escapeQuotes } from "../../services/utils.js";
const HELP_TEXT = `
<p>${t("attribute_editor.help_text_body1")}</p>
<p>${t("attribute_editor.help_text_body2")}</p>
<p>${t("attribute_editor.help_text_body3")}</p>`;
const TPL = /*html*/`
<div style="position: relative; padding-top: 10px; padding-bottom: 10px">
<style>
.attribute-list-editor {
border: 0 !important;
outline: 0 !important;
box-shadow: none !important;
padding: 0 0 0 5px !important;
margin: 0 !important;
max-height: 100px;
overflow: auto;
transition: opacity .1s linear;
}
.attribute-list-editor.ck-content .mention {
color: var(--muted-text-color) !important;
background: transparent !important;
}
.save-attributes-button {
color: var(--muted-text-color);
position: absolute;
bottom: 14px;
right: 25px;
cursor: pointer;
border: 1px solid transparent;
font-size: 130%;
}
.add-new-attribute-button {
color: var(--muted-text-color);
position: absolute;
bottom: 13px;
right: 0;
cursor: pointer;
border: 1px solid transparent;
font-size: 130%;
}
.add-new-attribute-button:hover, .save-attributes-button:hover {
border: 1px solid var(--button-border-color);
border-radius: var(--button-border-radius);
background: var(--button-background-color);
color: var(--button-text-color);
}
.attribute-errors {
color: red;
padding: 5px 50px 0px 5px; /* large right padding to avoid buttons */
}
</style>
<div class="attribute-list-editor" tabindex="200"></div>
<div class="bx bx-save save-attributes-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.save_attributes"))}"></div>
<div class="bx bx-plus add-new-attribute-button tn-tool-button" title="${escapeQuotes(t("attribute_editor.add_a_new_attribute"))}"></div>
<div class="attribute-errors" style="display: none;"></div>
</div>
`;
const mentionSetup: MentionFeed[] = [
{
marker: "@",
feed: (queryText) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
itemRenderer: (_item) => {
const item = _item as Suggestion;
const itemElement = document.createElement("button");
itemElement.innerHTML = `${item.highlightedNotePathTitle} `;
return itemElement;
},
minimumCharacters: 0
},
{
marker: "#",
feed: async (queryText) => {
const names = await server.get<string[]>(`attribute-names/?type=label&query=${encodeURIComponent(queryText)}`);
return names.map((name) => {
return {
id: `#${name}`,
name: name
};
});
},
minimumCharacters: 0
},
{
marker: "~",
feed: async (queryText) => {
const names = await server.get<string[]>(`attribute-names/?type=relation&query=${encodeURIComponent(queryText)}`);
return names.map((name) => {
return {
id: `~${name}`,
name: name
};
});
},
minimumCharacters: 0
}
];
const editorConfig: EditorConfig = {
toolbar: {
items: []
},
placeholder: t("attribute_editor.placeholder"),
mention: {
feeds: mentionSetup
},
licenseKey: "GPL"
};
type AttributeCommandNames = FilteredCommandNames<CommandData>;
export default class AttributeEditorWidget extends NoteContextAwareWidget implements EventListener<"entitiesReloaded">, EventListener<"addNewLabel">, EventListener<"addNewRelation"> {
private attributeDetailWidget: AttributeDetailWidget;
private $editor!: JQuery<HTMLElement>;
private $addNewAttributeButton!: JQuery<HTMLElement>;
private $saveAttributesButton!: JQuery<HTMLElement>;
private $errors!: JQuery<HTMLElement>;
private textEditor!: AttributeEditor;
private lastUpdatedNoteId!: string | undefined;
private lastSavedContent!: string;
constructor(attributeDetailWidget: AttributeDetailWidget) {
super();
this.attributeDetailWidget = attributeDetailWidget;
}
doRender() {
this.$widget = $(TPL);
this.$editor = this.$widget.find(".attribute-list-editor");
this.initialized = this.initEditor();
this.$editor.on("keydown", async (e) => {
if (e.which === 13) {
// allow autocomplete to fill the result textarea
setTimeout(() => this.save(), 100);
}
this.attributeDetailWidget.hide();
});
this.$editor.on("blur", () => setTimeout(() => this.save(), 100)); // Timeout to fix https://github.com/zadam/trilium/issues/4160
this.$addNewAttributeButton = this.$widget.find(".add-new-attribute-button");
this.$addNewAttributeButton.on("click", (e) => this.addNewAttribute(e));
this.$saveAttributesButton = this.$widget.find(".save-attributes-button");
this.$saveAttributesButton.on("click", () => this.save());
this.$errors = this.$widget.find(".attribute-errors");
}
addNewAttribute(e: JQuery.ClickEvent) {
contextMenuService.show<AttributeCommandNames>({
x: e.pageX,
y: e.pageY,
orientation: "left",
items: [
{ title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" },
{ title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" },
{ title: "----" },
{ title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" },
{ title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" }
],
selectMenuItemHandler: ({ command }) => this.handleAddNewAttributeCommand(command)
});
// Prevent automatic hiding of the context menu due to the button being clicked.
e.stopPropagation();
}
// triggered from keyboard shortcut
async addNewLabelEvent({ ntxId }: EventData<"addNewLabel">) {
if (this.isNoteContext(ntxId)) {
await this.refresh();
this.handleAddNewAttributeCommand("addNewLabel");
}
}
// triggered from keyboard shortcut
async addNewRelationEvent({ ntxId }: EventData<"addNewRelation">) {
if (this.isNoteContext(ntxId)) {
await this.refresh();
this.handleAddNewAttributeCommand("addNewRelation");
}
}
async handleAddNewAttributeCommand(command: AttributeCommandNames | undefined) {
// TODO: Not sure what the relation between FAttribute[] and Attribute[] is.
const attrs = this.parseAttributes() as FAttribute[];
if (!attrs) {
return;
}
let type: AttributeType;
let name;
let value;
if (command === "addNewLabel") {
type = "label";
name = "myLabel";
value = "";
} else if (command === "addNewRelation") {
type = "relation";
name = "myRelation";
value = "";
} else if (command === "addNewLabelDefinition") {
type = "label";
name = "label:myLabel";
value = "promoted,single,text";
} else if (command === "addNewRelationDefinition") {
type = "label";
name = "relation:myRelation";
value = "promoted,single";
} else {
return;
}
// TODO: Incomplete type
//@ts-ignore
attrs.push({
type,
name,
value,
isInheritable: false
});
await this.renderOwnedAttributes(attrs, false);
this.$editor.scrollTop(this.$editor[0].scrollHeight);
const rect = this.$editor[0].getBoundingClientRect();
setTimeout(() => {
// showing a little bit later because there's a conflict with outside click closing the attr detail
this.attributeDetailWidget.showAttributeDetail({
allAttributes: attrs,
attribute: attrs[attrs.length - 1],
isOwned: true,
x: (rect.left + rect.right) / 2,
y: rect.bottom,
focus: "name"
});
}, 100);
}
async save() {
if (this.lastUpdatedNoteId !== this.noteId) {
// https://github.com/zadam/trilium/issues/3090
console.warn("Ignoring blur event because a different note is loaded.");
return;
}
const attributes = this.parseAttributes();
if (attributes) {
await server.put(`notes/${this.noteId}/attributes`, attributes, this.componentId);
this.$saveAttributesButton.fadeOut();
// blink the attribute text to give a visual hint that save has been executed
this.$editor.css("opacity", 0);
// revert back
setTimeout(() => this.$editor.css("opacity", 1), 100);
}
}
parseAttributes() {
try {
return attributeParser.lexAndParse(this.getPreprocessedData());
} catch (e: any) {
this.$errors.text(e.message).slideDown();
}
}
getPreprocessedData() {
const str = this.textEditor
.getData()
.replace(/<a[^>]+href="(#[A-Za-z0-9_/]*)"[^>]*>[^<]*<\/a>/g, "$1")
.replace(/&nbsp;/g, " "); // otherwise .text() below outputs non-breaking space in unicode
return $("<div>").html(str).text();
}
async initEditor() {
this.$widget.show();
this.$editor.on("click", (e) => this.handleEditorClick(e));
this.textEditor = await AttributeEditor.create(this.$editor[0], editorConfig);
this.textEditor.model.document.on("change:data", () => this.dataChanged());
this.textEditor.editing.view.document.on(
"enter",
(event, data) => {
// disable entering new line - see https://github.com/ckeditor/ckeditor5/issues/9422
data.preventDefault();
event.stop();
},
{ priority: "high" }
);
// disable spellcheck for attribute editor
const documentRoot = this.textEditor.editing.view.document.getRoot();
if (documentRoot) {
this.textEditor.editing.view.change((writer) => writer.setAttribute("spellcheck", "false", documentRoot));
}
}
dataChanged() {
this.lastUpdatedNoteId = this.noteId;
if (this.lastSavedContent === this.textEditor.getData()) {
this.$saveAttributesButton.fadeOut();
} else {
this.$saveAttributesButton.fadeIn();
}
if (this.$errors.is(":visible")) {
// using .hide() instead of .slideUp() since this will also hide the error after confirming
// mention for relation name which suits up. When using.slideUp() error will appear and the slideUp which is weird
this.$errors.hide();
}
}
async handleEditorClick(e: JQuery.ClickEvent) {
const pos = this.textEditor.model.document.selection.getFirstPosition();
if (pos && pos.textNode && pos.textNode.data) {
const clickIndex = this.getClickIndex(pos);
let parsedAttrs;
try {
parsedAttrs = attributeParser.lexAndParse(this.getPreprocessedData(), true);
} catch (e) {
// the input is incorrect because the user messed up with it and now needs to fix it manually
return null;
}
let matchedAttr: Attribute | null = null;
for (const attr of parsedAttrs) {
if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) {
matchedAttr = attr;
break;
}
}
setTimeout(() => {
if (matchedAttr) {
this.$editor.tooltip("hide");
this.attributeDetailWidget.showAttributeDetail({
allAttributes: parsedAttrs,
attribute: matchedAttr,
isOwned: true,
x: e.pageX,
y: e.pageY
});
} else {
this.showHelpTooltip();
}
}, 100);
} else {
this.showHelpTooltip();
}
}
showHelpTooltip() {
this.attributeDetailWidget.hide();
this.$editor.tooltip({
trigger: "focus",
html: true,
title: HELP_TEXT,
placement: "bottom",
offset: "0,30"
});
this.$editor.tooltip("show");
}
getClickIndex(pos: ModelPosition) {
let clickIndex = pos.offset - (pos.textNode?.startOffset ?? 0);
let curNode: ModelNode | Text | ModelElement | null = pos.textNode;
while (curNode?.previousSibling) {
curNode = curNode.previousSibling;
if ((curNode as ModelElement).name === "reference") {
clickIndex += (curNode.getAttribute("href") as string).length + 1;
} else if ("data" in curNode) {
clickIndex += (curNode.data as string).length;
}
}
return clickIndex;
}
async loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string) {
const { noteId } = linkService.parseNavigationStateFromUrl(href);
const note = noteId ? await froca.getNote(noteId, true) : null;
const title = note ? note.title : "[missing]";
$el.text(title);
}
async refreshWithNote(note: FNote) {
await this.renderOwnedAttributes(note.getOwnedAttributes(), true);
}
async renderOwnedAttributes(ownedAttributes: FAttribute[], saved: boolean) {
// attrs are not resorted if position changes after the initial load
ownedAttributes.sort((a, b) => a.position - b.position);
let htmlAttrs = (await attributeRenderer.renderAttributes(ownedAttributes, true)).html();
if (htmlAttrs.length > 0) {
htmlAttrs += "&nbsp;";
}
this.textEditor.setData(htmlAttrs);
if (saved) {
this.lastSavedContent = this.textEditor.getData();
this.$saveAttributesButton.fadeOut(0);
}
}
async createNoteForReferenceLink(title: string) {
let result;
if (this.notePath) {
result = await noteCreateService.createNoteWithTypePrompt(this.notePath, {
activate: false,
title: title
});
}
return result?.note?.getBestNotePathString();
}
async updateAttributeList(attributes: FAttribute[]) {
await this.renderOwnedAttributes(attributes, false);
}
focus() {
this.$editor.trigger("focus");
this.textEditor.model.change((writer) => {
const documentRoot = this.textEditor.editing.model.document.getRoot();
if (!documentRoot) {
return;
}
const positionAt = writer.createPositionAt(documentRoot, "end");
writer.setSelection(positionAt);
});
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getAttributeRows(this.componentId).find((attr) => attributeService.isAffecting(attr, this.note))) {
this.refresh();
}
}
}

View File

@@ -1,7 +1,11 @@
import { isValidElement, VNode } from "preact";
import Component, { TypedComponent } from "../components/component.js";
import froca from "../services/froca.js";
import { t } from "../services/i18n.js";
import toastService from "../services/toast.js";
import { renderReactWidget } from "./react/react_utils.jsx";
import { EventNames, EventData } from "../components/app_context.js";
import { Handler } from "leaflet";
export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedComponent<T> {
protected attrs: Record<string, string>;
@@ -22,11 +26,14 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
this.childPositionCounter = 10;
}
child(...components: T[]) {
if (!components) {
child(..._components: (T | VNode)[]) {
if (!_components) {
return this;
}
// Convert any React components to legacy wrapped components.
const components = wrapReactWidgets(_components);
super.child(...components);
for (const component of components) {
@@ -48,7 +55,7 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
* @param components the components to be added as children to this component provided the condition is truthy.
* @returns self for chaining.
*/
optChild(condition: boolean, ...components: T[]) {
optChild(condition: boolean, ...components: (T | VNode)[]) {
if (condition) {
return this.child(...components);
} else {
@@ -258,3 +265,30 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
* For information on using widgets, see the tutorial {@tutorial widget_basics}.
*/
export default class BasicWidget extends TypedBasicWidget<Component> {}
export function wrapReactWidgets<T extends TypedComponent<any>>(components: (T | VNode)[]) {
const wrappedResult: T[] = [];
for (const component of components) {
if (isValidElement(component)) {
wrappedResult.push(new ReactWrappedWidget(component) as unknown as T);
} else {
wrappedResult.push(component);
}
}
return wrappedResult;
}
export class ReactWrappedWidget extends BasicWidget {
private el: VNode;
constructor(el: VNode) {
super();
this.el = el;
}
doRender() {
this.$widget = renderReactWidget(this, this.el);
}
}

View File

@@ -1,54 +0,0 @@
import SwitchWidget from "./switch.js";
import server from "../services/server.js";
import toastService from "../services/toast.js";
import { t } from "../services/i18n.js";
import type FNote from "../entities/fnote.js";
import type { EventData } from "../components/app_context.js";
// TODO: Deduplicate
type Response = {
success: true;
} | {
success: false;
message: string;
}
export default class BookmarkSwitchWidget extends SwitchWidget {
isEnabled() {
return (
super.isEnabled() &&
// it's not possible to bookmark root because that would clone it under bookmarks and thus create a cycle
!["root", "_hidden"].includes(this.noteId ?? "")
);
}
doRender() {
super.doRender();
this.switchOnName = t("bookmark_switch.bookmark");
this.switchOnTooltip = t("bookmark_switch.bookmark_this_note");
this.switchOffName = t("bookmark_switch.bookmark");
this.switchOffTooltip = t("bookmark_switch.remove_bookmark");
}
async toggle(state: boolean | null | undefined) {
const resp = await server.put<Response>(`notes/${this.noteId}/toggle-in-parent/_lbBookmarks/${!!state}`);
if (!resp.success && "message" in resp) {
toastService.showError(resp.message);
}
}
async refreshWithNote(note: FNote) {
const isBookmarked = !!note.getParentBranches().find((b) => b.parentNoteId === "_lbBookmarks");
this.isToggled = isBookmarked;
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getBranchRows().find((b) => b.noteId === this.noteId)) {
this.refresh();
}
}
}

View File

@@ -1,6 +1,7 @@
import { ComponentChildren } from "preact";
import { memo } from "preact/compat";
import AbstractBulkAction from "./abstract_bulk_action";
import HelpRemoveButtons from "../react/HelpRemoveButtons";
interface BulkActionProps {
label: string | ComponentChildren;
@@ -24,19 +25,11 @@ const BulkAction = memo(({ label, children, helpText, bulkAction }: BulkActionPr
{children}
</div>
</td>
<td className="button-column">
{helpText && <div className="dropdown help-dropdown">
<span className="bx bx-help-circle icon-action" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div className="dropdown-menu dropdown-menu-right p-4">
{helpText}
</div>
</div>}
<span
className="bx bx-x icon-action action-conf-del"
onClick={() => bulkAction?.deleteAction()}
/>
</td>
<HelpRemoveButtons
help={helpText}
removeText="Delete"
onRemove={() => bulkAction?.deleteAction()}
/>
</tr>
);
});

View File

@@ -1,11 +1,11 @@
import { t } from "../../../services/i18n.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";
import { useEffect, useState } from "preact/hooks";
import { useSpacedUpdate } from "../../react/hooks.jsx";
function MoveNoteBulkActionComponent({ bulkAction, actionDef }: { bulkAction: AbstractBulkAction, actionDef: ActionDefinition }) {
function MoveNoteBulkActionComponent({ bulkAction }: { bulkAction: AbstractBulkAction }) {
const [ targetParentNoteId, setTargetParentNoteId ] = useState<string>();
const spacedUpdate = useSpacedUpdate(() => {
return bulkAction.saveAction({ targetParentNoteId: targetParentNoteId })
@@ -45,6 +45,6 @@ export default class MoveNoteBulkAction extends AbstractBulkAction {
}
doRender() {
return <MoveNoteBulkActionComponent bulkAction={this} actionDef={this.actionDef} />
return <MoveNoteBulkActionComponent bulkAction={this} />
}
}

View File

@@ -1,4 +1,3 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import { t } from "../../../services/i18n.js";
import BulkAction from "../BulkAction.jsx";

View File

@@ -1,6 +1,4 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction, { ActionDefinition } from "../abstract_bulk_action.js";
import noteAutocompleteService from "../../../services/note_autocomplete.js";
import { t } from "../../../services/i18n.js";
import BulkAction, { BulkActionText } from "../BulkAction.jsx";
import NoteAutocomplete from "../../react/NoteAutocomplete.jsx";

View File

@@ -0,0 +1,102 @@
.global-menu {
width: 53px;
height: 53px;
flex-shrink: 0;
}
.global-menu .dropdown-menu {
min-width: 20em;
}
.global-menu-button {
width: 100% !important;
height: 100% !important;
position: relative;
padding: 6px;
border: 0;
}
.global-menu-button svg path {
fill: var(--launcher-pane-text-color);
}
.global-menu-button:hover { border: 0; }
.global-menu-button:hover svg path {
transition: 200ms ease-in-out fill;
}
.global-menu-button:hover svg path.st0 { fill:#95C980; }
.global-menu-button:hover svg path.st1 { fill:#72B755; }
.global-menu-button:hover svg path.st2 { fill:#4FA52B; }
.global-menu-button:hover svg path.st3 { fill:#EE8C89; }
.global-menu-button:hover svg path.st4 { fill:#E96562; }
.global-menu-button:hover svg path.st5 { fill:#E33F3B; }
.global-menu-button:hover svg path.st6 { fill:#EFB075; }
.global-menu-button:hover svg path.st7 { fill:#E99547; }
.global-menu-button:hover svg path.st8 { fill:#E47B19; }
.global-menu-button-update-available {
position: absolute;
right: -30px;
bottom: -30px;
width: 100%;
height: 100%;
pointer-events: none;
}
.global-menu .zoom-container {
display: flex;
flex-direction: row;
align-items: baseline;
}
.global-menu .zoom-buttons {
margin-left: 2em;
}
.global-menu .zoom-buttons a {
display: inline-block;
border: 1px solid var(--button-border-color);
border-radius: var(--button-border-radius);
color: var(--button-text-color);
background-color: var(--button-background-color);
padding: 3px;
margin-left: 3px;
text-decoration: none;
}
.global-menu .zoom-buttons a:hover {
text-decoration: none;
}
.global-menu .zoom-state {
margin-left: 5px;
margin-right: 5px;
}
.global-menu .dropdown-item .bx {
position: relative;
top: 3px;
font-size: 120%;
margin-right: 6px;
}
/* #region Update available */
.global-menu-button-update-available-button {
width: 21px !important;
height: 21px !important;
padding: 0 !important;
border-radius: var(--button-border-radius);
transform: scale(0.9);
border: none;
opacity: 0.8;
display: flex;
align-items: center;
justify-content: center;
}
.global-menu-button-wrapper:hover .global-menu-button-update-available-button {
opacity: 1;
}
/* #endregion */

View File

@@ -1,436 +0,0 @@
import { t } from "../../services/i18n.js";
import BasicWidget from "../basic_widget.js";
import utils from "../../services/utils.js";
import UpdateAvailableWidget from "./update_available.js";
import options from "../../services/options.js";
import { Tooltip, Dropdown } from "bootstrap";
const TPL = /*html*/`
<div class="dropdown global-menu">
<style>
.global-menu {
width: 53px;
height: 53px;
flex-shrink: 0;
}
.global-menu .dropdown-menu {
min-width: 20em;
}
.global-menu-button {
width: 100%;
height: 100%;
position: relative;
padding: 6px;
border: 0;
}
.global-menu-button > svg path {
fill: var(--launcher-pane-text-color);
}
.global-menu-button:hover { border: 0; }
.global-menu-button:hover > svg path {
transition: 200ms ease-in-out fill;
}
.global-menu-button:hover > svg path.st0 { fill:#95C980; }
.global-menu-button:hover > svg path.st1 { fill:#72B755; }
.global-menu-button:hover > svg path.st2 { fill:#4FA52B; }
.global-menu-button:hover > svg path.st3 { fill:#EE8C89; }
.global-menu-button:hover > svg path.st4 { fill:#E96562; }
.global-menu-button:hover > svg path.st5 { fill:#E33F3B; }
.global-menu-button:hover > svg path.st6 { fill:#EFB075; }
.global-menu-button:hover > svg path.st7 { fill:#E99547; }
.global-menu-button:hover > svg path.st8 { fill:#E47B19; }
.global-menu-button-update-available {
position: absolute;
right: -30px;
bottom: -30px;
width: 100%;
height: 100%;
pointer-events: none;
}
.global-menu .zoom-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
}
.global-menu .zoom-buttons a {
display: inline-block;
border: 1px solid var(--button-border-color);
border-radius: var(--button-border-radius);
color: var(--button-text-color);
background-color: var(--button-background-color);
padding: 3px;
margin-left: 3px;
text-decoration: none;
}
.global-menu .zoom-buttons a:hover {
text-decoration: none;
}
.global-menu .zoom-state {
margin-left: 5px;
margin-right: 5px;
}
.global-menu .dropdown-item .bx {
position: relative;
top: 3px;
font-size: 120%;
margin-right: 6px;
}
</style>
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true"
aria-expanded="false" class="icon-action global-menu-button">
<div class="global-menu-button-update-available"></div>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li class="dropdown-item" data-trigger-command="openNewWindow">
<span class="bx bx-window-open"></span>
${t("global_menu.open_new_window")}
<kbd data-command="openNewWindow"></kbd>
</li>
<li class="dropdown-item" data-trigger-command="showShareSubtree">
<span class="bx bx-share-alt"></span>
${t("global_menu.show_shared_notes_subtree")}
</li>
<div class="dropdown-divider"></div>
<span class="zoom-container dropdown-item dropdown-item-container">
<div>
<span class="bx bx-empty"></span>
${t("global_menu.zoom")}
</div>
<div class="zoom-buttons">
<a data-trigger-command="toggleFullscreen" title="${t("global_menu.toggle_fullscreen")}" class="bx bx-expand-alt"></a>
&nbsp;
<a data-trigger-command="zoomOut" title="${t("global_menu.zoom_out")}" class="bx bx-minus"></a>
<span data-trigger-command="zoomReset" title="${t("global_menu.reset_zoom_level")}" class="zoom-state"></span>
<a data-trigger-command="zoomIn" title="${t("global_menu.zoom_in")}" class="bx bx-plus"></a>
</div>
</span>
<li class="dropdown-item toggle-pin">
<span class="bx bx-pin"></span>
${t("title_bar_buttons.window-on-top")}
</li>
<li class="dropdown-item" data-trigger-command="toggleZenMode">
<span class="bx bxs-yin-yang"></span>
${t("global_menu.toggle-zen-mode")}
<kbd data-command="toggleZenMode"></kbd>
</li>
<div class="dropdown-divider desktop-only"></div>
<li class="dropdown-item switch-to-mobile-version-button" data-trigger-command="switchToMobileVersion">
<span class="bx bx-mobile"></span>
${t("global_menu.switch_to_mobile_version")}
</li>
<li class="dropdown-item switch-to-desktop-version-button" data-trigger-command="switchToDesktopVersion">
<span class="bx bx-desktop"></span>
${t("global_menu.switch_to_desktop_version")}
</li>
<li class="dropdown-item" data-trigger-command="showLaunchBarSubtree">
<span class="bx ${utils.isMobile() ? "bx-mobile" : "bx-sidebar"}"></span>
${t("global_menu.configure_launchbar")}
</li>
<li class="dropdown-item dropdown-submenu">
<span class="dropdown-toggle">
<span class="bx bx-chip"></span>${t("global_menu.advanced")}
</span>
<ul class="dropdown-menu">
<li class="dropdown-item" data-trigger-command="showHiddenSubtree">
<span class="bx bx-hide"></span>
${t("global_menu.show_hidden_subtree")}
</li>
<li class="dropdown-item" data-trigger-command="showSearchHistory">
<span class="bx bx-search-alt"></span>
${t("global_menu.open_search_history")}
</li>
<div class="dropdown-divider"></div>
<li class="dropdown-item" data-trigger-command="showBackendLog">
<span class="bx bx-detail"></span>
${t("global_menu.show_backend_log")}
<kbd data-command="showBackendLog"></kbd>
</li>
<li class="dropdown-item" data-trigger-command="showSQLConsole">
<span class="bx bx-data"></span>
${t("global_menu.open_sql_console")}
<kbd data-command="showSQLConsole"></kbd>
</li>
<li class="dropdown-item" data-trigger-command="showSQLConsoleHistory">
<span class="bx bx-data"></span>
${t("global_menu.open_sql_console_history")}
</li>
<div class="dropdown-divider"></div>
<li class="dropdown-item open-dev-tools-button" data-trigger-command="openDevTools">
<span class="bx bx-bug-alt"></span>
${t("global_menu.open_dev_tools")}
<kbd data-command="openDevTools"></kbd>
</li>
<li class="dropdown-item" data-trigger-command="reloadFrontendApp"
title="${t("global_menu.reload_hint")}">
<span class="bx bx-refresh"></span>
${t("global_menu.reload_frontend")}
<kbd data-command="reloadFrontendApp"></kbd>
</li>
</ul>
</li>
<li class="dropdown-item" data-trigger-command="showOptions">
<span class="bx bx-cog"></span>
${t("global_menu.options")}
</li>
<div class="dropdown-divider desktop-only"></div>
<li class="dropdown-item show-help-button" data-trigger-command="showHelp">
<span class="bx bx-help-circle"></span>
${t("global_menu.show_help")}
<kbd data-command="showHelp"></kbd>
</li>
<li class="dropdown-item show-help-button" data-trigger-command="showCheatsheet">
<span class="bx bxs-keyboard"></span>
${t("global_menu.show-cheatsheet")}
<kbd data-command="showCheatsheet"></kbd>
</li>
<li class="dropdown-item show-about-dialog-button">
<span class="bx bx-info-circle"></span>
${t("global_menu.about")}
</li>
<li class="dropdown-item update-to-latest-version-button" style="display: none;" data-trigger-command="downloadLatestVersion">
<span class="bx bx-sync"></span>
<span class="version-text"></span>
</li>
<div class="dropdown-divider logout-button-separator"></div>
<li class="dropdown-item logout-button" data-trigger-command="logout">
<span class="bx bx-log-out"></span>
${t("global_menu.logout")}
</li>
</ul>
</div>
`;
export default class GlobalMenuWidget extends BasicWidget {
private updateAvailableWidget: UpdateAvailableWidget;
private isHorizontalLayout: boolean;
private tooltip!: Tooltip;
private dropdown!: Dropdown;
private $updateToLatestVersionButton!: JQuery<HTMLElement>;
private $zoomState!: JQuery<HTMLElement>;
private $toggleZenMode!: JQuery<HTMLElement>;
constructor(isHorizontalLayout: boolean) {
super();
this.updateAvailableWidget = new UpdateAvailableWidget();
this.isHorizontalLayout = isHorizontalLayout;
}
doRender() {
this.$widget = $(TPL);
if (!this.isHorizontalLayout) {
this.$widget.addClass("dropend");
}
const $globalMenuButton = this.$widget.find(".global-menu-button");
if (!this.isHorizontalLayout) {
$globalMenuButton.prepend(
$(`\
<svg viewBox="0 0 256 256" data-bs-toggle="tooltip" title="${t("global_menu.menu")}">
<g>
<path class="st0" d="m202.9 112.7c-22.5 16.1-54.5 12.8-74.9 6.3l14.8-11.8 14.1-11.3 49.1-39.3-51.2 35.9-14.3 10-14.9 10.5c0.7-21.2 7-49.9 28.6-65.4 1.8-1.3 3.9-2.6 6.1-3.8 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.4 2.8-4.9 5.4-7.4 7.8-3.4 3.5-6.8 6.4-10.1 8.8z"/>
<path class="st1" d="m213.1 104c-22.2 12.6-51.4 9.3-70.3 3.2l14.1-11.3 49.1-39.3-51.2 35.9-14.3 10c0.5-18.1 4.9-42.1 19.7-58.6 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.3 2.8-4.8 5.4-7.2 7.8z"/>
<path class="st2" d="m220.5 96.2c-21.1 8.6-46.6 5.3-63.7-0.2l49.2-39.4-51.2 35.9c0.3-15.8 3.5-36.6 14.3-52.8 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.8 66z"/>
<path class="st3" d="m106.7 179c-5.8-21 5.2-43.8 15.5-57.2l4.8 14.2 4.5 13.4 15.9 47-12.8-47.6-3.6-13.2-3.7-13.9c15.5 6.2 35.1 18.6 40.7 38.8 0.5 1.7 0.9 3.6 1.2 5.5 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.1-3.8-7.6-1.6-3.5-2.9-6.8-3.8-10z"/>
<path class="st4" d="m110.4 188.9c-3.4-19.8 6.9-40.5 16.6-52.9l4.5 13.4 15.9 47-12.8-47.6-3.6-13.2c13.3 5.2 29.9 15 38.1 30.4 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.2-3.8-7.7z"/>
<path class="st5" d="m114.2 196.5c-0.7-18 8.6-35.9 17.3-47.1l15.9 47-12.8-47.6c11.6 4.4 26.1 12.4 35.2 24.8 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8z"/>
<path class="st6" d="m86.3 59.1c21.7 10.9 32.4 36.6 35.8 54.9l-15.2-6.6-14.5-6.3-50.6-22 48.8 24.9 13.6 6.9 14.3 7.3c-16.6 7.9-41.3 14.5-62.1 4.1-1.8-0.9-3.6-1.9-5.4-3.2-2.3-1.5-4.5-3.2-6.8-5.1-19.9-16.4-40.3-46.4-42.7-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.2 0.8 6.2 1.6 9.1 2.5 4 1.3 7.6 2.8 10.9 4.4z"/>
<path class="st7" d="m75.4 54.8c18.9 12 28.4 35.6 31.6 52.6l-14.5-6.3-50.6-22 48.7 24.9 13.6 6.9c-14.1 6.8-34.5 13-53.3 8.2-2.3-1.5-4.5-3.2-6.8-5.1-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.1 0.8 6.2 1.6 9.1 2.6z"/>
<path class="st8" d="m66.3 52.2c15.3 12.8 23.3 33.6 26.1 48.9l-50.6-22 48.8 24.9c-12.2 6-29.6 11.8-46.5 10-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3z"/>
</g>
</svg>`)
);
this.tooltip = new Tooltip(this.$widget.find("[data-bs-toggle='tooltip']")[0], { trigger: "hover" });
} else {
$globalMenuButton.toggleClass("bx bx-menu");
}
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0], {
popperConfig: {
placement: "bottom"
}
});
this.$widget.find(".show-about-dialog-button").on("click", () => this.triggerCommand("openAboutDialog"));
const isElectron = utils.isElectron();
this.$widget.find(".toggle-pin").toggle(isElectron);
if (isElectron) {
this.$widget.on("click", ".toggle-pin", (e) => {
const $el = $(e.target);
const remote = utils.dynamicRequire("@electron/remote");
const focusedWindow = remote.BrowserWindow.getFocusedWindow();
const isAlwaysOnTop = focusedWindow.isAlwaysOnTop();
if (isAlwaysOnTop) {
focusedWindow.setAlwaysOnTop(false);
$el.removeClass("active");
} else {
focusedWindow.setAlwaysOnTop(true);
$el.addClass("active");
}
});
}
this.$widget.find(".logout-button").toggle(!isElectron);
this.$widget.find(".logout-button-separator").toggle(!isElectron);
this.$widget.find(".open-dev-tools-button").toggle(isElectron);
this.$widget.find(".switch-to-mobile-version-button").toggle(!isElectron && utils.isDesktop());
this.$widget.find(".switch-to-desktop-version-button").toggle(!isElectron && utils.isMobile());
this.$widget.on("click", ".dropdown-item", (e) => {
if ($(e.target).parent(".zoom-buttons")) {
return;
}
this.dropdown.toggle();
});
if (utils.isMobile()) {
this.$widget.on("click", ".dropdown-submenu .dropdown-toggle", (e) => {
const $submenu = $(e.target).closest(".dropdown-item");
$submenu.toggleClass("submenu-open");
$submenu.find("ul.dropdown-menu").toggleClass("show");
e.stopPropagation();
return;
});
}
this.$widget.on("click", ".dropdown-submenu", (e) => {
if ($(e.target).children(".dropdown-menu").length === 1 || $(e.target).hasClass("dropdown-toggle")) {
e.stopPropagation();
}
});
this.$widget.find(".global-menu-button-update-available").append(this.updateAvailableWidget.render());
this.$updateToLatestVersionButton = this.$widget.find(".update-to-latest-version-button");
if (!utils.isElectron()) {
this.$widget.find(".zoom-container").hide();
}
this.$zoomState = this.$widget.find(".zoom-state");
this.$toggleZenMode = this.$widget.find('[data-trigger-command="toggleZenMode"');
this.$widget.on("show.bs.dropdown", () => this.#onShown());
if (this.tooltip) {
this.$widget.on("hide.bs.dropdown", () => this.tooltip.enable());
}
this.$widget.find(".zoom-buttons").on(
"click",
// delay to wait for the actual zoom change
() => setTimeout(() => this.updateZoomState(), 300)
);
this.updateVersionStatus();
setInterval(() => this.updateVersionStatus(), 8 * 60 * 60 * 1000);
}
#onShown() {
this.$toggleZenMode.toggleClass("active", $("body").hasClass("zen"));
this.updateZoomState();
if (this.tooltip) {
this.tooltip.hide();
this.tooltip.disable();
}
}
updateZoomState() {
if (!utils.isElectron()) {
return;
}
const zoomFactor = utils.dynamicRequire("electron").webFrame.getZoomFactor();
const zoomPercent = Math.round(zoomFactor * 100);
this.$zoomState.text(`${zoomPercent}%`);
}
async updateVersionStatus() {
await options.initializedPromise;
if (options.get("checkForUpdates") !== "true") {
return;
}
const latestVersion = await this.fetchLatestVersion();
this.updateAvailableWidget.updateVersionStatus(latestVersion);
// Show "click to download" button in options menu if there's a new version available
this.$updateToLatestVersionButton.toggle(utils.isUpdateAvailable(latestVersion, glob.triliumVersion));
this.$updateToLatestVersionButton.find(".version-text").text(`Version ${latestVersion} is available, click to download.`);
}
async fetchLatestVersion() {
const RELEASES_API_URL = "https://api.github.com/repos/TriliumNext/Notes/releases/latest";
const resp = await fetch(RELEASES_API_URL);
const data = await resp.json();
return data?.tag_name?.substring(1);
}
downloadLatestVersionCommand() {
window.open("https://github.com/TriliumNext/Trilium/releases/latest");
}
activeContextChangedEvent() {
this.dropdown.hide();
}
noteSwitchedEvent() {
this.dropdown.hide();
}
}

View File

@@ -0,0 +1,239 @@
import Dropdown from "../react/Dropdown";
import "./global_menu.css";
import { useStaticTooltip, useStaticTooltipWithKeyboardShortcut, useTriliumOption, useTriliumOptionBool } from "../react/hooks";
import { useContext, useEffect, useRef, useState } from "preact/hooks";
import { t } from "../../services/i18n";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import { CommandNames } from "../../components/app_context";
import KeyboardShortcut from "../react/KeyboardShortcut";
import { KeyboardActionNames } from "@triliumnext/commons";
import { ComponentChildren } from "preact";
import Component from "../../components/component";
import { ParentComponent } from "../react/react_utils";
import utils, { dynamicRequire, isElectron, isMobile } from "../../services/utils";
interface MenuItemProps<T> {
icon: string,
text: ComponentChildren,
title?: string,
command: T,
disabled?: boolean
active?: boolean;
outsideChildren?: ComponentChildren;
}
export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout: boolean }) {
const isVerticalLayout = !isHorizontalLayout;
const parentComponent = useContext(ParentComponent);
const { isUpdateAvailable, latestVersion } = useTriliumUpdateStatus();
return (
<Dropdown
className="global-menu"
buttonClassName={`global-menu-button ${isHorizontalLayout ? "bx bx-menu" : ""}`} noSelectButtonStyle iconAction hideToggleArrow
text={<>
{isVerticalLayout && <VerticalLayoutIcon />}
{isUpdateAvailable && <div class="global-menu-button-update-available">
<span className="bx bx-sync global-menu-button-update-available-button" title={t("update_available.update_available")}></span>
</div>}
</>}
>
<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")} />
<FormDropdownDivider />
<ZoomControls parentComponent={parentComponent} />
<ToggleWindowOnTop />
<KeyboardActionMenuItem command="toggleZenMode" icon="bx bxs-yin-yang" text={t("global_menu.toggle-zen-mode")} />
<FormDropdownDivider />
<SwitchToOptions />
<MenuItem command="showLaunchBarSubtree" icon={`bx ${isMobile() ? "bx-mobile" : "bx-sidebar"}`} text={t("global_menu.configure_launchbar")} />
<AdvancedMenu />
<MenuItem command="showOptions" icon="bx bx-cog" text={t("global_menu.options")} />
<FormDropdownDivider />
<KeyboardActionMenuItem command="showHelp" icon="bx bx-help-circle" text={t("global_menu.show_help")} />
<KeyboardActionMenuItem command="showCheatsheet" icon="bx bxs-keyboard" text={t("global_menu.show-cheatsheet")} />
<MenuItem command="openAboutDialog" icon="bx bx-info-circle" text={t("global_menu.about")} />
{isUpdateAvailable && <MenuItem command={() => window.open("https://github.com/TriliumNext/Trilium/releases/latest")} icon="bx bx-sync" text={`Version ${latestVersion} is available, click to download.`} /> }
{!isElectron() && <BrowserOnlyOptions />}
</Dropdown>
)
}
function AdvancedMenu() {
return (
<FormDropdownSubmenu icon="bx bx-chip" title={t("global_menu.advanced")}>
<MenuItem command="showHiddenSubtree" icon="bx bx-hide" text={t("global_menu.show_hidden_subtree")} />
<MenuItem command="showSearchHistory" icon="bx bx-search-alt" text={t("global_menu.open_search_history")} />
<FormDropdownDivider />
<KeyboardActionMenuItem command="showBackendLog" icon="bx bx-detail" text={t("global_menu.show_backend_log")} />
<KeyboardActionMenuItem command="showSQLConsole" icon="bx bx-data" text={t("global_menu.open_sql_console")} />
<MenuItem command="showSQLConsoleHistory" icon="bx bx-data" text={t("global_menu.open_sql_console_history")} />
<FormDropdownDivider />
{isElectron() && <MenuItem command="openDevTools" icon="bx bx-bug-alt" text={t("global_menu.open_dev_tools")} />}
<KeyboardActionMenuItem command="reloadFrontendApp" icon="bx bx-refresh" text={t("global_menu.reload_frontend")} title={t("global_menu.reload_hint")} />
</FormDropdownSubmenu>
)
}
function BrowserOnlyOptions() {
return <>
<FormDropdownDivider />
<MenuItem command="logout" icon="bx bx-log-out" text={t("global_menu.logout")} />
</>;
}
function SwitchToOptions() {
if (isElectron()) {
return;
} else if (!isMobile()) {
return <MenuItem command="switchToMobileVersion" icon="bx bx-mobile" text={t("global_menu.switch_to_mobile_version")} />
} else {
return <MenuItem command="switchToDesktopVersion" icon="bx bx-desktop" text={t("global_menu.switch_to_desktop_version")} />
}
}
function MenuItem({ icon, text, title, command, disabled, active }: MenuItemProps<KeyboardActionNames | CommandNames | (() => void)>) {
return <FormListItem
icon={icon}
title={title}
triggerCommand={typeof command === "string" ? command : undefined}
onClick={typeof command === "function" ? command : undefined}
disabled={disabled}
active={active}
>{text}</FormListItem>
}
function KeyboardActionMenuItem({ text, command, ...props }: MenuItemProps<KeyboardActionNames>) {
return <MenuItem
{...props}
command={command}
text={<>{text} <KeyboardShortcut actionName={command as KeyboardActionNames} /></>}
/>
}
function VerticalLayoutIcon() {
const logoRef = useRef<SVGSVGElement>(null);
useStaticTooltip(logoRef);
return (
<svg ref={logoRef} viewBox="0 0 256 256" title={t("global_menu.menu")}>
<g>
<path className="st0" d="m202.9 112.7c-22.5 16.1-54.5 12.8-74.9 6.3l14.8-11.8 14.1-11.3 49.1-39.3-51.2 35.9-14.3 10-14.9 10.5c0.7-21.2 7-49.9 28.6-65.4 1.8-1.3 3.9-2.6 6.1-3.8 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.4 2.8-4.9 5.4-7.4 7.8-3.4 3.5-6.8 6.4-10.1 8.8z"/>
<path className="st1" d="m213.1 104c-22.2 12.6-51.4 9.3-70.3 3.2l14.1-11.3 49.1-39.3-51.2 35.9-14.3 10c0.5-18.1 4.9-42.1 19.7-58.6 2.7-1.5 5.7-2.9 8.8-4.1 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.9 65.9-2.3 2.8-4.8 5.4-7.2 7.8z"/>
<path className="st2" d="m220.5 96.2c-21.1 8.6-46.6 5.3-63.7-0.2l49.2-39.4-51.2 35.9c0.3-15.8 3.5-36.6 14.3-52.8 27.1-11.1 68.5-15.3 85.2-9.5 0.1 16.2-15.9 45.4-33.8 66z"/>
<path className="st3" d="m106.7 179c-5.8-21 5.2-43.8 15.5-57.2l4.8 14.2 4.5 13.4 15.9 47-12.8-47.6-3.6-13.2-3.7-13.9c15.5 6.2 35.1 18.6 40.7 38.8 0.5 1.7 0.9 3.6 1.2 5.5 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.1-3.8-7.6-1.6-3.5-2.9-6.8-3.8-10z"/>
<path className="st4" d="m110.4 188.9c-3.4-19.8 6.9-40.5 16.6-52.9l4.5 13.4 15.9 47-12.8-47.6-3.6-13.2c13.3 5.2 29.9 15 38.1 30.4 0.4 2.4 0.6 5 0.7 7.7 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8-1.4-2.6-2.7-5.2-3.8-7.7z"/>
<path className="st5" d="m114.2 196.5c-0.7-18 8.6-35.9 17.3-47.1l15.9 47-12.8-47.6c11.6 4.4 26.1 12.4 35.2 24.8 0.9 23.1-7.1 54.9-15.9 65.7-12-4.3-29.3-24-39.7-42.8z"/>
<path className="st6" d="m86.3 59.1c21.7 10.9 32.4 36.6 35.8 54.9l-15.2-6.6-14.5-6.3-50.6-22 48.8 24.9 13.6 6.9 14.3 7.3c-16.6 7.9-41.3 14.5-62.1 4.1-1.8-0.9-3.6-1.9-5.4-3.2-2.3-1.5-4.5-3.2-6.8-5.1-19.9-16.4-40.3-46.4-42.7-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.2 0.8 6.2 1.6 9.1 2.5 4 1.3 7.6 2.8 10.9 4.4z"/>
<path className="st7" d="m75.4 54.8c18.9 12 28.4 35.6 31.6 52.6l-14.5-6.3-50.6-22 48.7 24.9 13.6 6.9c-14.1 6.8-34.5 13-53.3 8.2-2.3-1.5-4.5-3.2-6.8-5.1-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3 3.1 0.8 6.2 1.6 9.1 2.6z"/>
<path className="st8" d="m66.3 52.2c15.3 12.8 23.3 33.6 26.1 48.9l-50.6-22 48.8 24.9c-12.2 6-29.6 11.8-46.5 10-19.8-16.4-40.2-46.4-42.6-61.5 12.4-6.5 41.5-5.8 64.8-0.3z"/>
</g>
</svg>
)
}
function ZoomControls({ parentComponent }: { parentComponent?: Component | null }) {
const [ zoomLevel, setZoomLevel ] = useState(100);
function updateZoomState() {
if (!isElectron()) {
return;
}
const zoomFactor = dynamicRequire("electron").webFrame.getZoomFactor();
setZoomLevel(Math.round(zoomFactor * 100));
}
useEffect(updateZoomState, []);
function ZoomControlButton({ command, title, icon, children }: { command: KeyboardActionNames, title: string, icon?: string, children?: ComponentChildren }) {
const linkRef = useRef<HTMLAnchorElement>(null);
useStaticTooltipWithKeyboardShortcut(linkRef, title, command);
return (
<a
ref={linkRef}
onClick={(e) => {
parentComponent?.triggerCommand(command);
setTimeout(() => updateZoomState(), 300)
e.stopPropagation();
}}
className={icon}
>{children}</a>
)
}
return isElectron() ? (
<FormListItem
icon="bx bx-empty"
className="zoom-container"
>
{t("global_menu.zoom")}
<>
<div className="zoom-buttons">
<ZoomControlButton command="toggleFullscreen" title={t("global_menu.toggle_fullscreen")} icon="bx bx-expand-alt" />
&nbsp;
<ZoomControlButton command="zoomOut" title={t("global_menu.zoom_out")} icon="bx bx-minus" />
<ZoomControlButton command="zoomReset" title={t("global_menu.reset_zoom_level")}>{zoomLevel}{t("units.percentage")}</ZoomControlButton>
<ZoomControlButton command="zoomIn" title={t("global_menu.zoom_in")} icon="bx bx-plus" />
</div>
</>
</FormListItem>
) : (
<MenuItem icon="bx bx-expand-alt" command="toggleFullscreen" text={t("global_menu.toggle_fullscreen")} />
);
}
function ToggleWindowOnTop() {
const focusedWindow = isElectron() ? dynamicRequire("@electron/remote").BrowserWindow.getFocusedWindow() : null;
const [ isAlwaysOnTop, setIsAlwaysOnTop ] = useState(focusedWindow?.isAlwaysOnTop());
return (isElectron() &&
<MenuItem
icon="bx bx-pin"
text={t("title_bar_buttons.window-on-top")}
active={isAlwaysOnTop}
command={() => {
const newState = !isAlwaysOnTop;
focusedWindow?.setAlwaysOnTop(newState);
setIsAlwaysOnTop(newState);
}}
/>
)
}
function useTriliumUpdateStatus() {
const [ latestVersion, setLatestVersion ] = useState<string>();
const [ checkForUpdates ] = useTriliumOptionBool("checkForUpdates");
const isUpdateAvailable = utils.isUpdateAvailable(latestVersion, glob.triliumVersion);
async function updateVersionStatus() {
const RELEASES_API_URL = "https://api.github.com/repos/TriliumNext/Trilium/releases/latest";
const resp = await fetch(RELEASES_API_URL);
const data = await resp.json();
const latestVersion = data?.tag_name?.substring(1);
setLatestVersion(latestVersion);
}
useEffect(() => {
if (!checkForUpdates) {
setLatestVersion(undefined);
return;
}
updateVersionStatus();
const interval = setInterval(() => updateVersionStatus(), 8 * 60 * 60 * 1000);
return () => clearInterval(interval);
}, [ checkForUpdates ]);
return { isUpdateAvailable, latestVersion };
}

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