Compare commits

...

264 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
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
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
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
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
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
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
Adorian Doran
d73e84ea6c client/quick search results: tweak icon alignment 2025-08-29 18:37:02 +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
180 changed files with 5219 additions and 8035 deletions

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

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.

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

@@ -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
@@ -79,7 +78,7 @@ jobs:
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
@@ -120,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:
@@ -115,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

@@ -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

@@ -142,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
@@ -151,7 +151,7 @@ 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/Trilium/tree/main/docs/Developer%20Guide/Developer%20Guide).

View File

@@ -36,12 +36,12 @@
},
"devDependencies": {
"@playwright/test": "1.55.0",
"@stylistic/eslint-plugin": "5.2.3",
"@stylistic/eslint-plugin": "5.3.1",
"@types/express": "5.0.3",
"@types/node": "22.18.0",
"@types/node": "22.18.1",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.34.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.11",
"typedoc": "0.28.12",
"typedoc-plugin-missing-exports": "4.1.0"
},
"optionalDependencies": {

View File

@@ -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.34.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.9",
"@mermaid-js/layout-elk": "0.2.0",
"@mind-elixir/node-menu": "5.0.0",
"@popperjs/core": "2.11.8",
"@triliumnext/ckeditor5": "workspace:*",
@@ -30,13 +35,13 @@
"autocomplete.js": "0.38.1",
"bootstrap": "5.3.8",
"boxicons": "2.1.4",
"dayjs": "1.11.14",
"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.4.2",
"i18next": "25.5.2",
"i18next-http-backend": "3.0.2",
"jquery": "3.7.1",
"jquery.fancytree": "2.38.5",
@@ -47,12 +52,12 @@
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
"marked": "16.2.1",
"mermaid": "11.10.1",
"mind-elixir": "5.0.6",
"mermaid": "11.11.0",
"mind-elixir": "5.1.1",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.27.1",
"react-i18next": "15.7.2",
"react-i18next": "15.7.3",
"split.js": "1.6.5",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",
@@ -64,25 +69,12 @@
"@types/bootstrap": "5.2.10",
"@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.2"
},
"nx": {
"name": "client",
"targets": {
"serve": {
"dependsOn": [
"^build"
]
},
"circular-deps": {
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
}
}
}
}

View File

@@ -10,7 +10,7 @@ import { t } from "./services/i18n.js";
import options from "./services/options.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

@@ -22,6 +22,7 @@ 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 = `
@@ -131,30 +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(<ToggleSidebarButton />)
.child(<NoteTitleWidget />)
.child(<MobileDetailMenu />)
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(<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(

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

@@ -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

@@ -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

@@ -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

@@ -40,6 +40,24 @@ 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);
}
@@ -68,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();

View File

@@ -297,6 +297,54 @@ function isHtmlEmpty(html: string) {
);
}
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();
@@ -855,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

@@ -1134,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 {
@@ -1242,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;
@@ -1463,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;
}
@@ -1771,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;
}
@@ -2366,3 +2370,21 @@ footer.webview-footer button {
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;
@@ -147,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;
@@ -155,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;
@@ -115,17 +116,17 @@
--quick-search-focus-border: #00000029;
--quick-search-focus-background: #ffffff80;
--quick-search-focus-color: #000;
--quick-search-result-content-background: #00000017;
--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);
@@ -141,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;
@@ -148,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

@@ -329,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;
}
/*

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;

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

@@ -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;
}
@@ -575,31 +566,20 @@ 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;
}
.quick-search .quick-search-item-icon {
vertical-align: text-bottom;
}
/* 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;
}
/* Note path */
.quick-search .dropdown-menu small {
display: block;
@@ -622,9 +602,8 @@ div.quick-search .search-button.show {
font-weight: 600;
}
/* Divider line */
.quick-search .dropdown-item::after {
display: none;
.quick-search div.dropdown-divider {
margin: 8px 0;
}
/*
@@ -899,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);
}
@@ -1114,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
*/
@@ -1122,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;

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?",
@@ -1118,7 +1123,9 @@
"title": "Performance",
"enable-motion": "Enable transitions and animations",
"enable-shadows": "Enable shadows",
"enable-backdrop-effects": "Enable background effects for menus, popups and panels"
"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",

View File

@@ -30,13 +30,16 @@
"search_note": "Wyszukaj notatkę po nazwie",
"link_title_arbitrary": "Tytuł linku można dowolnie zmieniać",
"link_title": "Tytuł linku",
"button_add_link": "Dodaj link"
"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."
"branch_prefix_saved": "Zapisano prefiks gałęzi.",
"help_on_tree_prefix": "Pomoc dotycząca prefiksu drzewa"
},
"bulk_actions": {
"labels": "Etykiety",
@@ -98,7 +101,8 @@
"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}}\""
"note_cloned": "Notatka \"{{clonedTitle}}\" została sklonowana do \"{{targetTitle}}\"",
"help_on_links": "Pomoc dotycząca linków"
},
"help": {
"title": "Ściągawka",

View File

@@ -2013,7 +2013,9 @@
"title": "Setări de performanță",
"enable-motion": "Activează tranzițiile și animațiile",
"enable-shadows": "Activează umbrirea elementelor",
"enable-backdrop-effects": "Activează efectele de fundal pentru meniuri, popup-uri și panouri"
"enable-backdrop-effects": "Activează efectele de fundal pentru meniuri, popup-uri și panouri",
"enable-smooth-scroll": "Activează derularea lină",
"app-restart-required": "(este necesară repornirea aplicației pentru ca modificarea să aibă efect)"
},
"settings": {
"related_settings": "Setări similare"

View File

@@ -844,7 +844,8 @@
"note_type": "Тип нотатки",
"editable": "Редагув.",
"basic_properties": "Основні Властивості",
"language": "Мова"
"language": "Мова",
"configure_code_notes": "Конфігурація нотатки з кодом..."
},
"book_properties": {
"view_type": "Тип перегляду",
@@ -1586,7 +1587,8 @@
"hoist-this-note-workspace": "Закріпити цю нотатку (робочий простір)",
"refresh-saved-search-results": "Оновити збережені результати пошуку",
"create-child-note": "Створити дочірню нотатку",
"unhoist": "Відкріпити"
"unhoist": "Відкріпити",
"toggle-sidebar": "Перемикання бічної панелі"
},
"title_bar_buttons": {
"window-on-top": "Тримати вікно зверху"
@@ -1909,8 +1911,8 @@
"open-in-popup": "Швидке редагування"
},
"shared_info": {
"shared_publicly": "Ця нотатка опублікована на {{- link}}",
"shared_locally": "Цю нотатку опубліковано локально на {{- link}}",
"shared_publicly": "Ця нотатка опублікована на {{- link}}.",
"shared_locally": "Цю нотатку опубліковано локально на {{- link}}.",
"help_link": "Щоб отримати допомогу, відвідайте <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">вікі</a>."
},
"note_types": {
@@ -2018,5 +2020,11 @@
},
"units": {
"percentage": "%"
},
"ui-performance": {
"title": "Продуктивність",
"enable-motion": "Увімкнути переходи та анімацію",
"enable-shadows": "Увімкнути тіні",
"enable-backdrop-effects": "Увімкнути фонові ефекти для меню, спливаючих вікон та панелей"
}
}

View File

@@ -3,7 +3,7 @@ import appContext, { type CommandData, type CommandListenerData, type EventData,
import type BasicWidget from "../basic_widget.js";
import type NoteContext from "../../components/note_context.js";
import Component from "../../components/component.js";
import splitService from "../../services/resizer.js";
interface NoteContextEvent {
noteContext: NoteContext;
}
@@ -52,6 +52,10 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
await widget.handleEvent("setNoteContext", { noteContext });
this.child(widget);
if (noteContext.mainNtxId && noteContext.ntxId) {
splitService.setupNoteSplitResizer([noteContext.mainNtxId,noteContext.ntxId]);
}
}
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: EventData<"openNewNoteSplit">) {
@@ -95,9 +99,9 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
}
}
closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) {
async closeThisNoteSplitCommand({ ntxId }: CommandListenerData<"closeThisNoteSplit">) {
if (ntxId) {
appContext.tabManager.removeNoteContext(ntxId);
await appContext.tabManager.removeNoteContext(ntxId);
}
}
@@ -137,6 +141,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
// activate context that now contains the original note
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
splitService.moveNoteSplitResizer(ntxIds[leftIndex]);
}
activeContextChangedEvent() {
@@ -157,6 +163,8 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
recursiveCleanup(widget);
delete this.widgets[ntxId];
}
splitService.delNoteSplitResizer(ntxIds);
}
contextsReopenedEvent({ ntxId, afterNtxId }: EventData<"contextsReopened">) {

View File

@@ -7,6 +7,7 @@ import { t } from "../../services/i18n";
import server from "../../services/server";
import toast from "../../services/toast";
import Button from "../react/Button";
import FormToggle from "../react/FormToggle";
import Modal from "../react/Modal";
import FormList, { FormListItem } from "../react/FormList";
import utils from "../../services/utils";
@@ -18,12 +19,15 @@ import open from "../../services/open";
import ActionButton from "../react/ActionButton";
import options from "../../services/options";
import { useTriliumEvent } from "../react/hooks";
import { diffWords } from "diff";
export default function RevisionsDialog() {
const [ note, setNote ] = useState<FNote>();
const [ noteContent, setNoteContent ] = useState<string>();
const [ revisions, setRevisions ] = useState<RevisionItem[]>();
const [ currentRevision, setCurrentRevision ] = useState<RevisionItem>();
const [ shown, setShown ] = useState(false);
const [ showDiff, setShowDiff ] = useState(false);
const [ refreshCounter, setRefreshCounter ] = useState(0);
useTriliumEvent("showRevisions", async ({ noteId }) => {
@@ -37,8 +41,10 @@ export default function RevisionsDialog() {
useEffect(() => {
if (note?.noteId) {
server.get<RevisionItem[]>(`notes/${note.noteId}/revisions`).then(setRevisions);
note.getContent().then(setNoteContent);
} else {
setRevisions(undefined);
setNoteContent(undefined);
}
}, [ note?.noteId, refreshCounter ]);
@@ -54,22 +60,42 @@ export default function RevisionsDialog() {
helpPageId="vZWERwf8U3nx"
bodyStyle={{ display: "flex", height: "80vh" }}
header={
(!!revisions?.length && <Button text={t("revisions.delete_all_revisions")} size="small" style={{ padding: "0 10px" }}
onClick={async () => {
const text = t("revisions.confirm_delete_all");
!!revisions?.length && (
<>
{["text", "code", "mermaid"].includes(currentRevision?.type ?? "") && (
<FormToggle
currentValue={showDiff}
onChange={(newValue) => setShowDiff(newValue)}
switchOnName={t("revisions.diff_on")}
switchOffName={t("revisions.diff_off")}
switchOnTooltip={t("revisions.diff_on_hint")}
switchOffTooltip={t("revisions.diff_off_hint")}
/>
)}
&nbsp;
<Button
text={t("revisions.delete_all_revisions")}
size="small"
style={{ padding: "0 10px" }}
onClick={async () => {
const text = t("revisions.confirm_delete_all");
if (note && await dialog.confirm(text)) {
await server.remove(`notes/${note.noteId}/revisions`);
setRevisions([]);
setCurrentRevision(undefined);
toast.showMessage(t("revisions.revisions_deleted"));
}
}}/>)
if (note && await dialog.confirm(text)) {
await server.remove(`notes/${note.noteId}/revisions`);
setRevisions([]);
setCurrentRevision(undefined);
toast.showMessage(t("revisions.revisions_deleted"));
}
}}
/>
</>
)
}
footer={<RevisionFooter note={note} />}
footerStyle={{ paddingTop: 0, paddingBottom: 0 }}
onHidden={() => {
setShown(false);
setShowDiff(false);
setNote(undefined);
setCurrentRevision(undefined);
setRevisions(undefined);
@@ -92,10 +118,13 @@ export default function RevisionsDialog() {
marginLeft: "20px",
display: "flex",
flexDirection: "column",
maxWidth: "calc(100% - 150px)",
minWidth: 0
}}>
<RevisionPreview
revisionItem={currentRevision}
noteContent={noteContent}
revisionItem={currentRevision}
showDiff={showDiff}
setShown={setShown}
onRevisionDeleted={() => {
setRefreshCounter(c => c + 1);
@@ -121,8 +150,10 @@ function RevisionsList({ revisions, onSelect, currentRevision }: { revisions: Re
</FormList>);
}
function RevisionPreview({ revisionItem, setShown, onRevisionDeleted }: {
revisionItem?: RevisionItem,
function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevisionDeleted }: {
noteContent?: string,
revisionItem?: RevisionItem,
showDiff: boolean,
setShown: Dispatch<StateUpdater<boolean>>,
onRevisionDeleted?: () => void
}) {
@@ -179,7 +210,7 @@ function RevisionPreview({ revisionItem, setShown, onRevisionDeleted }: {
</div>)}
</div>
<div className="revision-content use-tn-links" style={{ overflow: "auto", wordBreak: "break-word" }}>
<RevisionContent revisionItem={revisionItem} fullRevision={fullRevision} />
<RevisionContent noteContent={noteContent} revisionItem={revisionItem} fullRevision={fullRevision} showDiff={showDiff}/>
</div>
</>
);
@@ -197,12 +228,15 @@ const CODE_STYLE: CSSProperties = {
whiteSpace: "pre-wrap"
};
function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: RevisionItem, fullRevision?: RevisionPojo }) {
function RevisionContent({ noteContent, revisionItem, fullRevision, showDiff }: { noteContent?:string, revisionItem?: RevisionItem, fullRevision?: RevisionPojo, showDiff: boolean}) {
const content = fullRevision?.content;
if (!revisionItem || !content) {
return <></>;
}
if (showDiff) {
return <RevisionContentDiff noteContent={noteContent} itemContent={content} itemType={revisionItem.type}/>
}
switch (revisionItem.type) {
case "text":
return <RevisionContentText content={content} />
@@ -267,6 +301,48 @@ function RevisionContentText({ content }: { content: string | Buffer<ArrayBuffer
return <div ref={contentRef} className="ck-content" dangerouslySetInnerHTML={{ __html: content as string }}></div>
}
function RevisionContentDiff({ noteContent, itemContent, itemType }: {
noteContent?: string,
itemContent: string | Buffer<ArrayBufferLike> | undefined,
itemType: string
}) {
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!noteContent || typeof itemContent !== "string") {
if (contentRef.current) {
contentRef.current.textContent = t("revisions.diff_not_available");
}
return;
}
let processedNoteContent = noteContent;
let processedItemContent = itemContent;
if (itemType === "text") {
processedNoteContent = utils.formatHtml(noteContent);
processedItemContent = utils.formatHtml(itemContent);
}
const diff = diffWords(processedNoteContent, processedItemContent);
const diffHtml = diff.map(part => {
if (part.added) {
return `<span class="revision-diff-added">${utils.escapeHtml(part.value)}</span>`;
} else if (part.removed) {
return `<span class="revision-diff-removed">${utils.escapeHtml(part.value)}</span>`;
} else {
return utils.escapeHtml(part.value);
}
}).join("");
if (contentRef.current) {
contentRef.current.innerHTML = diffHtml;
}
}, [noteContent, itemContent, itemType]);
return <div ref={contentRef} className="ck-content" style={{ whiteSpace: "pre-wrap" }}></div>;
}
function RevisionFooter({ note }: { note?: FNote }) {
if (!note) {
return <></>;

View File

@@ -8,6 +8,7 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
import attributeService from "../services/attributes.js";
import FindInText from "./find_in_text.js";
import FindInCode from "./find_in_code.js";
import { isIMEComposing } from "../services/shortcuts.js";
import FindInHtml from "./find_in_html.js";
import type { EventData } from "../components/app_context.js";
@@ -162,6 +163,11 @@ export default class FindWidget extends NoteContextAwareWidget {
this.$replaceButton.on("click", () => this.replace());
this.$input.on("keydown", async (e) => {
// Skip processing during IME composition
if (isIMEComposing(e.originalEvent as KeyboardEvent)) {
return;
}
if ((e.metaKey || e.ctrlKey) && (e.key === "F" || e.key === "f")) {
// If ctrl+f is pressed when the findbox is shown, select the
// whole input to find

View File

@@ -8,6 +8,7 @@ import "./note_title.css";
import { isLaunchBarConfig } from "../services/utils";
import appContext from "../components/app_context";
import branches from "../services/branches";
import { isIMEComposing } from "../services/shortcuts";
export default function NoteTitleWidget() {
const { note, noteId, componentId, viewScope, noteContext, parentComponent } = useNoteContext();
@@ -78,6 +79,12 @@ export default function NoteTitleWidget() {
spacedUpdate.scheduleUpdate();
}}
onKeyDown={(e) => {
// Skip processing if IME is composing to prevent interference
// with text input in CJK languages
if (isIMEComposing(e)) {
return;
}
// Focus on the note content when pressing enter.
if (e.key === "Enter") {
e.preventDefault();

View File

@@ -219,21 +219,22 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.$tree = this.$widget.find(".tree");
this.$treeActions = this.$widget.find(".tree-actions");
this.$tree.on("mousedown", ".unhoist-button", () => hoistedNoteService.unhoist());
this.$tree.on("mousedown", ".refresh-search-button", (e) => this.refreshSearch(e));
this.$tree.on("mousedown", ".add-note-button", (e) => {
const node = $.ui.fancytree.getNode(e as unknown as Event);
const parentNotePath = treeService.getNotePath(node);
this.$tree.on("mousedown", (e: JQuery.MouseDownEvent) => {
const target = e.target as HTMLElement;
if (e.button !== 0) return;
noteCreateService.createNote(parentNotePath, {
isProtected: node.data.isProtected
});
});
this.$tree.on("mousedown", ".enter-workspace-button", (e) => {
const node = $.ui.fancytree.getNode(e as unknown as Event);
this.triggerCommand("hoistNote", { noteId: node.data.noteId });
if (target.classList.contains("unhoist-button")) {
hoistedNoteService.unhoist();
} else if (target.classList.contains("refresh-search-button")) {
this.refreshSearch(e);
} else if (target.classList.contains("add-note-button")) {
const node = $.ui.fancytree.getNode(e as unknown as Event);
const parentNotePath = treeService.getNotePath(node);
noteCreateService.createNote(parentNotePath, { isProtected: node.data.isProtected });
} else if (target.classList.contains("enter-workspace-button")) {
const node = $.ui.fancytree.getNode(e as unknown as Event);
this.triggerCommand("hoistNote", { noteId: node.data.noteId });
}
});
// fancytree doesn't support middle click, so this is a way to support it

View File

@@ -23,15 +23,21 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
this.refresh();
}
noteSwitchedAndActivatedEvent() {
noteSwitchedAndActivatedEvent({ noteContext }: EventData<"setNoteContext">) {
this.noteContext = noteContext;
this.refresh();
}
noteSwitchedEvent() {
noteSwitchedEvent({ noteContext }: EventData<"setNoteContext">) {
this.noteContext = noteContext;
this.refresh();
}
activeContextChangedEvent() {
activeContextChangedEvent({ noteContext }: EventData<"setNoteContext">) {
this.noteContext = noteContext;
this.refresh();
}

View File

@@ -4,7 +4,7 @@ import linkService from "../services/link.js";
import froca from "../services/froca.js";
import utils from "../services/utils.js";
import appContext from "../components/app_context.js";
import shortcutService from "../services/shortcuts.js";
import shortcutService, { isIMEComposing } from "../services/shortcuts.js";
import { t } from "../services/i18n.js";
import { Dropdown, Tooltip } from "bootstrap";
@@ -15,13 +15,15 @@ const TPL = /*html*/`
padding: 10px 10px 10px 0px;
height: 50px;
}
.quick-search button, .quick-search input {
border: 0;
font-size: 100% !important;
}
.quick-search .dropdown-menu {
--quick-search-item-delimiter-color: var(--dropdown-border-color);
max-height: 80vh;
min-width: 400px;
max-width: 720px;
@@ -38,14 +40,14 @@ const TPL = /*html*/`
position: relative;
}
.quick-search .dropdown-item:not(:last-child)::after {
.quick-search .dropdown-item + .dropdown-item::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
top: 0;
width: 100%;
height: 1px;
background: var(--dropdown-border-color);
border-bottom: 1px solid var(--quick-search-item-delimiter-color);
}
.quick-search .dropdown-item:last-child::after {
@@ -92,6 +94,8 @@ const TPL = /*html*/`
background-color: var(--accented-background-color);
color: var(--main-text-color);
font-size: .85em;
overflow: hidden;
text-overflow: ellipsis;
}
/* Search result highlighting */
@@ -106,6 +110,10 @@ const TPL = /*html*/`
margin: 0;
}
.quick-search .bx-loader {
margin-inline-end: 4px;
}
</style>
<div class="input-group-prepend">
@@ -172,6 +180,14 @@ export default class QuickSearchWidget extends BasicWidget {
if (utils.isMobile()) {
this.$searchString.keydown((e) => {
// Skip processing if IME is composing to prevent interference
// with text input in CJK languages
// Note: jQuery wraps the native event, so we access originalEvent
const originalEvent = e.originalEvent as KeyboardEvent;
if (originalEvent && isIMEComposing(originalEvent)) {
return;
}
if (e.which === 13) {
if (this.$dropdownMenu.is(":visible")) {
this.search(); // just update already visible dropdown
@@ -220,7 +236,11 @@ export default class QuickSearchWidget extends BasicWidget {
this.isLoadingMore = false;
this.$dropdownMenu.empty();
this.$dropdownMenu.append(`<span class="dropdown-item disabled"><span class="bx bx-loader bx-spin"></span>${t("quick-search.searching")}</span>`);
this.$dropdownMenu.append(`
<span class="dropdown-item disabled">
<span class="bx bx-loader bx-spin"></span>
${t("quick-search.searching")}
</span>`);
const { searchResultNoteIds, searchResults, error } = await server.get<QuickSearchResponse>(`quick-search/${encodeURIComponent(searchString)}`);

View File

@@ -13,6 +13,7 @@ import attribute_parser, { Attribute } from "../../../services/attribute_parser"
import ActionButton from "../../react/ActionButton";
import { escapeQuotes, getErrorMessage } from "../../../services/utils";
import link from "../../../services/link";
import { isIMEComposing } from "../../../services/shortcuts";
import froca from "../../../services/froca";
import contextMenu from "../../../menus/context_menu";
import type { CommandData, FilteredCommandNames } from "../../../components/app_context";
@@ -287,6 +288,11 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
ref={wrapperRef}
style="position: relative; padding-top: 10px; padding-bottom: 10px"
onKeyDown={(e) => {
// Skip processing during IME composition
if (isIMEComposing(e)) {
return;
}
if (e.key === "Enter") {
// allow autocomplete to fill the result textarea
setTimeout(() => save(), 100);

View File

@@ -388,7 +388,7 @@ async function setupFonts() {
if (!glob.isDev) {
path = `${window.location.pathname}/node_modules/@excalidraw/excalidraw/dist/prod`;
} else {
path = (await import("../../../node_modules/@excalidraw/excalidraw/dist/prod/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2?url")).default;
path = (await import("../../../../../node_modules/@excalidraw/excalidraw/dist/prod/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2?url")).default;
let pathComponents = path.split("/");
path = pathComponents.slice(0, pathComponents.length - 2).join("/");
}

View File

@@ -4,7 +4,7 @@ import { buildExtraCommands, type EditorConfig, PREMIUM_PLUGINS } from "@trilium
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
import options from "../../../services/options.js";
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/src/emoji_definitions/en.json?url";
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
import getTemplates from "./snippets.js";
import { t } from "../../../services/i18n.js";

View File

@@ -1,5 +1,6 @@
import utils from "../../../services/utils.js";
import options from "../../../services/options.js";
import IconAlignCenter from "@ckeditor/ckeditor5-icons/theme/icons/align-center.svg?raw";
const TEXT_FORMATTING_GROUP = {
label: "Text formatting",
@@ -77,7 +78,7 @@ export function buildClassicToolbar(multilineToolbar: boolean) {
items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
},
"|",
"alignment",
buildAlignmentToolbar(),
"outdent",
"indent",
"|",
@@ -134,7 +135,7 @@ export function buildFloatingToolbar() {
items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
},
"|",
"alignment",
buildAlignmentToolbar(),
"outdent",
"indent",
"|",
@@ -147,3 +148,11 @@ export function buildFloatingToolbar() {
]
};
}
function buildAlignmentToolbar() {
return {
label: "Alignment",
icon: IconAlignCenter,
items: ["alignment:left", "alignment:center", "alignment:right", "|", "alignment:justify"]
};
}

View File

@@ -12,7 +12,6 @@ import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
import { buildConfig, BuildEditorOptions, OPEN_SOURCE_LICENSE_KEY } from "./ckeditor/config.js";
import type FNote from "../../entities/fnote.js";
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
import "@triliumnext/ckeditor5/index.css";
import { updateTemplateCache } from "./ckeditor/snippets.js";
const TPL = /*html*/`

View File

@@ -266,9 +266,20 @@ function Performance() {
label={t("ui-performance.enable-backdrop-effects")}
currentValue={backdropEffectsEnabled} onChange={setBackdropEffectsEnabled}
/>
{isElectron() && <SmoothScrollEnabledOption />}
</OptionsSection>
}
function SmoothScrollEnabledOption() {
const [ smoothScrollEnabled, setSmoothScrollEnabled ] = useTriliumOptionBool("smoothScrollEnabled");
return <FormCheckbox
label={`${t("ui-performance.enable-smooth-scroll")} ${t("ui-performance.app-restart-required")}`}
currentValue={smoothScrollEnabled} onChange={setSmoothScrollEnabled}
/>
}
function MaxContentWidth() {
const [ maxContentWidth, setMaxContentWidth ] = useTriliumOption("maxContentWidth");

View File

@@ -1,6 +1,7 @@
import type { EventData } from "../../components/app_context.js";
import type FNote from "../../entities/fnote.js";
import AbstractCodeTypeWidget from "./abstract_code_type_widget.js";
import utils from "../../services/utils.js";
const TPL = /*html*/`
<div class="note-detail-readonly-code note-detail-printable">
@@ -33,7 +34,7 @@ export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget {
if (!blob) return;
const isFormattable = note.type === "text" && this.noteContext?.viewScope?.viewMode === "source";
const content = isFormattable ? this.format(blob.content) : blob.content;
const content = isFormattable ? utils.formatHtml(blob.content) : blob.content;
this._update(note, content);
this.show();
@@ -54,52 +55,4 @@ export default class ReadOnlyCodeTypeWidget extends AbstractCodeTypeWidget {
resolve(this.$editor);
}
format(html: string) {
let indent = "\n";
const tab = "\t";
let i = 0;
let pre: { indent: string; tag: string }[] = [];
html = html
.replace(new RegExp("<pre>((.|\\t|\\n|\\r)+)?</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;
}
}

View File

@@ -2,26 +2,23 @@
import { join, resolve } from 'path';
import { defineConfig, type Plugin } from 'vite';
import { viteStaticCopy } from 'vite-plugin-static-copy'
import asset_path from './src/asset_path';
import webpackStatsPlugin from 'rollup-plugin-webpack-stats';
import preact from "@preact/preset-vite";
const assets = [ "assets", "stylesheets", "fonts", "translations" ];
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/client',
base: process.env.NODE_ENV === "production" ? "" : asset_path,
server: {
port: 4200,
host: 'localhost',
},
preview: {
port: 4300,
host: 'localhost',
},
plugins: [
preact(),
const isDev = process.env.NODE_ENV === "development";
let plugins: any = [
preact({
babel: {
compact: !isDev
}
})
];
if (!isDev) {
plugins = [
...plugins,
viteStaticCopy({
targets: assets.map((asset) => ({
src: `src/${asset}/*`,
@@ -32,20 +29,22 @@ export default defineConfig(() => ({
structured: true,
targets: [
{
src: "node_modules/@excalidraw/excalidraw/dist/prod/fonts/*",
src: "../../node_modules/@excalidraw/excalidraw/dist/prod/fonts/*",
dest: "",
}
]
}),
webpackStatsPlugin()
] as Plugin[],
]
}
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/client',
base: "",
plugins,
resolve: {
alias: [
// Force the use of dist in development mode because upstream ESM is broken (some hybrid between CJS and ESM, will be improved in upcoming versions).
{
find: "@triliumnext/highlightjs",
replacement: resolve(__dirname, "node_modules/@triliumnext/highlightjs/dist")
},
{
find: "react",
replacement: "preact/compat"
@@ -63,10 +62,6 @@ export default defineConfig(() => ({
"preact/hooks"
]
},
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: {
target: "esnext",
outDir: './dist',
@@ -105,18 +100,6 @@ export default defineConfig(() => ({
"./src/test/setup.ts"
]
},
optimizeDeps: {
exclude: [
"@triliumnext/highlightjs"
]
},
css: {
preprocessorOptions: {
scss: {
quietDeps: true
}
}
},
commonjsOptions: {
transformMixedEsModules: true,
},

View File

@@ -6,12 +6,12 @@
To build and run manually:
```sh
nx build db-compare
pnpm build db-compare
node ./apps/db-compare/dist/compare.js
```
To serve development build with arguments:
```sh
nx serve db-compare --args "apps/server/spec/db/document_v214.db" --args "apps/server/spec/db/document_v214_migrated.db"
pnpm dev --args "apps/server/spec/db/document_v214.db" --args "apps/server/spec/db/document_v214_migrated.db"
```

View File

@@ -9,63 +9,8 @@
"sqlite": "5.1.1",
"sqlite3": "5.1.7"
},
"nx": {
"name": "db-compare",
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"defaultConfiguration": "production",
"options": {
"platform": "node",
"outputPath": "apps/db-compare/dist",
"format": [
"cjs"
],
"bundle": false,
"main": "apps/db-compare/src/compare.ts",
"tsConfig": "apps/db-compare/tsconfig.app.json",
"assets": [],
"esbuildOptions": {
"sourcemap": true,
"outExtension": {
".js": ".js"
}
}
},
"configurations": {
"development": {},
"production": {
"esbuildOptions": {
"sourcemap": false,
"outExtension": {
".js": ".js"
}
}
}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"dependsOn": [
"build"
],
"options": {
"buildTarget": "db-compare:build",
"runBuildTargetDependencies": false
},
"configurations": {
"development": {
"buildTarget": "db-compare:build:development"
},
"production": {
"buildTarget": "db-compare:build:production"
}
}
}
}
"scripts": {
"dev": "tsx src/compare.ts",
"build": "esbuild --platform=node --format=cjs --outdir=dist src/compare.ts"
}
}

View File

@@ -1,3 +0,0 @@
TRILIUM_INTEGRATION_TEST=memory-no-store
TRILIUM_PORT=8082
TRILIUM_DATA_DIR=data

View File

@@ -1,15 +0,0 @@
import playwright from "eslint-plugin-playwright";
import baseConfig from "../../eslint.config.mjs";
export default [
playwright.configs["flat/recommended"],
...baseConfig,
{
files: [
"**/*.ts",
"**/*.js"
],
// Override or add rules here
rules: {}
}
];

View File

@@ -1,24 +0,0 @@
{
"name": "@triliumnext/desktop-e2e",
"version": "0.0.1",
"private": true,
"nx": {
"name": "desktop-e2e",
"implicitDependencies": [
"client",
"desktop"
],
"targets": {
"e2e": {
"dependsOn": [
"desktop:build",
"desktop:rebuild-deps"
]
}
}
},
"devDependencies": {
"dotenv": "17.2.1",
"electron": "37.4.0"
}
}

View File

@@ -1,25 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"allowJs": true,
"outDir": "out-tsc/playwright",
"sourceMap": false
},
"include": [
"**/*.ts",
"**/*.js",
"playwright.config.ts",
"src/**/*.spec.ts",
"src/**/*.spec.js",
"src/**/*.test.ts",
"src/**/*.test.js",
"src/**/*.d.ts"
],
"exclude": [
"out-tsc",
"test-output",
"eslint.config.js",
"eslint.config.mjs",
"eslint.config.cjs"
]
}

View File

@@ -1 +0,0 @@
node-linker = hoisted

View File

@@ -1 +0,0 @@
TRILIUM_PORT=37743

View File

@@ -1,3 +0,0 @@
TRILIUM_PORT=37741
TRILIUM_DATA_DIR=../data
NODE_OPTIONS=--enable-source-maps

View File

@@ -60,7 +60,7 @@ test('First setup', async () => {
// Verify the shared link is valid
const requestContext = await request.newContext();
const response = await requestContext.get(linkUrl!);
expect(response).toBeOK();
await expect(response).toBeOK();
await mainWindow.waitForTimeout(5000);
});

View File

@@ -3,7 +3,23 @@
"version": "0.98.1",
"description": "Build your personal knowledge base with Trilium Notes",
"private": true,
"main": "main.cjs",
"main": "src/main.ts",
"license": "AGPL-3.0-only",
"author": {
"name": "Trilium Notes Team",
"email": "contact@eliandoran.me",
"url": "https://github.com/TriliumNext/Notes"
},
"scripts": {
"dev": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data tsx ../../scripts/electron-start.mts src/main.ts",
"start-no-dir": "cross-env TRILIUM_PORT=37743 tsx ../../scripts/electron-start.mts src/main.ts",
"build": "tsx scripts/build.ts",
"start-prod": "pnpm build && cross-env TRILIUM_DATA_DIR=data TRILIUM_PORT=37841 ELECTRON_IS_DEV=0 electron dist",
"electron-forge:make": "pnpm build && cross-env electron-forge make dist",
"electron-forge:package": "pnpm build && electron-forge package dist",
"electron-forge:start": "pnpm build && electron-forge start dist",
"e2e": "pnpm build && cross-env TRILIUM_INTEGRATION_TEST=memory-no-store TRILIUM_PORT=8082 TRILIUM_DATA_DIR=data-e2e ELECTRON_IS_DEV=0 playwright test"
},
"dependencies": {
"@electron/remote": "2.1.3",
"better-sqlite3": "^12.0.0",
@@ -15,6 +31,7 @@
},
"devDependencies": {
"@types/electron-squirrel-startup": "1.0.2",
"@triliumnext/commons": "workspace:*",
"@triliumnext/server": "workspace:*",
"copy-webpack-plugin": "13.0.1",
"electron": "37.4.0",
@@ -27,174 +44,5 @@
"@electron-forge/maker-zip": "7.8.3",
"@electron-forge/plugin-auto-unpack-natives": "7.8.3",
"prebuild-install": "^7.1.1"
},
"config": {
"forge": "./electron-forge/forge.config.ts"
},
"scripts": {
"start-prod": "nx build desktop && cross-env TRILIUM_DATA_DIR=data TRILIUM_RESOURCE_DIR=dist TRILIUM_PORT=37841 electron dist/main.js"
},
"license": "AGPL-3.0-only",
"author": {
"name": "Trilium Notes Team",
"email": "contact@eliandoran.me",
"url": "https://github.com/TriliumNext/Notes"
},
"nx": {
"name": "desktop",
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"defaultConfiguration": "production",
"configurations": {
"production": {
"minify": true,
"sourcemap": false
},
"development": {
"minify": false,
"sourcemap": true
}
},
"options": {
"main": "apps/desktop/src/electron-main.ts",
"outputPath": "apps/desktop/dist",
"outputFileName": "main.js",
"tsConfig": "apps/desktop/tsconfig.app.json",
"platform": "node",
"external": [
"electron",
"@electron/remote",
"better-sqlite3",
"./xhr-sync-worker.js"
],
"format": [
"cjs"
],
"thirdParty": true,
"declaration": false,
"esbuildOptions": {
"splitting": false,
"loader": {
".css": "text"
}
},
"assets": [
{
"glob": "**/*",
"input": "apps/server/dist/node_modules",
"output": "node_modules"
},
{
"glob": "**/*",
"input": "apps/desktop/node_modules/@electron/remote",
"output": "node_modules/@electron/remote"
},
{
"glob": "**/*",
"input": "apps/server/dist/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "packages/share-theme/src/templates",
"output": "share-theme/templates"
},
{
"glob": "**/*",
"input": "apps/desktop/src/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "apps/server/dist/public",
"output": "public"
},
{
"glob": "xhr-sync-worker.js",
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
"output": ""
}
],
"declarationRootDir": "apps/desktop/src"
}
},
"rebuild-deps": {
"executor": "nx:run-commands",
"dependsOn": [
"build"
],
"defaultConfiguration": "default",
"cache": false,
"configurations": {
"default": {
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist"
},
"nixos": {
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist $(nix-shell -p electron_35 --run \"electron --version\")"
}
}
},
"serve": {
"executor": "nx:run-commands",
"dependsOn": [
"rebuild-deps"
],
"defaultConfiguration": "default",
"configurations": {
"default": {
"command": "electron main.cjs",
"cwd": "{projectRoot}/dist"
},
"nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
"cwd": ".",
"forwardAllArgs": false
}
}
},
"serve-nodir": {
"executor": "nx:run-commands",
"dependsOn": [
"rebuild-deps"
],
"defaultConfiguration": "default",
"configurations": {
"default": {
"command": "electron main.cjs",
"cwd": "{projectRoot}/dist"
},
"nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
"cwd": ".",
"forwardAllArgs": false
}
}
},
"electron-forge:make": {
"dependsOn": [
"build",
"rebuild-deps"
],
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge make dist"
},
"electron-forge:package": {
"dependsOn": [
"build",
"rebuild-deps"
],
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge package dist"
},
"electron-forge:start": {
"dependsOn": [
"build",
"rebuild-deps"
],
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm TRILIUM_DATA_DIR=./data electron-forge start dist"
}
}
}
}
}

View File

@@ -1,10 +1,5 @@
import { defineConfig, devices } from '@playwright/test';
require('dotenv').config({
path: __dirname + "/" + ".env"
});
/**
* See https://playwright.dev/docs/test-configuration.
*/
@@ -14,6 +9,8 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
testDir: "e2e",
outputDir: "test-output",
projects: [
{
name: "chromium",

View File

@@ -0,0 +1,46 @@
import { join } from "path";
import BuildHelper from "../../../scripts/build-utils";
import originalPackageJson from "../package.json" with { type: "json" };
import { writeFileSync } from "fs";
const build = new BuildHelper("apps/desktop");
async function main() {
await build.buildBackend([ "src/main.ts"]);
// Copy assets.
build.copy("src/assets", "assets/");
build.copy("/apps/server/src/assets", "assets/");
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
// Copy node modules dependencies
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote" ]);
build.copy("/node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js", "xhr-sync-worker.js");
// Integrate the client.
build.triggerBuildAndCopyTo("apps/client", "public/");
build.deleteFromOutput("public/webpack-stats.json");
generatePackageJson();
}
function generatePackageJson() {
const { version, author, license, description, dependencies, devDependencies } = originalPackageJson;
const packageJson = {
name: "trilium",
main: "main.cjs",
version, author, license, description,
dependencies: {
"better-sqlite3": dependencies["better-sqlite3"],
},
devDependencies: {
electron: devDependencies.electron
},
config: {
forge: "../electron-forge/forge.config.ts"
}
};
writeFileSync(join(build.outDir, "package.json"), JSON.stringify(packageJson, null, "\t"), "utf-8");
}
main();

View File

@@ -25,6 +25,12 @@ async function main() {
// needed for excalidraw export https://github.com/zadam/trilium/issues/4271
electron.app.commandLine.appendSwitch("enable-experimental-web-platform-features");
electron.app.commandLine.appendSwitch("lang", options.getOptionOrNull("formattingLocale") ?? "en");
// Disable smooth scroll if the option is set
const smoothScrollEnabled = options.getOptionOrNull("smoothScrollEnabled");
if (smoothScrollEnabled === "false") {
electron.app.commandLine.appendSwitch("disable-smooth-scrolling");
}
// Electron 36 crashes with "Using GTK 2/3 and GTK 4 in the same process is not supported" on some distributions.
// See https://github.com/electron/electron/issues/46538 for more info.

View File

@@ -15,65 +15,8 @@
"@types/mime-types": "^3.0.0",
"@types/yargs": "^17.0.33"
},
"nx": {
"name": "dump-db",
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"defaultConfiguration": "production",
"options": {
"platform": "node",
"outputPath": "apps/dump-db/dist",
"format": [
"cjs"
],
"bundle": false,
"main": "apps/dump-db/src/main.ts",
"tsConfig": "apps/dump-db/tsconfig.app.json",
"assets": [
"apps/dump-db/src/assets"
],
"esbuildOptions": {
"sourcemap": true,
"outExtension": {
".js": ".js"
}
}
},
"configurations": {
"development": {},
"production": {
"esbuildOptions": {
"sourcemap": false,
"outExtension": {
".js": ".js"
}
}
}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"dependsOn": [
"build"
],
"options": {
"buildTarget": "dump-db:build",
"runBuildTargetDependencies": false
},
"configurations": {
"development": {
"buildTarget": "dump-db:build:development"
},
"production": {
"buildTarget": "dump-db:build:production"
}
}
}
}
"scripts": {
"dev": "tsx src/main.ts",
"build": "esbuild --platform=node --format=cjs --outdir=dist src/main.ts"
}
}

View File

@@ -1,7 +0,0 @@
TRILIUM_DATA_DIR=../data
TRILIUM_INTEGRATION_TEST=memory-no-store
TRILIUM_PORT=37741
# Paths are relative to dist root
DOCS_ROOT=../../../docs
USER_GUIDE_ROOT=../../../apps/server/src/assets/doc_notes/en/User Guide

View File

@@ -15,120 +15,8 @@
"electron": "37.4.0",
"fs-extra": "11.3.1"
},
"nx": {
"name": "edit-docs",
"implicitDependencies": [
"server"
],
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"options": {
"main": "apps/edit-docs/src/edit-docs.ts",
"outputPath": "apps/edit-docs/dist",
"tsConfig": "apps/edit-docs/tsconfig.app.json",
"platform": "node",
"additionalEntryPoints": [
"apps/edit-docs/src/edit-demo.ts"
],
"external": [
"electron",
"@electron/remote",
"better-sqlite3",
"./xhr-sync-worker.js"
],
"format": [
"cjs"
],
"minify": false,
"thirdParty": true,
"declaration": false,
"esbuildOptions": {
"splitting": false,
"loader": {
".css": "text"
}
},
"assets": [
{
"glob": "**/*",
"input": "apps/server/dist/node_modules",
"output": "node_modules"
},
{
"glob": "**/*",
"input": "apps/server/dist/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "apps/server/dist/public",
"output": "public"
},
{
"glob": "xhr-sync-worker.js",
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
"output": ""
}
],
"declarationRootDir": "apps/edit-docs/src"
}
},
"rebuild-deps": {
"executor": "nx:run-commands",
"dependsOn": [
"build"
],
"defaultConfiguration": "default",
"cache": true,
"configurations": {
"default": {
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist"
},
"nixos": {
"command": "cross-env DEBUG=* tsx scripts/electron-rebuild.mts {projectRoot}/dist $(nix-shell -p electron_35 --run \"electron --version\")"
}
}
},
"edit-docs": {
"executor": "nx:run-commands",
"dependsOn": [
"rebuild-deps"
],
"defaultConfiguration": "default",
"configurations": {
"default": {
"command": "electron edit-docs.cjs",
"cwd": "{projectRoot}/dist"
},
"nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/edit-docs.cjs\"",
"cwd": ".",
"forwardAllArgs": false
}
}
},
"edit-demo": {
"executor": "nx:run-commands",
"dependsOn": [
"rebuild-deps"
],
"defaultConfiguration": "default",
"configurations": {
"default": {
"command": "electron edit-demo.cjs",
"cwd": "{projectRoot}/dist"
},
"nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/edit-demo.cjs\"",
"cwd": ".",
"forwardAllArgs": false
}
}
}
}
}
"scripts": {
"edit-docs": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data TRILIUM_INTEGRATION_TEST=memory-no-store DOCS_ROOT=../../../docs USER_GUIDE_ROOT=\"../../server/src/assets/doc_notes/en/User Guide\" tsx ../../scripts/electron-start.mts src/edit-docs.ts",
"edit-demo": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data TRILIUM_INTEGRATION_TEST=memory-no-store DOCS_ROOT=../../../docs USER_GUIDE_ROOT=\"../../server/src/assets/doc_notes/en/User Guide\" tsx ../../scripts/electron-start.mts src/edit-demo.ts"
}
}

View File

@@ -1,3 +0,0 @@
TRILIUM_INTEGRATION_TEST=memory
TRILIUM_PORT=8082
TRILIUM_DATA_DIR=apps/server/spec/db

View File

@@ -2,21 +2,10 @@
"name": "@triliumnext/server-e2e",
"version": "0.0.1",
"private": true,
"nx": {
"name": "server-e2e",
"implicitDependencies": [
"client",
"server"
],
"targets": {
"e2e": {
"dependsOn": [
"server:build"
]
}
}
"scripts": {
"e2e": "playwright test"
},
"devDependencies": {
"dotenv": "17.2.1"
"dotenv": "17.2.2"
}
}

View File

@@ -1,68 +1,44 @@
import { defineConfig, devices } from '@playwright/test';
import { nxE2EPreset } from '@nx/playwright/preset';
import { workspaceRoot } from '@nx/devkit';
require('dotenv').config({
path: __dirname + "/" + ".env"
});
import { join } from 'path';
// For CI, you may want to set BASE_URL to the deployed application.
const port = process.env['TRILIUM_PORT'];
const port = process.env['TRILIUM_PORT'] ?? "8082";
const baseURL = process.env['BASE_URL'] || `http://127.0.0.1:${port}`;
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
...nxE2EPreset(__filename, { testDir: './src' }),
testDir: "src",
reporter: [["list"], ["html", { outputFolder: "test-output" }]],
outputDir: "test-output",
retries: 3,
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
baseURL,
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Run your local dev server before starting the tests */
webServer: !process.env.TRILIUM_DOCKER ? {
command: 'pnpm server:start-prod',
command: 'pnpm start-prod-no-dir',
url: baseURL,
reuseExistingServer: !process.env.CI,
cwd: workspaceRoot,
cwd: join(__dirname, "../server"),
env: {
TRILIUM_DATA_DIR: "spec/db",
TRILIUM_PORT: port,
TRILIUM_INTEGRATION_TEST: "memory"
},
timeout: 5 * 60 * 1000
} : undefined,
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
// {
// name: "firefox",
// use: { ...devices["Desktop Firefox"] },
// },
// {
// name: "webkit",
// use: { ...devices["Desktop Safari"] },
// },
// Uncomment for mobile browsers support
/* {
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
}, */
// Uncomment for branded browsers
/* {
name: 'Microsoft Edge',
use: { ...devices['Desktop Edge'], channel: 'msedge' },
},
{
name: 'Google Chrome',
use: { ...devices['Desktop Chrome'], channel: 'chrome' },
} */
],
}
]
});

View File

@@ -79,7 +79,7 @@ test("Tabs are restored in right order", async ({ page, context }) => {
// Refresh the page and check the order.
await app.goto( { preserveTabs: true });
await expect(app.getTab(0)).toContainText("Code notes");
await expect(app.getTab(0)).toContainText("Code notes", { timeout: 15_000 });
await expect(app.getTab(1)).toContainText("Text notes");
await expect(app.getTab(2)).toContainText("Mermaid");

View File

@@ -1,6 +0,0 @@
TRILIUM_ENV=dev
TRILIUM_DATA_DIR=./apps/server/spec/db
TRILIUM_RESOURCE_DIR=./apps/server/dist
TRILIUM_PUBLIC_SERVER=http://localhost:4200
TRILIUM_PORT=8086
TRILIUM_INTEGRATION_TEST=edit

View File

@@ -1,3 +0,0 @@
TRILIUM_ENV=dev
TRILIUM_RESOURCE_DIR=./apps/server/dist
TRILIUM_PUBLIC_SERVER=http://localhost:4200

View File

@@ -1,4 +0,0 @@
TRILIUM_ENV=dev
TRILIUM_DATA_DIR=./apps/server/data
TRILIUM_RESOURCE_DIR=./apps/server/dist
TRILIUM_PUBLIC_SERVER=http://localhost:4200

View File

@@ -1,3 +0,0 @@
TRILIUM_ENV=production
TRILIUM_DATA_DIR=./apps/server/data
TRILIUM_PORT=8082

View File

@@ -1,4 +0,0 @@
TRILIUM_ENV=dev
TRILIUM_DATA_DIR=./spec/db
TRILIUM_PUBLIC_SERVER=http://localhost:4200
TRILIUM_INTEGRATION_TEST=memory

View File

@@ -1,4 +1,4 @@
FROM node:22.18.0-bullseye-slim AS builder
FROM node:22.19.0-bullseye-slim AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:22.18.0-bullseye-slim
FROM node:22.19.0-bullseye-slim
# Install only runtime dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \

View File

@@ -1,4 +1,4 @@
FROM node:22.18.0-alpine AS builder
FROM node:22.19.0-alpine AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:22.18.0-alpine
FROM node:22.19.0-alpine
# Install runtime dependencies
RUN apk add --no-cache su-exec shadow

View File

@@ -1,4 +1,4 @@
FROM node:22.18.0-alpine AS builder
FROM node:22.19.0-alpine AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:22.18.0-alpine
FROM node:22.19.0-alpine
# Create a non-root user with configurable UID/GID
ARG USER=trilium
ARG UID=1001

View File

@@ -1,4 +1,4 @@
FROM node:22.18.0-bullseye-slim AS builder
FROM node:22.19.0-bullseye-slim AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:22.18.0-bullseye-slim
FROM node:22.19.0-bullseye-slim
# Create a non-root user with configurable UID/GID
ARG USER=trilium
ARG UID=1001

View File

@@ -3,11 +3,38 @@
"version": "0.98.1",
"description": "The server-side component of TriliumNext, which exposes the client via the web, allows for sync and provides a REST API for both internal and external use.",
"private": true,
"main": "./src/main.ts",
"scripts": {
"dev": "cross-env NODE_ENV=development TRILIUM_ENV=dev TRILIUM_DATA_DIR=data TRILIUM_RESOURCE_DIR=src tsx watch --ignore '../client/node_modules/.vite-temp' ./src/main.ts",
"start-no-dir": "cross-env NODE_ENV=development TRILIUM_ENV=dev TRILIUM_RESOURCE_DIR=src tsx watch --ignore '../client/node_modules/.vite-temp' ./src/main.ts",
"edit-integration-db": "cross-env NODE_ENV=development TRILIUM_PORT=8086 TRILIUM_ENV=dev TRILIUM_DATA_DIR=spec/db TRILIUM_INTEGRATION_TEST=edit TRILIUM_RESOURCE_DIR=src tsx watch --ignore '../client/node_modules/.vite-temp' ./src/main.ts",
"build": "tsx scripts/build.ts",
"package": "pnpm build && bash scripts/build-server.sh",
"test": "vitest",
"test-build": "vitest --config vitest.build.config.mts",
"start-prod": "cross-env TRILIUM_DATA_DIR=data pnpm start-prod-no-dir",
"start-prod-no-dir": "pnpm build && cross-env TRILIUM_ENV=production TRILIUM_PORT=8082 node dist/main.cjs",
"circular-deps": "dpdm -T src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular",
"docker-build-debian": "pnpm build && docker build . -t triliumnext-debian -f Dockerfile",
"docker-build-alpine": "pnpm build && docker build . -t triliumnext-alpine -f Dockerfile.alpine",
"docker-build-rootless-debian": "pnpm build && docker build . -t triliumnext-rootless-debian -f Dockerfile.rootless",
"docker-build-rootless-alpine": "pnpm build && docker build . -t triliumnext-rootless-alpine -f Dockerfile.alpine.rootless",
"docker-start-debian": "pnpm docker-build-debian && docker run -p 8081:8080 triliumnext-debian",
"docker-start-alpine": "pnpm docker-build-alpine && docker run -p 8081:8080 triliumnext-alpine",
"docker-start-rootless-debian": "pnpm docker-build-rootless-debian && docker run -p 8081:8080 triliumnext-rootless-debian",
"docker-start-rootless-alpine": "pnpm docker-build-rootless-alpine && docker run -p 8081:8080 triliumnext-rootless-alpine"
},
"dependencies": {
"better-sqlite3": "12.2.0"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.61.0",
"@braintree/sanitize-url": "7.1.1",
"@electron/remote": "2.1.3",
"@preact/preset-vite": "2.10.2",
"@triliumnext/commons": "workspace:*",
"@triliumnext/express-partial-content": "workspace:*",
"@triliumnext/turndown-plugin-gfm": "workspace:*",
"@types/archiver": "6.0.3",
"@types/better-sqlite3": "7.6.13",
"@types/cls-hooked": "4.3.9",
@@ -38,16 +65,11 @@
"@types/turndown": "5.0.5",
"@types/ws": "8.18.1",
"@types/xml2js": "0.4.14",
"express-http-proxy": "2.1.1",
"@anthropic-ai/sdk": "0.60.0",
"@braintree/sanitize-url": "7.1.1",
"@triliumnext/commons": "workspace:*",
"@triliumnext/express-partial-content": "workspace:*",
"@triliumnext/turndown-plugin-gfm": "workspace:*",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
"axios": "1.11.0",
"bindings": "1.5.0",
"bootstrap": "5.3.8",
"chardet": "2.1.0",
"cheerio": "1.1.2",
"chokidar": "4.0.3",
@@ -55,7 +77,7 @@
"compression": "1.8.1",
"cookie-parser": "1.4.7",
"csrf-csrf": "3.2.2",
"dayjs": "1.11.14",
"dayjs": "1.11.18",
"debounce": "2.2.0",
"debug": "4.4.1",
"ejs": "3.1.10",
@@ -64,8 +86,9 @@
"electron-window-state": "5.0.3",
"escape-html": "1.0.3",
"express": "5.1.0",
"express-http-proxy": "2.1.1",
"express-openid-connect": "^2.17.1",
"express-rate-limit": "8.0.1",
"express-rate-limit": "8.1.0",
"express-session": "1.18.2",
"file-uri-to-path": "2.0.0",
"fs-extra": "11.3.1",
@@ -74,7 +97,7 @@
"html2plaintext": "2.1.4",
"http-proxy-agent": "7.0.2",
"https-proxy-agent": "7.0.6",
"i18next": "25.4.2",
"i18next": "25.5.2",
"i18next-fs-backend": "2.6.0",
"image-type": "6.0.0",
"ini": "5.0.0",
@@ -105,272 +128,9 @@
"tmp": "0.2.5",
"turndown": "7.2.1",
"unescape": "1.0.1",
"vite": "^7.1.3",
"ws": "8.18.3",
"xml2js": "0.6.2",
"yauzl": "3.2.0"
},
"nx": {
"name": "server",
"implicitDependencies": [
"share-theme"
],
"targets": {
"serve": {
"executor": "@nx/js:node",
"dependsOn": [
{
"projects": [
"client"
],
"target": "serve"
},
"build-without-client"
],
"continuous": true,
"options": {
"buildTarget": "server:build-without-client:development",
"runBuildTargetDependencies": false
}
},
"serve-nodir": {
"executor": "@nx/js:node",
"dependsOn": [
{
"projects": [
"client"
],
"target": "serve"
},
"build-without-client"
],
"continuous": true,
"options": {
"buildTarget": "server:build-without-client:development",
"runBuildTargetDependencies": false
}
},
"edit-integration-db": {
"executor": "@nx/js:node",
"dependsOn": [
{
"projects": [
"client"
],
"target": "serve"
},
"build-without-client"
],
"continuous": true,
"options": {
"buildTarget": "server:build-without-client:development",
"runBuildTargetDependencies": false
}
},
"package": {
"dependsOn": [
"build"
],
"command": "bash apps/server/scripts/build-server.sh"
},
"start-prod": {
"dependsOn": [
"build"
],
"command": "node apps/server/dist/main.cjs"
},
"docker-build": {
"dependsOn": [
"build"
],
"options": {
"cwd": "{projectRoot}"
},
"executor": "nx:run-commands",
"defaultConfiguration": "alpine",
"configurations": {
"debian": {
"command": "docker build . -t triliumnext-debian -f Dockerfile"
},
"alpine": {
"command": "docker build . -t triliumnext-alpine -f Dockerfile.alpine"
},
"rootless-debian": {
"command": "docker build . -t triliumnext-rootless-debian -f Dockerfile.rootless"
},
"rootless-alpine": {
"command": "docker build . -t triliumnext-rootless-alpine -f Dockerfile.alpine.rootless"
}
}
},
"docker-start": {
"dependsOn": [
"docker-build"
],
"executor": "nx:run-commands",
"defaultConfiguration": "alpine",
"configurations": {
"debian": {
"command": "docker run -p 8081:8080 triliumnext-debian"
},
"alpine": {
"command": "docker run -p 8081:8080 triliumnext-alpine"
},
"rootless-debian": {
"command": "docker run -p 8081:8080 triliumnext-rootless-debian"
},
"rootless-alpine": {
"command": "docker run -p 8081:8080 triliumnext-rootless-alpine"
}
}
},
"build-without-client": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"options": {
"main": "apps/server/src/main.ts",
"outputPath": "apps/server/dist",
"outputFileName": "main.js",
"tsConfig": "apps/server/tsconfig.app.json",
"platform": "node",
"format": [
"cjs"
],
"esbuildOptions": {
"loader": {
".css": "text",
".ejs": "text"
}
},
"declarationRootDir": "apps/server/src",
"minify": false,
"sourcemap": true,
"assets": [
{
"glob": "**/*",
"input": "apps/server/src/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "packages/share-theme/src/templates",
"output": "share-theme/templates"
}
]
}
},
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"dependsOn": [
"^build",
"client:build"
],
"defaultConfiguration": "production",
"configurations": {
"production": {
"minify": true,
"sourcemap": false
},
"development": {
"minify": false,
"sourcemap": true
}
},
"options": {
"main": "apps/server/src/main.ts",
"outputPath": "apps/server/dist",
"tsConfig": "apps/server/tsconfig.app.json",
"platform": "node",
"external": [
"electron",
"@electron/remote",
"better-sqlite3",
"./xhr-sync-worker.js"
],
"format": [
"cjs"
],
"declarationRootDir": "apps/server/src",
"thirdParty": true,
"declaration": false,
"esbuildOptions": {
"splitting": false,
"loader": {
".css": "text",
".ejs": "text"
}
},
"additionalEntryPoints": [
"apps/server/src/docker_healthcheck.ts"
],
"assets": [
{
"glob": "**/*",
"input": "apps/server/src/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "packages/share-theme/src/templates",
"output": "share-theme/templates"
},
{
"glob": "**/*",
"input": "apps/client/dist",
"output": "public",
"ignore": [
"webpack-stats.json"
]
},
{
"glob": "**/*",
"input": "apps/server/node_modules/better-sqlite3",
"output": "node_modules/better-sqlite3"
},
{
"glob": "**/*",
"input": "apps/server/node_modules/bindings",
"output": "node_modules/bindings"
},
{
"glob": "**/*",
"input": "apps/server/node_modules/file-uri-to-path",
"output": "node_modules/file-uri-to-path"
},
{
"glob": "xhr-sync-worker.js",
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
"output": ""
}
]
}
},
"test-build": {
"dependsOn": [
"build"
],
"command": "vitest --config {projectRoot}/vitest.build.config.mts"
},
"circular-deps": {
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
}
}
},
"exports": {
"./package.json": "./package.json",
"./src/*": "./src/*",
".": {
"development": "./src/main.ts",
"types": "./dist/main.d.ts",
"import": "./dist/main.js",
"default": "./dist/main.js"
}
},
"types": "./dist/main.d.ts",
"module": "./dist/main.js",
"main": "./dist/main.js"
}
}

View File

@@ -0,0 +1,22 @@
import BuildHelper from "../../../scripts/build-utils";
const build = new BuildHelper("apps/server");
async function main() {
await build.buildBackend([ "src/main.ts", "src/docker_healthcheck.ts" ])
// Copy assets
build.copy("src/assets", "assets/");
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
// Copy node modules dependencies
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path" ]);
build.copy("/node_modules/jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js", "xhr-sync-worker.js");
build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css");
// Integrate the client.
build.triggerBuildAndCopyTo("apps/client", "public/");
build.deleteFromOutput("public/webpack-stats.json");
}
main();

View File

@@ -30,5 +30,5 @@ describe("etapi/import", () => {
.expect(201);
expect(response.body.note.title).toStrictEqual("Journal");
expect(response.body.branch.parentNoteId).toStrictEqual("root");
});
}, 10_000);
});

View File

@@ -74,7 +74,10 @@
"zoom-out": "Pomniejsz",
"zoom-in": "Powiększ",
"print-active-note": "Drukuj aktywną notatkę",
"toggle-full-screen": "Przełącz pełny ekran"
"toggle-full-screen": "Przełącz pełny ekran",
"cut-into-note": "Wycina zaznaczony tekst i tworzy podrzędną notatkę z tym tekstem",
"edit-readonly-note": "Edytuj notatkę tylko do odczytu",
"other": "Inne"
},
"keyboard_action_names": {
"zoom-in": "Powiększ",

View File

@@ -65,6 +65,7 @@ const ALLOWED_OPTIONS = new Set<OptionNames>([
"monthlyBackupEnabled",
"motionEnabled",
"shadowsEnabled",
"smoothScrollEnabled",
"backdropEffectsEnabled",
"maxContentWidth",
"compressImages",

View File

@@ -3,7 +3,6 @@ import path from "path";
import express from "express";
import { getResourceDir, isDev } from "../services/utils.js";
import type serveStatic from "serve-static";
import proxy from "express-http-proxy";
import { existsSync } from "fs";
const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<unknown, Record<string, unknown>>>) => {
@@ -17,17 +16,22 @@ const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOp
};
async function register(app: express.Application) {
const srcRoot = path.join(__dirname, "..");
const srcRoot = path.join(__dirname, "..", "..");
const resourceDir = getResourceDir();
if (isDev) {
const publicUrl = process.env.TRILIUM_PUBLIC_SERVER;
if (!publicUrl) {
throw new Error("Missing TRILIUM_PUBLIC_SERVER");
}
app.use("/" + assetUrlFragment + `/@fs`, proxy(publicUrl, {
proxyReqPathResolver: (req) => "/" + assetUrlFragment + `/@fs` + req.url
}));
if (process.env.NODE_ENV === "development") {
const { createServer: createViteServer } = await import("vite");
const vite = await createViteServer({
server: { middlewareMode: true },
appType: "custom",
cacheDir: path.join(srcRoot, "../../.cache/vite"),
base: `/${assetUrlFragment}/`,
root: path.join(srcRoot, "../client")
});
app.use(`/${assetUrlFragment}/`, (req, res, next) => {
req.url = `/${assetUrlFragment}` + req.url;
vite.middlewares(req, res, next);
});
} else {
const publicDir = path.join(resourceDir, "public");
if (!existsSync(publicDir)) {

View File

@@ -5,7 +5,7 @@ import attributeService from "../services/attributes.js";
import config from "../services/config.js";
import optionService from "../services/options.js";
import log from "../services/log.js";
import { isDev, isElectron } from "../services/utils.js";
import { isDev, isElectron, isWindows11 } from "../services/utils.js";
import protectedSessionService from "../services/protected_session.js";
import packageJson from "../../package.json" with { type: "json" };
import assetPath from "../services/asset_path.js";
@@ -42,7 +42,7 @@ function index(req: Request, res: Response) {
platform: process.platform,
isElectron,
hasNativeTitleBar: isElectron && options.nativeTitleBarVisible === "true",
hasBackgroundEffects: isElectron && options.backgroundEffects === "true",
hasBackgroundEffects: isElectron && isWindows11 && options.backgroundEffects === "true",
mainFontSize: parseInt(options.mainFontSize),
treeFontSize: parseInt(options.treeFontSize),
detailFontSize: parseInt(options.detailFontSize),

View File

@@ -1,3 +1,4 @@
import assetPath from "./asset_path.js";
import { isDev } from "./utils.js";
export default assetPath + "/src";
export default isDev ? assetPath : assetPath + "/src";

View File

@@ -1,7 +1,14 @@
import packageJson from "../../package.json" with { type: "json" };
import { isDev } from "./utils";
/**
* The URL prefix for assets (e.g. `assets/v1.2.3`).
*/
export const assetUrlFragment = `assets/v${packageJson.version}`;
const assetPath = isDev ? `http://localhost:4200/${assetUrlFragment}` : assetUrlFragment;
/**
* Similar to the {@link assetUrlFragment}, but on dev mode it also contains the `/src` suffix.
*/
const assetPath = isDev ? assetUrlFragment + "/src" : assetUrlFragment;
export default assetPath;

View File

@@ -6,7 +6,7 @@ import path from "path";
import mimeTypes from "mime-types";
import mdService from "./markdown.js";
import packageInfo from "../../../package.json" with { type: "json" };
import { getContentDisposition, escapeHtml, getResourceDir } from "../utils.js";
import { getContentDisposition, escapeHtml, getResourceDir, isDev } from "../utils.js";
import protectedSessionService from "../protected_session.js";
import sanitize from "sanitize-filename";
import fs from "fs";
@@ -21,7 +21,6 @@ import type AttributeMeta from "../meta/attribute_meta.js";
import type BBranch from "../../becca/entities/bbranch.js";
import type { Response } from "express";
import type { NoteMetaFile } from "../meta/note_meta.js";
import cssContent from "@triliumnext/ckeditor5/content.css";
type RewriteLinksFn = (content: string, noteMeta: NoteMeta) => string;
@@ -515,7 +514,11 @@ ${markdownContent}`;
return;
}
archive.append(cssContent, { name: cssMeta.dataFileName });
const cssFile = isDev
? path.join(__dirname, "../../../../../node_modules/ckeditor5/dist/ckeditor5-content.css")
: path.join(getResourceDir(), "ckeditor5-content.css");
archive.append(fs.readFileSync(cssFile, "utf-8"), { name: cssMeta.dataFileName });
}
try {

View File

@@ -155,7 +155,7 @@ const defaultOptions: DefaultOption[] = [
{ name: "motionEnabled", value: "true", isSynced: false },
{ name: "shadowsEnabled", value: "true", isSynced: false },
{ name: "backdropEffectsEnabled", value: "true", isSynced: false },
{ name: "smoothScrollEnabled", value: "true", isSynced: false },
// Internationalization
{ name: "locale", value: "en", isSynced: true },

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