Compare commits

..

636 Commits

Author SHA1 Message Date
Elian Doran 6db9918702
Update README.md 2025-06-24 23:24:23 +07:00
Elian Doran f234e3543d
Update README.md 2025-06-22 10:43:15 +07:00
Elian Doran f529cfd928
chore(i18n): update Spanish translation (#2371) 2025-06-21 23:18:28 +07:00
hasecilu 26d0ba04ab
chore(i18n): update Spanish translation 2025-06-21 11:19:57 +07:00
hasecilu f85ef444f4
fix(script): paths on translation script 2025-06-21 11:19:55 +07:00
Elian Doran f97bd77117
Update README.md 2025-06-21 17:11:25 +07:00
Elian Doran 962066fd60
chore(scripts): add script used to port issues 2025-06-21 16:38:22 +07:00
Elian Doran ff917e93c9
e2e(desktop): fix process leak 2025-06-21 14:28:57 +07:00
Elian Doran a7091779b7
e2e(desktop): create empty project 2025-06-21 14:23:47 +07:00
Elian Doran 520b862551
Revert "feat(ci): experiment with building Windows on same runner"
This reverts commit ce305f0f45.
2025-06-21 14:18:41 +07:00
Elian Doran 5fbd34c7c1
Revert "chore(forge): add an arbitrary wait to see if it solves some CI errors"
This reverts commit 4f6729857b.
2025-06-21 14:18:36 +07:00
Elian Doran c827b20e26
Revert "chore(forge): add a pre-make wait as well"
This reverts commit a150047432.
2025-06-21 14:18:31 +07:00
Elian Doran a150047432
chore(forge): add a pre-make wait as well 2025-06-21 13:40:47 +07:00
Elian Doran 4f6729857b
chore(forge): add an arbitrary wait to see if it solves some CI errors 2025-06-21 13:19:35 +07:00
Elian Doran ce305f0f45
feat(ci): experiment with building Windows on same runner 2025-06-21 10:57:45 +07:00
Elian Doran 3a50ffede1
refactor(forge): add types to config 2025-06-21 10:46:12 +07:00
Elian Doran 41e88614d7
docs(guide): improve and update supported syntax for Markdown import 2025-06-21 09:49:26 +07:00
Elian Doran 002c567ae9
feat(ci): display trace if playwright fails in docker 2025-06-21 09:26:05 +07:00
Elian Doran a9c07af402
fix(client): failing with duplicate modules 2025-06-21 09:00:07 +07:00
Elian Doran 3048c39877
Revert "chore(env): remove 4GB override"
This reverts commit 3280123414.
2025-06-21 01:20:41 +07:00
Elian Doran a95dc14d24
test(import/zip): test silverbullet import 2025-06-21 01:09:03 +07:00
Elian Doran 24c82fa7b6
fix(import/zip): regression in normal import 2025-06-20 22:13:36 +07:00
Elian Doran 02a0624e8f
feat(import/zip): support image with absolute path 2025-06-20 21:59:11 +07:00
Elian Doran e6e276a0cf
feat(import/markdown): support image via wikilink 2025-06-20 21:40:23 +07:00
Elian Doran 8d90231f76
fix(import/markdown): support wikilinks in other elements other than paragraphs 2025-06-20 21:00:39 +07:00
Elian Doran 4a40b22c9a
feat(import/zip): support root-relative paths 2025-06-20 20:56:25 +07:00
Elian Doran 79b3b92ec9
chore(client): skip dynamic imports in cyclic check 2025-06-20 18:32:32 +07:00
Elian Doran ae1a4fbbf6
fix(client): cyclic dependency server <-> ws 2025-06-20 18:32:20 +07:00
Elian Doran 3190aa6fe6
feat(import/markdown): start parsing wikilinks 2025-06-20 18:28:08 +07:00
Elian Doran 1c3cd9e7ca
chore(deps): update vitest monorepo to v3.2.4 (#2364) 2025-06-20 15:52:42 +07:00
Elian Doran c61713333d
Merge remote-tracking branch 'origin/develop' into renovate/vitest-monorepo 2025-06-20 15:35:27 +07:00
Elian Doran a861defbee
feat(server): lint for trailing slashes in sync URL and extra slashes… (#2345) 2025-06-20 10:55:46 +07:00
Elian Doran ea0b570910
chore(deps): update dependency @playwright/test to v1.53.1 (#2362) 2025-06-20 09:51:22 +07:00
renovate[bot] 8d46ab3806
chore(deps): update dependency @playwright/test to v1.53.1 2025-06-20 06:28:12 +07:00
Elian Doran 058b3f8241
chore(deps): update nx monorepo to v21.2.1 (#2363) 2025-06-20 08:27:22 +07:00
Elian Doran 57a688b6b6
fix(deps): update codemirror to v6.0.2 (#2365) 2025-06-20 08:26:16 +07:00
Elian Doran ad18d853e2
chore(deps): update dependency electron to v36.5.0 (#2366) 2025-06-20 08:25:12 +07:00
renovate[bot] 8f5be936a7
chore(deps): update dependency electron to v36.5.0 2025-06-20 02:47:20 +07:00
renovate[bot] f2c02f869e
fix(deps): update codemirror to v6.0.2 2025-06-20 02:46:24 +07:00
renovate[bot] cf28777119
chore(deps): update nx monorepo to v21.2.1 2025-06-20 02:43:32 +07:00
Elian Doran cca8504796
refactor(client): circular dep in utils 2025-06-19 22:44:02 +07:00
Elian Doran 09391a92e5
refactor(client): circular dep: toast <-> ws 2025-06-19 22:29:44 +07:00
Elian Doran 50db8ef9c3
refactor(client): circular dependency in fnote 2025-06-19 22:23:17 +07:00
Elian Doran 8ab21f3bab
chore(client): add tool to view circular deps 2025-06-19 22:03:01 +07:00
Elian Doran efdf79feaa
refactor(client): circular dependency causing test failure 2025-06-19 22:01:01 +07:00
Elian Doran 9418055b69
chore(test): fix imports 2025-06-19 21:30:42 +07:00
Elian Doran 5070633257
refactor(client): remove unnecessary files 2025-06-19 21:30:26 +07:00
Elian Doran 764917562e
chore(test): fix set up 2025-06-19 21:30:10 +07:00
Elian Doran 54290a1373
chore(tsconfig): remove src for forge config 2025-06-19 21:01:23 +07:00
Elian Doran 0325bee425
feat(ckeditor): fallback to GPL if license key fails 2025-06-19 19:38:10 +07:00
Elian Doran e280968271
feat(ckeditor): allow use of GPL license 2025-06-19 18:55:02 +07:00
Elian Doran b809137c93
refactor(text): move license key management to config 2025-06-19 18:47:41 +07:00
Elian Doran 248f6d6a7d
refactor(forge): switch to TypeScript 2025-06-19 15:45:39 +07:00
Elian Doran 0744a85421
feat(flake): handle StartupWMClass 2025-06-19 15:34:09 +07:00
Elian Doran e5a6f53f98
Revert "feat(flake); set up overlay"
This reverts commit 751de1d43c.
2025-06-19 12:55:04 +07:00
Elian Doran 751de1d43c
feat(flake); set up overlay 2025-06-19 12:49:26 +07:00
perf3ct 2704b1546b
feat(server): fix lint type errors for normalizing server URLs 2025-06-18 21:07:12 +07:00
perf3ct acd68817e9
feat(server): fix lint type errors for normalizing server URLs 2025-06-18 20:46:11 +07:00
Elian Doran 8b841c5aa7
feat(autocomplete): support specifying path when creating a new note (#2342) 2025-06-18 23:14:46 +07:00
Elian Doran a37af29c6c
fix(client): link to API documentation (#2356) 2025-06-18 23:10:00 +07:00
Elian Doran ade0d4bb1a
fix(website): update README.md instructions (#2357) 2025-06-18 23:08:56 +07:00
Arne Keller d79c491777
fix(website): update README.md instructions 2025-06-18 19:08:24 +07:00
FliegendeWurst f9cf542e66 fix(client): link to API documentation
Fixes https://github.com/TriliumNext/Notes/issues/1779
2025-06-18 17:58:29 +07:00
Elian Doran 609ed6274e
fix(code): respect user font selection 2025-06-18 17:42:03 +07:00
Elian Doran 7faaefee20
feat(help): display contextual button text snippets 2025-06-18 15:35:56 +07:00
Elian Doran 578310a1c0
test(ckeditor5): add check for translation override 2025-06-18 14:44:56 +07:00
Elian Doran 93f544a221
feat(text-snippets): rewrite CKEditor translations 2025-06-18 13:13:59 +07:00
Elian Doran 9c80ab22af
chore(client): use production ckeditor key for dev as well 2025-06-18 11:06:57 +07:00
Elian Doran 7decbc34c7
chore(deps): update dependency @types/node to v22.15.32 (#2347) 2025-06-18 11:02:07 +07:00
Elian Doran e606276f19
chore(deps): update typescript-eslint monorepo to v8.34.1 (#2348) 2025-06-18 11:01:42 +07:00
SiriusXT a78e4d7a58 chore(i18n): update path selection prompt 2025-06-18 14:28:43 +07:00
renovate[bot] 426c9a377a
chore(deps): update typescript-eslint monorepo to v8.34.1 2025-06-18 06:16:35 +07:00
renovate[bot] d2a3ae2a6f
chore(deps): update dependency @types/node to v22.15.32 2025-06-18 06:15:35 +07:00
renovate[bot] fbd6b7d22b
chore(deps): update vitest monorepo to v3.2.4 2025-06-18 06:11:31 +07:00
Elian Doran a42375931f
chore(deps): update vitest monorepo to v3.2.4 (#2349) 2025-06-18 09:08:41 +07:00
Elian Doran d894bff2a0
fix(deps): update dependency bootstrap to v5.3.7 (#2350) 2025-06-18 09:07:48 +07:00
Elian Doran 4515ed600c
fix(deps): update dependency mind-elixir to v4.6.1 (#2351) 2025-06-18 09:07:10 +07:00
Elian Doran 4df3cdd975
chore(deps): update dependency openai to v5.5.1 (#2352) 2025-06-18 09:06:31 +07:00
SiriusXT 5fc0a0460d Merge branch 'develop' into note-create 2025-06-18 11:21:32 +07:00
renovate[bot] 74851a859b
chore(deps): update dependency openai to v5.5.1 2025-06-18 02:56:07 +07:00
renovate[bot] 9bb94513ca
fix(deps): update dependency mind-elixir to v4.6.1 2025-06-18 02:55:10 +07:00
renovate[bot] bb74aa7976
fix(deps): update dependency bootstrap to v5.3.7 2025-06-18 02:54:19 +07:00
renovate[bot] 12318b38ee
chore(deps): update vitest monorepo to v3.2.4 2025-06-18 02:53:25 +07:00
Jon Fuller bb70c2a3fa
Merge branch 'develop' into fix/lint-server-url 2025-06-17 16:00:36 +07:00
perf3ct b47180a219
feat(server): create unit tests for normalizing server URL, and fix logic based on feedback 2025-06-17 21:32:27 +07:00
Elian Doran 4eb64357a1
chore(website): add macos screenshots 2025-06-17 23:39:25 +07:00
perf3ct 0fe89115d1
feat(server): lint for trailing slashes in sync URL and extra slashes in customRequestHandler 2025-06-17 19:37:40 +07:00
Elian Doran 330932adde
fix(website): script errors due to double body 2025-06-17 22:08:34 +07:00
Elian Doran b9daca5b9c
website: favicon / dark mode / more content (#2343) 2025-06-17 21:38:24 +07:00
Elian Doran c6c48d84ab
flake: fix Electron version, fix Wayland support, fix source filter (#2329) 2025-06-17 21:33:45 +07:00
Elian Doran 2c87721953
docs(guide): premium features 2025-06-17 21:01:01 +07:00
Elian Doran 45a446d0f6
chore(text): set up production key 2025-06-17 20:55:38 +07:00
Elian Doran 40086434ec
refactor(subtree): duplicate bx in icon name 2025-06-17 20:55:38 +07:00
Elian Doran bde3f0a55c
fix(nx): revert infinite watching causing severe issues with the daemon
See https://github.com/TriliumNext/Notes/pull/2285#issuecomment-2981118813
2025-06-17 20:55:37 +07:00
Elian Doran 9adae105e2
docs: regex search / Nix flake / restore dev docs (#2341) 2025-06-17 20:53:43 +07:00
Elian Doran 60dbf9dd67
Text snippets (#2344) 2025-06-17 19:29:53 +07:00
Elian Doran 374309a40c
fix(templates): description displayed on separate lines 2025-06-17 19:21:21 +07:00
Elian Doran dcccb5ad30
feat(builtin_templates): add icon for text snippets 2025-06-17 19:21:21 +07:00
Elian Doran 47eaee8b70
feat(builtin_templates): add description field for text snippets 2025-06-17 19:21:21 +07:00
Elian Doran 9687a9d8ff
refactor(note_types): separate user templates into own method 2025-06-17 19:21:21 +07:00
Elian Doran fa11295693
feat(templates): add support for built-in templates 2025-06-17 19:21:21 +07:00
Elian Doran 7e399cc10c
feat(text-snippets): support description 2025-06-17 19:21:20 +07:00
Elian Doran 59e0857bb5
feat(text-snippets): add default icon for templates 2025-06-17 19:21:20 +07:00
Elian Doran 4f9bd970af
feat(text-snippets): better reaction to removing templates 2025-06-17 19:21:20 +07:00
Elian Doran 3e40a35c19
feat(text-snippets): reload when a new template is added 2025-06-17 19:21:20 +07:00
Elian Doran 97799bfacc
feat(text-snippets): handle renames by refreshing the editor 2025-06-17 19:21:20 +07:00
Elian Doran fb1a74a96d
feat(text-snippets): debounce updates to avoid duplication 2025-06-17 19:21:20 +07:00
Elian Doran 9f82e0a6d6
refactor(text-snippets): use a map instead of an object 2025-06-17 19:21:20 +07:00
Elian Doran 421e125882
feat(text-snippets): handle content changes 2025-06-17 19:21:20 +07:00
Elian Doran 17ede00fb2
feat(text-snippets): reload editors when templates change 2025-06-17 19:21:20 +07:00
Elian Doran 502638bae7
feat(text-snippets): add toolbar entry 2025-06-17 19:21:20 +07:00
Elian Doran af8a905150
feat(text-snippets): basic integration 2025-06-17 19:21:20 +07:00
FliegendeWurst 6c2a228267 fix(website): feature block descriptions 2025-06-17 18:10:56 +07:00
FliegendeWurst f9ecfd1ad0 feat(website): dark mode 2025-06-17 18:10:56 +07:00
FliegendeWurst 2c3cbcb1f9 website: refactor feature blocks, add more 2025-06-17 18:10:56 +07:00
FliegendeWurst d1583ca091 fix(website): normal link styling 2025-06-17 18:10:56 +07:00
FliegendeWurst 3c21d97a8a fix(website): page titles 2025-06-17 18:10:56 +07:00
FliegendeWurst 8f7468cd60 website: link to distribution packages 2025-06-17 18:10:56 +07:00
FliegendeWurst 6668e639d5 fix(website): proper favicon 2025-06-17 18:10:56 +07:00
SiriusXT bcc689cae3 Merge branch 'develop' into note-create 2025-06-17 21:18:06 +07:00
SiriusXT 20173d544b feat(autocomple): support specifying path when creating a new note 2025-06-17 20:41:23 +07:00
Arne Keller e90d4cf86f docs: add section on Nix flake 2025-06-17 13:05:20 +07:00
Arne Keller 1de02b85b3 chore(docs): restore dev docs 2025-06-17 13:05:20 +07:00
FliegendeWurst dab43d9372 docs: mention escaped backslashes in regex search 2025-06-17 13:05:20 +07:00
Elian Doran 3b579a3b7b
Slash commands (#2336) 2025-06-17 12:49:26 +07:00
Elian Doran a4a5e0bdf0
refactor(ckeditor5): class name 2025-06-16 23:52:16 +07:00
Elian Doran 9cb227c1ca
fix(context menu): not auto-closing on mobile (#2331) 2025-06-16 23:49:41 +07:00
Elian Doran c4c7321f60
fix(slash): layout on legacy theme 2025-06-16 23:06:34 +07:00
Elian Doran cce27900b8
feat(slash): add a few aliases 2025-06-16 22:57:47 +07:00
Elian Doran 32f4d7be39
fix(slash): adding links not focusing box 2025-06-16 22:52:54 +07:00
Elian Doran bbeb4e25f3
refactor(ckeditor): use vite environment variables for inspector 2025-06-16 22:01:05 +07:00
Elian Doran 88f78f3e32
feat(slash): better icons for admonitions 2025-06-16 21:42:31 +07:00
Adorian Doran 36581ba882 style(next)/dialogs/note type chooser: stop highlighting the default item 2025-06-16 21:30:34 +07:00
Elian Doran 1a64b3ce8e
feat(slash): alignment 2025-06-16 21:21:42 +07:00
Elian Doran 32ee75ea43
feat(slash): markdown import 2025-06-16 21:14:46 +07:00
Elian Doran 06ebe0a9b3
feat(slash): include page break 2025-06-16 21:11:03 +07:00
Elian Doran 6069518749
feat(slash): include note 2025-06-16 20:58:42 +07:00
Elian Doran c09a9aa7d3
feat(slash): insert math 2025-06-16 20:55:02 +07:00
Elian Doran 13afcb8a49
feat(slash): insert internal link 2025-06-16 20:48:01 +07:00
Elian Doran 9bfff03cff
feat(slash): insert date/time 2025-06-16 20:42:55 +07:00
Adorian Doran 8780debc90 style(next)/dialogs/note type chooser: improve appearance 2025-06-16 20:31:57 +07:00
Elian Doran 6f386f50ff
chore(slash): change title of admonitions 2025-06-16 20:22:37 +07:00
Elian Doran 43eff08004
feat(slash): insert footnote command 2025-06-16 20:21:54 +07:00
Elian Doran 3a0b616800
feat(slash): admonition types 2025-06-16 20:15:44 +07:00
Adorian Doran cb65d02dc6 style(next)/quick search: avoid text shadow for the focused search result items 2025-06-16 20:11:38 +07:00
Adorian Doran bef485c676 style: switch to the new link style 2025-06-16 20:01:41 +07:00
Adorian Doran 9e4d350848 style(next)/launcher calendar: fix the hover state for the month field 2025-06-16 19:46:20 +07:00
Adorian Doran ccc7a8010e style(next)/combo box: properly handle the hover state when the dropdown arrow is not defined 2025-06-16 19:44:07 +07:00
Adorian Doran 7269c46de3 style(next): note attachments page: tweak header font size 2025-06-16 19:32:05 +07:00
Elian Doran 178df38377
chore(text): increase mention list max height 2025-06-16 19:25:06 +07:00
Elian Doran 1201f7138a
style(next): slash commands layout 2025-06-16 19:24:42 +07:00
Elian Doran 9ff4be8871
feat(text): set up slash commands 2025-06-16 19:24:23 +07:00
Adorian Doran 0a17fb586c Merge branch 'develop' of https://github.com/TriliumNext/Notes into develop 2025-06-16 19:08:08 +07:00
Adorian Doran 1f8713f57e style(next): tweak the note icon selection button 2025-06-16 19:07:56 +07:00
Elian Doran 507fc17701
feat(text): set up license key for development 2025-06-16 18:38:06 +07:00
Elian Doran 26ad428b24
Merge branch 'jshprentz/develop' into develop 2025-06-16 17:51:22 +07:00
Elian Doran 26af3a9d68
Highlight Elixir in code notes (#2327) 2025-06-16 17:50:51 +07:00
Elian Doran 8ecf6ad78e
feat(codemirror): add elixir support 2025-06-16 17:48:20 +07:00
SiriusXT 45977efd5f fix(context menu): not auto-closing on mobile 2025-06-16 17:21:44 +07:00
FliegendeWurst c0b746e03f fix(desktop): proper icon path
The previous value points to a file that no longer exists in the
release builds. This file also only exists in the flake build.
2025-06-16 08:34:46 +07:00
FliegendeWurst a13e4d5d79 fix(flake): set resourceDir explicitly 2025-06-16 08:28:44 +07:00
FliegendeWurst e8f2f0f577 feat(flake): Wayland support 2025-06-16 08:08:04 +07:00
FliegendeWurst f56ba7295a chore(flake): note differences to electron-forge build 2025-06-16 08:04:43 +07:00
FliegendeWurst 8cdb1859e3 fix(flake): use same Electron version as normal build
Also fix the source filtering logic to work properly.
2025-06-16 07:51:26 +07:00
Joel Shprentz 6bfc78e148 Merge branch 'develop' of https://github.com/jshprentz/TriliumNextNotes into develop 2025-06-16 02:28:50 +07:00
Joel Shprentz cc97ec332d Codemirror does not support Elixir. Remove Elixir from the list. 2025-06-16 02:25:57 +07:00
Joel Shprentz f2e4dad318
Merge branch 'TriliumNext:develop' into develop 2025-06-15 22:12:08 +07:00
Joel Shprentz 9c45e9fa1a Support highlighting Elixir code notes. 2025-06-16 01:54:31 +07:00
Elian Doran e1e2afc3cd
docs(release): sync description with GitHub 2025-06-16 00:14:52 +07:00
Elian Doran b9ae226569
chore(release): bump version 2025-06-15 23:32:26 +07:00
Elian Doran e23e5227a4
docs(user): improve documentation on sharing 2025-06-15 23:32:26 +07:00
Elian Doran dd647dbb0b
docs(release): v0.95.0 2025-06-15 23:32:26 +07:00
Elian Doran 04794c38d8
fix(forge): locales with dash on non-mac 2025-06-15 21:27:53 +07:00
Elian Doran 8d868b77d0
feat(forge): English locale on Linux 2025-06-15 21:16:02 +07:00
Elian Doran 3435f20baa
feat(forge): remove unused locales on macOS 2025-06-15 21:06:01 +07:00
Elian Doran 7427507aca
fix(forge): adapt removing lproj on macOS 2025-06-15 20:49:07 +07:00
Elian Doran 3d7784ca18
Revert "fix(desktop): use plugin to clean up languages"
This reverts commit cbcc219f1f.
2025-06-15 19:57:48 +07:00
Elian Doran 0414500c17
Revert "fix(forge): English locale still missing"
This reverts commit 385ebb486e.
2025-06-15 19:57:37 +07:00
Elian Doran 385ebb486e
fix(forge): English locale still missing 2025-06-15 19:30:22 +07:00
Elian Doran cbcc219f1f
fix(desktop): use plugin to clean up languages 2025-06-15 19:06:06 +07:00
Elian Doran 58e2bc2b6c
Merge pull request #2324 from TriliumNext/renovate/major-ckeditor5-config-packages
chore(deps): update ckeditor5 config packages to v11 (major)
2025-06-15 18:51:19 +07:00
Elian Doran c1e53c09d6
Merge pull request #2305 from TriliumNext/renovate/mime-types-3.x-lockfile
chore(deps): update dependency @types/mime-types to v3.0.1
2025-06-15 18:50:20 +07:00
Elian Doran f7a6a10c7a
Merge branch 'develop' into renovate/mime-types-3.x-lockfile 2025-06-15 18:08:24 +07:00
Elian Doran e739aee95f
Merge pull request #2318 from TriliumNext/renovate/eslint-monorepo
chore(deps): update eslint monorepo to v9.29.0
2025-06-15 18:01:49 +07:00
Elian Doran 1b6c2cdad1
Merge pull request #2320 from TriliumNext/renovate/tsx-4.x-lockfile
fix(deps): update dependency tsx to v4.20.3
2025-06-15 18:01:16 +07:00
Elian Doran e56d7e3aa0
Merge pull request #2306 from TriliumNext/renovate/typescript-eslint-monorepo
chore(deps): update dependency @typescript-eslint/parser to v8.34.0
2025-06-15 18:00:34 +07:00
Elian Doran 354c4ff8db
Merge pull request #2317 from TriliumNext/renovate/axios-1.x
chore(deps): update dependency axios to v1.10.0
2025-06-15 17:59:39 +07:00
Elian Doran 681b61e166
Merge pull request #2316 from TriliumNext/renovate/vite-plugin-static-copy-3.x
chore(deps): update dependency vite-plugin-static-copy to v3.0.2
2025-06-15 17:59:11 +07:00
Elian Doran 74c6c5d8cf
Merge pull request #2315 from TriliumNext/renovate/svelte-5.x-lockfile
chore(deps): update dependency svelte to v5.34.3
2025-06-15 17:57:53 +07:00
Elian Doran 8746df4907
Merge pull request #2314 from TriliumNext/renovate/lint-staged-16.x-lockfile
chore(deps): update dependency lint-staged to v16.1.2
2025-06-15 17:57:02 +07:00
Elian Doran acbd4c5c3e
Merge pull request #2288 from TriliumNext/renovate/vitest-monorepo
chore(deps): update vitest monorepo to v3.2.3
2025-06-15 17:56:34 +07:00
renovate[bot] edd1571c4e
chore(deps): update ckeditor5 config packages to v11 2025-06-15 14:43:06 +07:00
renovate[bot] 8289dff3e8
fix(deps): update dependency tsx to v4.20.3 2025-06-15 14:42:18 +07:00
renovate[bot] d654bb5d16
chore(deps): update eslint monorepo to v9.29.0 2025-06-15 14:41:28 +07:00
renovate[bot] b985796139
chore(deps): update dependency axios to v1.10.0 2025-06-15 14:40:40 +07:00
renovate[bot] 99fd7b275b
chore(deps): update dependency @typescript-eslint/parser to v8.34.0 2025-06-15 14:39:53 +07:00
renovate[bot] 161671e112
chore(deps): update vitest monorepo to v3.2.3 2025-06-15 14:39:00 +07:00
renovate[bot] 02a0173c91
chore(deps): update dependency vite-plugin-static-copy to v3.0.2 2025-06-15 14:38:03 +07:00
renovate[bot] 4f7069a7c3
chore(deps): update dependency svelte to v5.34.3 2025-06-15 14:37:15 +07:00
renovate[bot] fa4e17c273
chore(deps): update dependency lint-staged to v16.1.2 2025-06-15 14:36:22 +07:00
renovate[bot] 7edd20d179
chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-15 14:35:27 +07:00
Elian Doran f423d3a145
Merge pull request #2322 from TriliumNext/feature/fix_nx_ignore
Feature/fix nx ignore
2025-06-15 17:10:54 +07:00
Elian Doran 3aba961f5e
Revert "test(server): skip failing test"
This reverts commit 82be37b4af.
2025-06-15 17:09:23 +07:00
Elian Doran d2cfb5e94f
chore(nx): have website:typecheck depend on build 2025-06-15 17:03:02 +07:00
Elian Doran cb22c2cc7f
chore(pnpm): align only built dependencies 2025-06-15 16:42:18 +07:00
Elian Doran 3280123414
chore(env): remove 4GB override 2025-06-15 16:11:39 +07:00
Elian Doran 82be37b4af
test(server): skip failing test 2025-06-15 16:03:12 +07:00
Elian Doran 0e51a35842
feat(ci): run test-build in affected 2025-06-15 15:42:33 +07:00
Elian Doran 86dc3762b6
fix(nx): patch asset copying to ignore .gitignore 2025-06-15 15:41:10 +07:00
Elian Doran edc341f024
test(server): ensure build artifacts exist 2025-06-15 15:40:54 +07:00
Elian Doran f2bef879ce
chore(nx): remove dist & node_modules from .nxignore 2025-06-15 14:52:27 +07:00
Elian Doran a5c6a8e0a9
Revert "test(server): disable file parallelism to avoid CI failures"
This reverts commit 17f27e520c.
2025-06-15 14:41:51 +07:00
Elian Doran 641d2b0527
test(server): skip streaming tests 2025-06-15 14:41:29 +07:00
Elian Doran e7d129f2f7
chore(renovate): disable bun 2025-06-15 14:41:29 +07:00
Elian Doran bfc778f002
chore(test): use verbose reporter 2025-06-15 14:28:47 +07:00
Elian Doran 8d2d5504dd
test(server): skip memory leak test to see if it breaks the CI 2025-06-15 14:17:18 +07:00
Elian Doran 17f27e520c
test(server): disable file parallelism to avoid CI failures 2025-06-15 14:00:54 +07:00
Elian Doran f8c1dabfd5
Revert "chore(test): skip test breaking the CI"
This reverts commit f3b6817aa7.
2025-06-15 13:59:56 +07:00
Elian Doran f3b6817aa7
chore(test): skip test breaking the CI 2025-06-15 13:46:13 +07:00
Elian Doran 61d405da68
chore(ci): run server in sequential tests 2025-06-15 13:01:37 +07:00
Elian Doran d4be6b1de0
chore(renovate): disable HTML manager
Hopefully to reduce the time it takes for renovate to process the repo.
2025-06-15 12:49:08 +07:00
Elian Doran d199fc322e
refactor(client): set up manual chunk for CKEditor 2025-06-15 12:41:03 +07:00
Elian Doran 42ccdd1646
test(server): try to use threads instead of forks to avoid ELIFECYCLE issues in the CI 2025-06-15 12:40:53 +07:00
Elian Doran 185652fb6e
chore(deps): sync package lock 2025-06-15 12:06:43 +07:00
Elian Doran 4a579863d1
refactor(deps): remove unnecessary root dependencies 2025-06-15 12:00:59 +07:00
Elian Doran 882b2418d6
chore(renovate): group ckeditor5 config packages 2025-06-15 11:59:52 +07:00
Elian Doran 1dce202d21
test(server): try to reduce number to avoid CI crashing 2025-06-15 11:58:03 +07:00
Elian Doran 2d5754c005
refactor(client,server): remove redundant libraries directory 2025-06-15 11:56:50 +07:00
Elian Doran 9f694fad8b
fix(forge): missing English locale on Windows 2025-06-15 11:38:14 +07:00
Elian Doran 9a2264c7f6
chore(forge): make the removed locales list more compact 2025-06-15 11:20:18 +07:00
Elian Doran 77497dbc28
chore(forge): fail build if locale is missing 2025-06-15 11:12:01 +07:00
Elian Doran 1848d5dc96
chore(types): type missing in the CI 2025-06-14 23:54:19 +07:00
Elian Doran 6f5c66f324
chore(types): fix errors in website 2025-06-14 23:40:41 +07:00
Elian Doran 296c95e800
chore(nx): configure for SvelteKit 2025-06-14 22:40:45 +07:00
Elian Doran 6416c6b947
Merge branch 'feature/landing_page' into develop 2025-06-14 21:41:19 +07:00
Elian Doran 30320f6d84
fix(website): download links for server 2025-06-14 21:39:22 +07:00
Elian Doran b58d0f6663
style(website): slight adjustments to sizes 2025-06-14 18:02:10 +07:00
Elian Doran 56dc1b7e49
feat(website): bigger download button 2025-06-14 17:59:43 +07:00
Elian Doran cef832fda4
fix(website/download): fix URLs for download 2025-06-14 17:55:38 +07:00
Elian Doran 7066fc3092
feat(website/download): section for paid hosting 2025-06-14 17:14:33 +07:00
Elian Doran 235f7c8aec
feat(website/download): section for server downloads 2025-06-14 17:06:51 +07:00
Elian Doran f0ce728982
refactor(website): improve types 2025-06-14 16:40:50 +07:00
Elian Doran be7fe9610f
feat(website/download): improved descriptions 2025-06-14 16:38:26 +07:00
Elian Doran 49cd0d44ac
feat(website/download): improved platform titles 2025-06-14 16:34:51 +07:00
Elian Doran 45a053dfbf
feat(website/download): reorganize desktop downloads 2025-06-14 16:29:40 +07:00
Elian Doran cf47abe849
refactor(website/download): read version number from package.json 2025-06-14 14:34:31 +07:00
Elian Doran 6e97b08256
fix(website/download): use proper download links 2025-06-14 14:33:17 +07:00
Elian Doran 668706a6d9
feat(website/download): integrate architecture selector 2025-06-14 14:24:59 +07:00
Elian Doran 68dc81ac96
feat(website): integrate download matrix for server 2025-06-14 14:14:58 +07:00
Elian Doran 58a099dbc7
Merge pull request #2312 from TriliumNext/doc
docs: ✏️ Fix MFA Readme link
2025-06-14 14:14:10 +07:00
JYC333 740e65a791
Merge pull request #2307 from TriliumNext/renovate/tsx-4.x-lockfile
fix(deps): update dependency tsx to v4.20.3
2025-06-14 13:09:24 +07:00
JYC333 d3aabadb66
Merge branch 'develop' into renovate/tsx-4.x-lockfile 2025-06-14 12:58:08 +07:00
JYC333 571c93c610
Merge pull request #2266 from TriliumNext/renovate/eslint-monorepo
fix(deps): update eslint monorepo to v9.29.0
2025-06-14 12:57:37 +07:00
Jin 168f9b54ba docs: ✏️ Fix MFA Readme link 2025-06-14 12:54:58 +07:00
Elian Doran 65e207648b
feat(website): basic platform detection 2025-06-14 13:45:44 +07:00
Elian Doran cb6cb97326
feat(website): place download now button in header as well 2025-06-14 13:35:31 +07:00
Elian Doran 450fa5311c
feat(website/download): add basic server section 2025-06-14 13:31:52 +07:00
Elian Doran 4edd701c29
feat(website/download): add a basic architecture selector 2025-06-14 13:25:19 +07:00
Elian Doran 630aade97a
feat(website): start a download section 2025-06-14 13:18:34 +07:00
Elian Doran c97098d60d
chore(website): change hero section 2025-06-14 12:45:34 +07:00
Elian Doran 7d0a555999
fix(website): z-index of header 2025-06-14 12:38:31 +07:00
Elian Doran 2c4d0d800c
feat(website): simplify features highlights section 2025-06-14 12:36:32 +07:00
Elian Doran c7ed63a0e3
fix(website): alignment of footer 2025-06-14 12:31:41 +07:00
Elian Doran 3cc69849db
feat(website): add a nice gradient 2025-06-14 12:24:23 +07:00
Elian Doran 32ef25a868
feat(website): add a basic final call-to-action 2025-06-14 12:17:37 +07:00
Elian Doran 437e8875d8
feat(website): move screenshot to the right 2025-06-14 12:15:08 +07:00
Elian Doran 652e91586e
feat(website): add a basic intro into the features 2025-06-14 12:13:23 +07:00
Elian Doran 4946a50ebd
chore(vscode): add tailwind extension to recommendations 2025-06-14 12:02:43 +07:00
Elian Doran c69ecb768b
feat(website): add a basic feature highlights section 2025-06-14 12:02:22 +07:00
Elian Doran 88abb895ff
feat(website): add a basic copyright footer 2025-06-14 11:59:11 +07:00
Elian Doran 09dd8f9828
feat(website): slight improvements to header 2025-06-14 11:49:45 +07:00
Elian Doran 7dd6c1a6cc
feat(website): basic screenshot 2025-06-14 11:42:02 +07:00
Elian Doran 9eda9b1399
feat(website): basic hero section 2025-06-14 11:30:13 +07:00
Elian Doran e66e70f7f2
feat(website): very simple header 2025-06-14 11:13:40 +07:00
Elian Doran 77213a0dbe
chore(website): add inlang to ignore 2025-06-14 11:13:30 +07:00
Elian Doran 73b1aa7c6c
chore(vscode): add svelte to recommendations 2025-06-14 11:12:49 +07:00
Elian Doran c43a7df781
chore(website): create empty SvelteKit project 2025-06-14 11:05:38 +07:00
renovate[bot] 97d6d1a5d2
fix(deps): update eslint monorepo to v9.29.0 2025-06-14 07:45:46 +07:00
Elian Doran 925a674de6
Merge pull request #2311 from TriliumNext/renovate/eslint-linter-browserify-9.x
fix(deps): update dependency eslint-linter-browserify to v9.29.0
2025-06-14 10:39:41 +07:00
Elian Doran 297aef788c
Merge pull request #2298 from TriliumNext/renovate/codemirror
fix(deps): update codemirror
2025-06-14 10:36:34 +07:00
renovate[bot] 6ecc67c8cf
fix(deps): update dependency eslint-linter-browserify to v9.29.0 2025-06-14 07:24:48 +07:00
renovate[bot] 0f4b26fa6f
fix(deps): update codemirror 2025-06-14 07:23:54 +07:00
Elian Doran 8704b01f4c
Merge pull request #2299 from TriliumNext/renovate/nx-monorepo
chore(deps): update nx monorepo to v21.2.0
2025-06-14 10:19:53 +07:00
Elian Doran 5c3a69984a
Merge remote-tracking branch 'origin/develop' into renovate/tsx-4.x-lockfile 2025-06-14 10:15:47 +07:00
renovate[bot] 1dec510120
chore(deps): update nx monorepo to v21.2.0 2025-06-13 13:28:06 +07:00
Elian Doran 4da7fd2c87
Merge pull request #2297 from TriliumNext/renovate/tsx-4.x
chore(deps): update dependency tsx to v4.20.3
2025-06-13 16:23:51 +07:00
renovate[bot] 143209307f
chore(deps): update dependency tsx to v4.20.3 2025-06-13 12:45:44 +07:00
renovate[bot] e300f9087a
fix(deps): update dependency tsx to v4.20.3 2025-06-13 12:42:01 +07:00
Elian Doran 26afab03ce
Merge pull request #2303 from TriliumNext/sirius_patch
fix(image): ensure images opened in a new tab can be activated
2025-06-13 15:31:27 +07:00
Elian Doran 2f3275474f
Merge pull request #2304 from TriliumNext/sirius_patch2
fix(ckeditor): restore focus after importing markdown
2025-06-13 15:28:04 +07:00
SiriusXT 41eff6de17 fix(ckeditor): restore focus after importing markdown 2025-06-13 19:40:49 +07:00
SiriusXT 07bb0644b2 fix(image): ensure images opened in a new tab can be activated 2025-06-13 18:08:15 +07:00
Elian Doran 5b6c003888
Merge pull request #2300 from TriliumNext/renovate/tsx-4.x-lockfile
fix(deps): update dependency tsx to v4.20.2
2025-06-13 09:21:05 +07:00
renovate[bot] d93e66665c
fix(deps): update dependency tsx to v4.20.2 2025-06-13 00:42:33 +07:00
Elian Doran f0c696d6fd
Merge pull request #2281 from TriliumNext/fix/show-warning-when-rosetta-2
fix(client): show warning/error when app is using Rosetta 2 translation (running wrong arch)
2025-06-12 23:05:54 +07:00
Elian Doran 9713864bb6
refactor(cpu_dialog): more mentions of rosetta 2025-06-12 22:53:15 +07:00
Elian Doran c6c59c63bb
feat(cpu_dialog): add actual Windows CPU detection 2025-06-12 22:49:31 +07:00
Elian Doran 0a81e26e7e
feat(cpu_dialog): fix icon alignment & add message for windows 2025-06-12 22:43:30 +07:00
Elian Doran 2c032d54c1
chore(cpu_dialog): simplify the dialog 2025-06-12 22:30:33 +07:00
Elian Doran a635131f47
style(cpu_dialog): use modal-footer 2025-06-12 22:23:37 +07:00
Elian Doran 8edbbe27f8
refactor(client,server): rebrand to CPU arch warnings 2025-06-12 22:16:57 +07:00
Elian Doran db3c008c07
fix(server): headers sent twice 2025-06-12 22:07:50 +07:00
Elian Doran a20a06c994
refactor(client): simplify DOM 2025-06-12 22:06:34 +07:00
Elian Doran fe1f4a4294
refactor(client): remove contrasting title bar 2025-06-12 22:03:22 +07:00
Elian Doran b4d2d21620
refactor(client): relocate architecture mismatch checks 2025-06-12 22:00:51 +07:00
Elian Doran f6bba436f4
Revert "fix(client): also move the logic from the server to the client lol"
This reverts commit e401c8c930.
2025-06-12 21:46:27 +07:00
Elian Doran 79aae8fb43
Merge remote-tracking branch 'origin/develop' into fix/show-warning-when-rosetta-2 2025-06-12 21:32:50 +07:00
Elian Doran c34c0c49db
Merge pull request #2293 from TriliumNext/tree
fix(tree): Unexpected collapse after hoisting a note
2025-06-12 18:34:09 +07:00
Elian Doran 59296f3045
fix(server): crashes due to req.body being undefined 2025-06-12 15:01:35 +07:00
SiriusXT df68ed33bc Merge branch 'develop' into tree 2025-06-12 19:46:11 +07:00
SiriusXT 369eb7844d fix(tree): Unexpected collapse after hoisting a note 2025-06-12 19:36:54 +07:00
Elian Doran b3270ae7c8
chore(server): enable source map for serve 2025-06-12 12:08:45 +07:00
Elian Doran 995de2b740
feat(editor): hide balloon toolbar when in code block 2025-06-12 10:12:17 +07:00
Elian Doran be718ce4e0
refactor(client): remove unnecessary widget 2025-06-12 09:23:30 +07:00
Elian Doran 63ce62a67d
Merge pull request #2290 from TriliumNext/renovate/anthropic-ai-sdk-0.x
chore(deps): update dependency @anthropic-ai/sdk to v0.54.0
2025-06-12 09:20:42 +07:00
Elian Doran fc84c06346
Merge pull request #2292 from TriliumNext/renovate/tsx-4.x-lockfile
fix(deps): update dependency tsx to v4.20.1
2025-06-12 09:20:30 +07:00
renovate[bot] 3ccd5b52fa
fix(deps): update dependency tsx to v4.20.1 2025-06-12 05:54:46 +07:00
renovate[bot] bd3f74c17b
chore(deps): update dependency @anthropic-ai/sdk to v0.54.0 2025-06-12 05:53:56 +07:00
Elian Doran 10e063b39e
Merge pull request #2285 from werererer/fix/nx-project-discovery
fix(build): fix Nx project discovery issue caused by parent .gitignore
2025-06-12 08:46:17 +07:00
Elian Doran 6876337f5f
Merge pull request #2289 from TriliumNext/renovate/preact-10.x
fix(deps): update dependency preact to v10.26.9
2025-06-12 08:45:58 +07:00
Elian Doran c43a654f8e
Merge pull request #2291 from TriliumNext/renovate/tsx-4.x
chore(deps): update dependency tsx to v4.20.1
2025-06-12 08:44:54 +07:00
Elian Doran 40a7890b45
Merge pull request #2287 from TriliumNext/renovate/mime-types-3.x-lockfile
chore(deps): update dependency @types/mime-types to v3.0.1
2025-06-12 08:44:32 +07:00
renovate[bot] b8115fed70
chore(deps): update dependency tsx to v4.20.1 2025-06-12 01:16:04 +07:00
renovate[bot] b1f7a8e932
fix(deps): update dependency preact to v10.26.9 2025-06-12 01:14:34 +07:00
renovate[bot] 5514fbd113
chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-12 01:12:47 +07:00
Elian Doran 2fe34b1b0d
Merge branch 'develop' into fix/nx-project-discovery 2025-06-11 23:58:47 +07:00
Jakob Schlanstedt efb0050e57 fix(build): ensure projects are discovered by ignoring parent .gitignore rules 2025-06-11 22:43:42 +07:00
Elian Doran cfd68f41eb
Merge pull request #2220 from TriliumNext/moveupdown
fix(move_block): move multiple lines up/down
2025-06-11 22:42:10 +07:00
Elian Doran 5090b38f9c
Merge branch 'develop' of https://github.com/TriliumNext/Notes into develop 2025-06-11 22:33:18 +07:00
Elian Doran 57ad6065d8
Merge pull request #2282 from TriliumNext/feat/add-llm-model-unit-tests
feat(unit): add unit tests around LLM model names within outgoing req…
2025-06-11 22:10:36 +07:00
Elian Doran 65b7c2d82f
Merge pull request #2249 from TriliumNext/odic
MFA docs improve
2025-06-11 22:08:21 +07:00
Elian Doran 7863a5e09c
chore: disable source maps 2025-06-11 22:03:25 +07:00
Jin 94e3586555 refactor: 💡 add link in MFA option point to help page 2025-06-11 20:52:07 +07:00
Jin ac14a22a43 chore: 🤖 align the same structure with option page 2025-06-11 20:52:07 +07:00
Jin 14179f3bd3 docs: ✏️ show MFA in-app help page 2025-06-11 20:52:07 +07:00
Jin 988a2b8d29 docs: ✏️ Add custom OAuth issuer docs 2025-06-11 20:52:07 +07:00
JYC333 9c38398ae3
Merge pull request #2278 from TriliumNext/renovate/major-typescript-eslint-monorepo
chore(deps): update typescript-eslint monorepo to v8 (major)
2025-06-11 20:51:13 +07:00
renovate[bot] c4226ea454
chore(deps): update typescript-eslint monorepo to v8 2025-06-11 18:11:06 +07:00
Elian Doran 19668d1931
Merge branch 'develop' of https://github.com/TriliumNext/Notes into develop 2025-06-11 21:08:37 +07:00
Elian Doran 23c45ee219
feat(forge): remove unused locales 2025-06-11 21:08:02 +07:00
Elian Doran ed5505e093
Merge pull request #2271 from TriliumNext/renovate/softprops-action-gh-release-2.x
chore(deps): update softprops/action-gh-release action to v2.3.2
2025-06-11 20:09:52 +07:00
Elian Doran fb9f5a7584
Merge branch 'develop' into renovate/softprops-action-gh-release-2.x 2025-06-11 20:09:30 +07:00
Elian Doran 825c2c1fe9
Merge pull request #2255 from TriliumNext/feature/client_size_optimisation
Client bundle size optimization
2025-06-11 20:06:31 +07:00
Elian Doran 7c2a29edce
chore(deps): deduplicate roughjs 2025-06-11 19:45:36 +07:00
Elian Doran 26f44ea1f8
chore(deps): deduplicate preact 2025-06-11 19:42:44 +07:00
Jon Fuller 6dbdf336aa
Merge branch 'develop' into feat/add-llm-model-unit-tests 2025-06-11 09:41:11 +07:00
Elian Doran 89d32db601
fix(canvas): loading of fonts under dev mode 2025-06-11 19:34:35 +07:00
Elian Doran 65f9b1c4ef
Merge remote-tracking branch 'origin/develop' into feature/client_size_optimisation
; Conflicts:
;	pnpm-lock.yaml
2025-06-11 19:02:34 +07:00
Elian Doran 6596431154
chore(client): force mermaid to avoid double packaging 2025-06-11 18:59:46 +07:00
Elian Doran 9bab8f0bdb
refactor(canvas): remove unnecessary env setup 2025-06-11 18:59:46 +07:00
Elian Doran 892c27c945
chore(canvas): set up cleanup 2025-06-11 18:59:45 +07:00
Elian Doran bb762cfab6
refactor(canvas): remove use of any 2025-06-11 18:59:45 +07:00
Elian Doran 9d296dd692
refactor(canvas): use deferred promise instead of sleep 2025-06-11 18:59:45 +07:00
Elian Doran 0da05a7dbe
refactor(canvas): don't expose API directly 2025-06-11 18:59:45 +07:00
Elian Doran dab9b02990
refactor(canvas): remove unnecessary fragment 2025-06-11 18:59:45 +07:00
Elian Doran 5b4ceb27c5
refactor(canvas): remove unnecessary ref 2025-06-11 18:59:45 +07:00
Elian Doran 51d06ec40d
chore(deps): fix lockfile 2025-06-11 18:59:45 +07:00
renovate[bot] e7285573ac
chore(deps): update dependency openai to v5.3.0 2025-06-11 18:59:45 +07:00
renovate[bot] 509e3284ed
chore(deps): update vitest monorepo to v3.2.3 2025-06-11 18:59:45 +07:00
renovate[bot] c8530bca75
chore(deps): update dependency serve-favicon to v2.5.1 2025-06-11 18:59:45 +07:00
renovate[bot] b27d017174
chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-11 18:59:45 +07:00
Elian Doran 963c7386b1
chore(ci): remove accidental workflow 2025-06-11 18:59:45 +07:00
Elian Doran e032e6f217
refactor(deps): remove package lock from package 2025-06-11 18:59:45 +07:00
Elian Doran 00c5497d93
chore(cI): use pnpm exec instead of pnpx 2025-06-11 18:59:45 +07:00
JYC333 8ed62398fa
Merge pull request #2276 from TriliumNext/renovate/major-happy-dom-monorepo
chore(deps): update dependency happy-dom to v18
2025-06-11 17:00:49 +07:00
renovate[bot] fff87aec87
chore(deps): update dependency happy-dom to v18 2025-06-11 14:35:56 +07:00
JYC333 6ef71b9209
Merge pull request #2274 from TriliumNext/renovate/major-eslint-monorepo
chore(deps): update dependency eslint to v9
2025-06-11 16:32:50 +07:00
perf3ct e401c8c930
fix(client): also move the logic from the server to the client lol 2025-06-11 14:30:33 +07:00
renovate[bot] ba9f034694 chore(deps): update dependency eslint to v9 2025-06-11 16:17:30 +07:00
JYC333 adbc906f9e
Merge pull request #2273 from TriliumNext/renovate/swagger-ui-5.x
chore(deps): update dependency @types/swagger-ui to v5
2025-06-11 16:17:04 +07:00
perf3ct 3041af7fe2
feat(client): also fix translations 2025-06-11 14:13:59 +07:00
perf3ct 23ce896681
feat(client): show warning when running through rosetta 2 2025-06-11 14:04:42 +07:00
renovate[bot] 5249624486
chore(deps): update dependency @types/swagger-ui to v5 2025-06-11 13:23:33 +07:00
JYC333 3f5024dc6d
Merge pull request #2264 from TriliumNext/renovate/playwright-monorepo
chore(deps): update dependency @playwright/test to v1.53.0
2025-06-11 15:20:37 +07:00
renovate[bot] 1afb971877
chore(deps): update dependency @playwright/test to v1.53.0 2025-06-11 09:46:56 +07:00
JYC333 0d605cbc74
Merge pull request #2260 from TriliumNext/renovate/node-22.x
chore(deps): update dependency @types/node to v22.15.31
2025-06-11 11:35:06 +07:00
renovate[bot] cc7c175b45
chore(deps): update dependency @types/node to v22.15.31 2025-06-11 09:10:55 +07:00
JYC333 c6f48d1dc0
Merge pull request #2269 from TriliumNext/renovate/openai-5.x
chore(deps): update dependency openai to v5.3.0
2025-06-11 11:08:55 +07:00
renovate[bot] 3ddde700fb
chore(deps): update dependency openai to v5.3.0 2025-06-11 08:59:30 +07:00
JYC333 22450519a4
Merge pull request #2263 from TriliumNext/renovate/vitest-monorepo
chore(deps): update vitest monorepo to v3.2.3
2025-06-11 10:54:41 +07:00
renovate[bot] 9ece11ae9b
chore(deps): update vitest monorepo to v3.2.3 2025-06-11 08:46:50 +07:00
JYC333 c3796dcdb0
Merge pull request #2262 from TriliumNext/renovate/serve-favicon-2.x
chore(deps): update dependency serve-favicon to v2.5.1
2025-06-11 10:43:18 +07:00
renovate[bot] c0b9bb282c
chore(deps): update dependency serve-favicon to v2.5.1 2025-06-11 08:34:14 +07:00
JYC333 97a904e4b4
Merge pull request #2259 from TriliumNext/renovate/mime-types-3.x-lockfile
chore(deps): update dependency @types/mime-types to v3.0.1
2025-06-11 10:22:37 +07:00
renovate[bot] 411875ac5f
chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-11 07:48:05 +07:00
Elian Doran 7b43cfc563
chore(ci): remove accidental workflow 2025-06-11 10:45:25 +07:00
Elian Doran d6c250111a
chore(ci): trigger nightly when workflow changes 2025-06-11 10:24:27 +07:00
renovate[bot] fdd70e4403
chore(deps): update softprops/action-gh-release action to v2.3.2 2025-06-11 07:17:24 +07:00
Elian Doran bcd7c29db4
refactor(deps): remove package lock from package 2025-06-11 10:07:39 +07:00
Elian Doran d3c2315e2d
chore(cI): use pnpm exec instead of pnpx 2025-06-11 09:24:07 +07:00
Elian Doran a7c115cdf5
chore(canvas): solve type errors 2025-06-10 23:41:15 +07:00
Elian Doran 4bd5644258
refactor(canvas): access to wrapper 2025-06-10 23:40:14 +07:00
Elian Doran 5b98277f3c
chore(canvas): bring back more options 2025-06-10 23:29:36 +07:00
Elian Doran ddbd268a66
chore(canvas): bring back load/save 2025-06-10 23:25:43 +07:00
Elian Doran dd58685455
chore(canvas): bring back scene API 2025-06-10 23:09:36 +07:00
Elian Doran 5ad3d7d077
chore(canvas): missing API endpoint 2025-06-10 22:50:39 +07:00
Elian Doran 3e0f420eec
chore(canvas): reintroduce wrapper 2025-06-10 22:44:11 +07:00
Elian Doran 0f47a4988b
refactor(canvas): proof of concept for preact-based canvas 2025-06-10 22:22:47 +07:00
Elian Doran 0f9fe0367a
chore(client): fix excalidraw erroring out due to react 2025-06-10 21:23:46 +07:00
Elian Doran b2b6654846
refactor(share): removing problematic build step 2025-06-10 21:09:24 +07:00
Elian Doran 83c136174e
chore(client): build dependencies before serving 2025-06-10 21:09:10 +07:00
perf3ct e1e1eb4f51
feat(unit): add unit tests around LLM model names within outgoing requests 2025-06-10 16:27:05 +07:00
Elian Doran 61958a35c2
fix(ci): release breaking due to upstream
See:
- https://github.com/softprops/action-gh-release/issues/627
- https://github.com/softprops/action-gh-release/issues/628
2025-06-10 14:16:42 +07:00
Elian Doran 5558d6e149
chore(ci): try to increase max heap size everywhere 2025-06-10 13:56:56 +07:00
SiriusXT 3a56a16a58 fix: moving tables/blockQuote 2025-06-10 18:50:32 +07:00
Elian Doran e7ab96538c
refactor(share): update package meta 2025-06-10 13:46:33 +07:00
Elian Doran cbc57c3a77
Merge pull request #2243 from TriliumNext/open_in_new_window
fix(link): Unable to open notes in a new window
2025-06-10 13:35:00 +07:00
Elian Doran 2bcaf7d8a8
Merge pull request #2236 from TriliumNext/zen
fix(zen): Show fixed toolbar in Zen mode
2025-06-10 13:18:15 +07:00
Elian Doran db80ebe6dd
Merge pull request #2232 from TriliumNext/ckeditor5-alignment
feat(ckeditor): Add text alignment
2025-06-10 13:15:42 +07:00
Elian Doran 72217253be
Merge pull request #2245 from TriliumNext/renovate/npm-esbuild-vulnerability
chore(deps): update dependency esbuild to ^0.25.0 [security]
2025-06-10 13:10:12 +07:00
Elian Doran 12ef314ab7
Merge pull request #2230 from TriliumNext/tab-row
Tab row scroll
2025-06-10 13:08:51 +07:00
renovate[bot] ce38f47bee
chore(deps): update dependency esbuild to ^0.25.0 [security] 2025-06-10 09:55:49 +07:00
Elian Doran 36f0de888e
Merge pull request #2209 from TriliumNext/feat/llm-unit-tests
feat(llm): add unit tests
2025-06-10 12:52:29 +07:00
SiriusXT e440f31cb5 Merge branch 'develop' into open_in_new_window 2025-06-10 17:51:54 +07:00
Elian Doran 94cd0fc5d1
Merge pull request #2222 from TriliumNext/feature/share_theme
Integrate Trilium Rocks share theme
2025-06-10 12:43:51 +07:00
SiriusXT ee9267775e vitest(link): add Vitest test cases for parseNavigationStateFromUrl 2025-06-10 17:38:05 +07:00
SiriusXT 885cdc8c97 Fix: Unable to open in a new window 2025-06-10 17:25:10 +07:00
Elian Doran 9d347ecf74
Merge remote-tracking branch 'origin/develop' into feature/share_theme
; Conflicts:
;	pnpm-lock.yaml
2025-06-10 08:58:20 +07:00
Elian Doran 23238a6ca0
Merge pull request #2238 from TriliumNext/renovate/react-monorepo
chore(deps): update dependency @types/react to v19.1.7
2025-06-10 08:52:17 +07:00
Elian Doran 9dc067564e
Merge pull request #2239 from TriliumNext/renovate/vitest-monorepo
chore(deps): update vitest monorepo to v3.2.3
2025-06-10 08:51:54 +07:00
Elian Doran 7936e2d124
Merge pull request #2240 from TriliumNext/renovate/openai-5.x
chore(deps): update dependency openai to v5.2.0
2025-06-10 08:51:33 +07:00
Elian Doran 3e73fc03e6
Merge pull request #2241 from TriliumNext/renovate/typescript-eslint-monorepo
chore(deps): update typescript-eslint monorepo to ~8.34.0
2025-06-10 08:51:08 +07:00
renovate[bot] ff19bab496
chore(deps): update typescript-eslint monorepo to ~8.34.0 2025-06-10 01:13:09 +07:00
renovate[bot] 12c4ca5f08
chore(deps): update dependency openai to v5.2.0 2025-06-10 01:11:51 +07:00
renovate[bot] 7b0ab4ee83
chore(deps): update vitest monorepo to v3.2.3 2025-06-10 01:11:10 +07:00
renovate[bot] 1feb4d42b7
chore(deps): update dependency @types/react to v19.1.7 2025-06-10 01:10:24 +07:00
SiriusXT 17ff0e8afc Merge branch 'develop' into ckeditor5-alignment 2025-06-10 09:07:21 +07:00
Elian Doran 7fcbb83dbe
chore(forge): increase heap space for CI 2025-06-09 23:31:56 +07:00
Elian Doran 8ee6c7114c
test(server-e2e): remove test that was no longer relevant 2025-06-09 23:10:19 +07:00
Elian Doran ceb6134af2
fix(client): uninitialized field 2025-06-09 22:40:45 +07:00
Elian Doran 0ff8b7edd8
chore(share): fix typecheck issues 2025-06-09 22:31:14 +07:00
Elian Doran 8e73c5906d
fix(server): missing boxicons in desktop/mobile 2025-06-09 22:18:44 +07:00
Elian Doran 4216a71a85
fix(server): render share theme on prod 2025-06-09 21:58:56 +07:00
Elian Doran 64e857362f
refactor(share): remove Swagger-specific CSS 2025-06-09 21:54:46 +07:00
Elian Doran 8e4bf1538d
feat(share): render math 2025-06-09 21:35:29 +07:00
Elian Doran 0ea60fa9d7
fix(client): bring back toast for read-only notes 2025-06-09 21:31:04 +07:00
Elian Doran 6667167b26
chore(server): set implicit dependency to share 2025-06-09 21:19:54 +07:00
Elian Doran fd06046be5
refactor(share): remove swagger integration 2025-06-09 21:17:04 +07:00
Elian Doran f20d4cbed2
refactor(share): translate messages 2025-06-09 21:16:03 +07:00
Elian Doran 18e6981e9a
refactor(share): don't load boxicons if not needed 2025-06-09 21:00:58 +07:00
Elian Doran d68d0b5dd8
feat(share): disable search if single note 2025-06-09 20:53:48 +07:00
Elian Doran 3b81d00de7
fix(share): crash if sharing single note without tree 2025-06-09 20:52:02 +07:00
Elian Doran dd6660a6cd
chore(share): use locale-friendly date 2025-06-09 20:49:06 +07:00
Elian Doran 8cb7dc7a7e
feat(share): integrate last updated date 2025-06-09 20:47:36 +07:00
Elian Doran dc5bb627ed
feat(share): integrate prev/next navigation 2025-06-09 20:36:04 +07:00
Elian Doran 4d5a0e7832
refactor(share): don't load jQuery if not needed 2025-06-09 20:15:55 +07:00
Elian Doran 0e88818220
refactor(share): don't load highlighting if not needed 2025-06-09 20:03:32 +07:00
Elian Doran ea015bc2cf
refactor(share): remove no longer necessary highlight module 2025-06-09 19:55:21 +07:00
Elian Doran 02fe7c97ca
feat(share): automatic light/dark mode 2025-06-09 19:38:44 +07:00
Elian Doran 0cba323091
feat(share): display icons in note tree 2025-06-09 18:47:02 +07:00
Elian Doran 89e931bca6
feat(share): improve style of copy button 2025-06-09 18:31:02 +07:00
SiriusXT ace7e67c00 fix(zen): Show fixed toolbar in Zen mode 2025-06-09 17:27:31 +07:00
Elian Doran 1bcb811835
feat(share): enable syntax highlighting 2025-06-09 18:22:30 +07:00
JYC333 fba29249b0
Merge pull request #2231 from TriliumNext/toolbar
chore(editor): relocate link icon in floating toolbar
2025-06-09 17:18:02 +07:00
SiriusXT 496c36fde8 chore(editor): relocate link icon in floating toolbar 2025-06-09 16:56:51 +07:00
Elian Doran 54ffeeaed9
refactor(share): remove experiment to precompile EJS 2025-06-09 16:24:21 +07:00
Elian Doran 828da2aabf
refactor(share): relocate 404 template 2025-06-09 16:22:22 +07:00
Elian Doran a61d76deed
refactor(share): remove old templates 2025-06-09 16:13:11 +07:00
Elian Doran 2238fed1b7
style(share): remove superfluous padding 2025-06-09 16:06:30 +07:00
Elian Doran 50777faa02
feat(share): add the Trilium logo by default 2025-06-09 15:10:50 +07:00
Elian Doran 157b4f9398
feat(share): expand subchildren when in folder 2025-06-09 15:02:50 +07:00
Elian Doran 091ffdeb59
feat(share): allow viewing directories 2025-06-09 14:54:04 +07:00
Elian Doran 0b3846fc35
fix(share): reintroduce theme-specific scripts 2025-06-09 14:39:56 +07:00
Elian Doran c2e0640ea8
chore(share): fix error in dev mode 2025-06-09 14:29:19 +07:00
Elian Doran 3cd4b1b67e
chore(share): remove redundant script import 2025-06-09 14:28:13 +07:00
Elian Doran 41edab3857
fix(share): regressions in child preview due to base theme 2025-06-09 14:21:47 +07:00
Elian Doran 12dbf36358
fix(share): regressions in menu due to base theme 2025-06-09 14:21:07 +07:00
Elian Doran d630d407ca
refactor(share): integrate with base CSS 2025-06-09 14:20:50 +07:00
Elian Doran 8bdf3626f0
refactor(share): integrate with client 2025-06-09 14:13:35 +07:00
Elian Doran 3def1a1e57
chore(share): introduce theme script 2025-06-09 13:37:38 +07:00
Elian Doran 4fcb186022
chore(share): bring back original share.js 2025-06-09 13:35:14 +07:00
Elian Doran 7451c2701e
chore(share): bring back CSS 2025-06-09 13:33:25 +07:00
Elian Doran 098cd19b18
chore(share): fall back to original EJS rendering mechanism 2025-06-09 13:20:41 +07:00
Elian Doran 2827126be7
chore(share): embed imports 2025-06-09 12:39:48 +07:00
SiriusXT e6c2f729f4 Merge branch 'develop' into moveupdown 2025-06-09 16:41:31 +07:00
Elian Doran 7e443e7b8d
refactor(share): use custom build plugin 2025-06-09 11:18:47 +07:00
Elian Doran d6bb790e26
chore(share): use rendered template (missing include) 2025-06-09 11:12:02 +07:00
Elian Doran 2d3265136d
chore(share): prebuilt template 2025-06-09 11:00:58 +07:00
Elian Doran 11853962b7
refactor(share): remove note uploading mechanism 2025-06-09 10:51:18 +07:00
Elian Doran c6ffa53f0b
chore(share): set nx name 2025-06-09 10:48:31 +07:00
SiriusXT 2544c9b483 feat(ckeditor): Add text alignment 2025-06-09 15:00:08 +07:00
Elian Doran 8557159273
Merge pull request #2229 from TriliumNext/renovate/stylelint-config-ckeditor5-11.x-lockfile
chore(deps): update dependency stylelint-config-ckeditor5 to v11
2025-06-09 09:43:54 +07:00
Elian Doran a4192cf061
Merge pull request #2228 from TriliumNext/renovate/eslint-config-ckeditor5-11.x-lockfile
chore(deps): update dependency eslint-config-ckeditor5 to v11
2025-06-09 09:43:32 +07:00
Elian Doran d37491e95a
Merge pull request #2227 from TriliumNext/renovate/pnpm-10.x
chore(deps): update pnpm to v10.12.1
2025-06-09 09:43:11 +07:00
renovate[bot] e93e32d01c
chore(deps): update pnpm to v10.12.1 2025-06-09 06:23:50 +07:00
SiriusXT ff1030332c Merge branch 'develop' into tab-row 2025-06-09 14:20:08 +07:00
SiriusXT f48d17cb22 fix(tab-row): Make the default distance include margin 2025-06-09 14:06:15 +07:00
Elian Doran ca4b8fa5ec
Merge pull request #2226 from TriliumNext/renovate/cheerio-1.x
chore(deps): update dependency cheerio to v1.1.0
2025-06-09 09:03:20 +07:00
Elian Doran c155b8651f
Merge branch 'develop' into renovate/cheerio-1.x 2025-06-09 09:03:17 +07:00
Elian Doran 199c047c28
Merge pull request #2225 from TriliumNext/renovate/vitest-monorepo
chore(deps): update vitest monorepo to v3.2.2
2025-06-09 09:02:37 +07:00
Elian Doran ff603b2fab
Merge pull request #2224 from TriliumNext/renovate/mime-types-3.x-lockfile
chore(deps): update dependency @types/mime-types to v3.0.1
2025-06-09 09:01:46 +07:00
SiriusXT 949ff17776 fix(tab-row): Simplify scrolling logic 2025-06-09 13:59:52 +07:00
SiriusXT de4f0f7d6f fix(tab-row): Simplify scrolling logic 2025-06-09 11:33:16 +07:00
SiriusXT a23b0c5ec9 fix(move_block): Make the object movable 2025-06-09 10:04:10 +07:00
renovate[bot] 96000a8a72
chore(deps): update dependency stylelint-config-ckeditor5 to v11 2025-06-09 01:03:54 +07:00
renovate[bot] b2ccf126ed
chore(deps): update dependency eslint-config-ckeditor5 to v11 2025-06-09 01:03:05 +07:00
renovate[bot] b779577f9b
chore(deps): update dependency cheerio to v1.1.0 2025-06-09 00:47:05 +07:00
renovate[bot] 21e2a7e2cd
chore(deps): update vitest monorepo to v3.2.2 2025-06-09 00:46:25 +07:00
renovate[bot] 583356cf89
chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-09 00:45:45 +07:00
perf3ct e96fdbf72f
fix(llm): fix logging type check 2025-06-09 00:23:02 +07:00
perf3ct 41906abaf9
feat(route): allow for routes to handle their own response 2025-06-09 00:08:06 +07:00
perf3ct ca6277f6e9
feat(llm): handle error catching in streaming better 2025-06-09 00:07:00 +07:00
perf3ct e98fabcc9d
fix(unit): resolve auth error in llm unit test
keep working
2025-06-08 23:19:40 +07:00
perf3ct f5ad5b875e
fix(tests): resolve LLM streaming unit test failures
closer to fixing...

closer...

very close to passing...
2025-06-08 23:02:15 +07:00
perf3ct daa32e4355
Revert "fix(unit): comment out this test for now to see if the rest pass"
This reverts commit 95a33ba3c0.
2025-06-08 22:02:56 +07:00
perf3ct 95a33ba3c0
fix(unit): comment out this test for now to see if the rest pass 2025-06-08 21:54:19 +07:00
perf3ct b28387bada
feat(llm): decrease the throttle on the chunking tests lol 2025-06-08 21:47:53 +07:00
perf3ct 93cf868dcf
feat(llm): last test should be passing now 2025-06-08 21:38:57 +07:00
perf3ct 224cae6db2
fix(unit): resolve type errors 2025-06-08 21:03:07 +07:00
perf3ct 0c44dd0e63
fix(unit): alright I'm just going to get rid of this test to pick my battles 2025-06-08 20:55:27 +07:00
perf3ct d60e795421
feat(llm): still working on fixing tests... 2025-06-08 20:39:35 +07:00
perf3ct c6f2124e9d
feat(llm): add tests for streaming 2025-06-08 20:30:33 +07:00
Elian Doran 04f4933e97
fix(share): fonts not loading 2025-06-08 23:23:21 +07:00
Elian Doran fa5cd150d9
fix(share): loading of tree and table of contents 2025-06-08 23:22:59 +07:00
Elian Doran 9469433143
chore(share): basic integration of CSS 2025-06-08 23:12:09 +07:00
Elian Doran 881a015f68
chore(share): integrate root template 2025-06-08 22:57:24 +07:00
Elian Doran 8e27d877a1
chore(share): install packages 2025-06-08 22:16:21 +07:00
Elian Doran eea3163f51 Add 'packages/share-theme/' from commit '2cdd2a0a543f0bced8284ca55bc94efadbc7c91f'
git-subtree-dir: packages/share-theme
git-subtree-mainline: d8f0709bce
git-subtree-split: 2cdd2a0a54
2025-06-08 22:06:45 +07:00
perf3ct c1bcb73337
feat(llm): also improve the llm streaming service, to make it cooperate with unit tests better 2025-06-08 18:40:20 +07:00
Elian Doran d8f0709bce
fix(editor): white screen if CKEditor crashes a second time 2025-06-08 21:35:01 +07:00
perf3ct 40cad2e886
fix(unit): I believe it should pass now? 2025-06-08 18:20:30 +07:00
perf3ct a8faf5d699
fix(unit): still working on getting the LLM unit tests to pass... 2025-06-08 18:13:27 +07:00
Elian Doran d7f26fa27c
fix(editor): data loss if updating while editor crashed 2025-06-08 20:39:31 +07:00
Elian Doran b379f89a7a
chore(client): fix logging errors with circular objects 2025-06-08 20:35:38 +07:00
Elian Doran 10abb50971
chore(text): remove duplicate error log 2025-06-08 20:31:09 +07:00
perf3ct e011c56715
fix(unit): no more type errors hopefully 2025-06-08 16:33:26 +07:00
Jon Fuller d7abd3a8ed
Merge branch 'develop' into feat/llm-unit-tests 2025-06-08 08:49:08 +07:00
SiriusXT be447b4139 fix(move_block): keep moved blocks in view 2025-06-08 16:49:02 +07:00
SiriusXT 48613da57f fix(move_block): move multiple lines up/down 2025-06-08 16:30:10 +07:00
Elian Doran ef111c36c0
Merge pull request #2219 from hulmgulm/nginx
Doc: update Nginx reverse proxy documentation
2025-06-08 11:02:07 +07:00
hulmgulm 765601bb75
Merge branch 'TriliumNext:develop' into nginx 2025-06-08 09:52:18 +07:00
Elian Doran e87789d92b
Merge pull request #2208 from TriliumNext/fix/llm-chat-save-bug
fix(llm): save to the same note that the chat request was sent from
2025-06-08 10:45:58 +07:00
Elian Doran a95acd5529
Merge pull request #2210 from TriliumNext/renovate/compression-1.x
chore(deps): update dependency @types/compression to v1.8.1
2025-06-08 10:45:39 +07:00
Elian Doran 34bc4c9c8c
Merge pull request #2218 from hulmgulm/develop
Doc: Updated theme reference
2025-06-08 10:43:50 +07:00
renovate[bot] e3d0a73459
chore(deps): update dependency @types/compression to v1.8.1 2025-06-08 07:43:23 +07:00
Elian Doran 93b5fc2236
Merge pull request #2206 from TriliumNext/fix/llm-embeddings-removal-increment-version
fix(server): increment SYNC_VERSION and APP_DB_VERSION for LLM embedd…
2025-06-08 10:42:59 +07:00
Elian Doran 8c25b59bed
Merge pull request #2217 from TriliumNext/fix/llm-add-warning2
feat(llm): add warning to the top of LLM Chat Notes and LLM settings that the feature is experimental
2025-06-08 10:40:22 +07:00
Elian Doran fb8f3d1d29
Merge pull request #2212 from TriliumNext/renovate/express-5.x-lockfile
chore(deps): update dependency @types/express to v5.0.3
2025-06-08 10:38:53 +07:00
renovate[bot] 3f1007e904
chore(deps): update dependency @types/express to v5.0.3 2025-06-08 07:36:36 +07:00
Elian Doran 916e855ed0
Merge pull request #2215 from TriliumNext/renovate/multer-1.x
chore(deps): update dependency @types/multer to v1.4.13
2025-06-08 10:33:11 +07:00
renovate[bot] 5b154d2544
chore(deps): update dependency @types/multer to v1.4.13 2025-06-08 07:31:57 +07:00
Elian Doran 6734dae956
chore(server): add serve-nodir 2025-06-08 10:29:20 +07:00
Elian Doran 6b844063b6
Merge pull request #2216 from TriliumNext/renovate/serve-static-1.x
chore(deps): update dependency @types/serve-static to v1.15.8
2025-06-08 10:28:11 +07:00
Elian Doran 5313ce7843
Merge pull request #2211 from TriliumNext/renovate/cookie-parser-1.x
chore(deps): update dependency @types/cookie-parser to v1.4.9
2025-06-08 10:27:33 +07:00
Elian Doran 34fa770748
Merge pull request #2213 from TriliumNext/renovate/express-session-1.x
chore(deps): update dependency @types/express-session to v1.18.2
2025-06-08 10:26:58 +07:00
Elian Doran ee1e0da156
Merge pull request #2214 from TriliumNext/renovate/mime-types-3.x
chore(deps): update dependency @types/mime-types to v3.0.1
2025-06-08 10:26:27 +07:00
hulmgulm 48a310e0aa
Update Nginx.md 2025-06-08 08:34:27 +07:00
hulmgulm 7ef610b702
Update Reference.md 2025-06-08 08:14:39 +07:00
perf3ct 29d9c9834a
feat(llm): add warning to the top of LLM Chat Notes and LLM settings that the feature is experimental 2025-06-08 04:48:19 +07:00
perf3ct 055b34fb46
feat(llm): fix last unit tests 2025-06-08 04:33:33 +07:00
renovate[bot] 858801502d
chore(deps): update dependency @types/serve-static to v1.15.8 2025-06-08 01:58:09 +07:00
renovate[bot] f83d33754e
chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-08 01:56:48 +07:00
renovate[bot] a3c655ffc5
chore(deps): update dependency @types/express-session to v1.18.2 2025-06-08 01:55:20 +07:00
renovate[bot] cf9f59304c
chore(deps): update dependency @types/cookie-parser to v1.4.9 2025-06-08 01:53:26 +07:00
perf3ct cc089c245e
fix(llm): save the chat to a specific note, not the currently active note 2025-06-08 00:02:40 +07:00
perf3ct c6062f453a
fix(llm): changing providers works now 2025-06-07 23:57:35 +07:00
perf3ct 414781936b
fix(llm): always fetch the user's selected model 2025-06-07 23:36:53 +07:00
perf3ct 0ce5307c0b
fix(llm): well this has been using the wrong value the whole time 2025-06-07 23:16:44 +07:00
perf3ct 313c624871
feat(e2e): more tests pass! 2025-06-07 23:15:30 +07:00
perf3ct d8bbece02a
feat(e2e): llm tests mostly pass 2025-06-07 23:07:54 +07:00
perf3ct bb483558b0
feat(llm): add e2e tests for llm 2025-06-07 22:41:55 +07:00
perf3ct b6b88dff86
fix(server): increment SYNC_VERSION and APP_DB_VERSION for LLM embeddings removal 2025-06-07 21:13:02 +07:00
perf3ct 7f9ad04b57
feat(llm): create unit tests for LLM services 2025-06-07 21:03:54 +07:00
perf3ct c2eed44150
fix(llm): make sure that we're referencing the correct Note ID when saving 2025-06-07 20:15:11 +07:00
Elian Doran ebb1654d0e
Merge pull request #2205 from TriliumNext/feat/llm-remove-embeddings
Remove Embeddings from LLM feature
2025-06-07 22:54:05 +07:00
perf3ct ff37050470
fix(llm): delete provider_manager for embeddings too 2025-06-07 19:33:19 +07:00
perf3ct b0d804da08
fix(llm): remove the vectorSearch stage from the pipeline 2025-06-07 18:57:08 +07:00
perf3ct 4550c12c6e
feat(llm): remove everything to do with embeddings, part 3 2025-06-07 18:30:46 +07:00
perf3ct 44a2e7df21
feat(llm): remove everything to do with embeddings, part 2 2025-06-07 18:20:06 +07:00
Elian Doran bed3c61142
Merge branch 'develop' of https://github.com/TriliumNext/Notes into develop 2025-06-07 21:15:38 +07:00
perf3ct 44a45780b7
feat(llm): remove everything to do with embeddings 2025-06-07 18:11:12 +07:00
Elian Doran cedf6cc631
Merge pull request #1888 from TriliumNext/renovate/express-5.x
fix(deps): update dependency express to v5
2025-06-07 20:17:10 +07:00
Elian Doran 7281233012
chore(build): enable source maps 2025-06-07 20:12:13 +07:00
Elian Doran 4def18e882
fix(desktop): query parameters not working 2025-06-07 18:46:29 +07:00
Elian Doran 03024ef7a9
chore(desktop): bypass query error 2025-06-07 15:59:17 +07:00
Elian Doran 9907f7f60f
fix(desktop): CLS failing due to lack of listeners 2025-06-07 15:45:56 +07:00
Elian Doran ebeabe2b54
fix(desktop): adapt request mocking to express 5 2025-06-07 14:58:07 +07:00
Panagiotis Papadopoulos 0bc91349f6
refactor(routes/custom): update path "/custom/*path" for express v5 2025-06-07 14:21:37 +07:00
Panagiotis Papadopoulos 23297c9860
fix(routes): remove unused wildcard in apiRoute "/api/options/:name/:value*"
the updateOption function that handles the req.param is just destructuring `const { name, value } = req.params;` and does nothing else with the path or any params.
The remaining parts of the wildcard (which can be accessed via req.param[0]) are just ignored here.

even with express v4, this would *always* just take and process the very first part of the path, in the exact wildcard's place, e.g.

`/api/options/locale/de` and
`/api/options/locale/de/test/whatever`
would *both* end up destructuring "value" from req.param as "de" (because it is in the exact place of the 'value' wildcard)

in express v5 the wildcard behaviour changes -> here req.param.value would return an array with the paths split into separate string.

but since the code previously regarded only the first part of the path -> we can just get rid of the wildcard and use a named route param

the only thing to keep in mind: if a request with more than one "value" is received, (e.g. `/api/options/locale/de/test/whatever`) -> since we don't have the wildcard anymore  -> this will turn to a 404.
IMHO that is actually desirable here though
2025-06-07 14:21:35 +07:00
renovate[bot] 086bd10f84
fix(deps): update dependency express to v5 2025-06-07 11:15:22 +07:00
Elian Doran 70cdc100d9
Merge pull request #2198 from TriliumNext/oidc
Support custom oidc server
2025-06-07 14:13:14 +07:00
Elian Doran 17c2ae1177
Merge pull request #2177 from TriliumNext/tab-row-scroll
fix(tab-row): ensure similar behavior between horizontal/vertical scrolling
2025-06-07 14:05:03 +07:00
Elian Doran 497a851ebe
Merge branch 'develop' of https://github.com/TriliumNext/Notes into develop 2025-06-07 13:52:00 +07:00
Elian Doran 9a48547232
Merge pull request #2194 from TriliumNext/renovate/eslint-config-ckeditor5-11.x-lockfile
chore(deps): update dependency eslint-config-ckeditor5 to v11
2025-06-07 13:42:36 +07:00
Elian Doran 97dfd454b8
Merge pull request #2195 from TriliumNext/renovate/stylelint-config-ckeditor5-11.x-lockfile
chore(deps): update dependency stylelint-config-ckeditor5 to v11
2025-06-07 13:42:14 +07:00
Elian Doran 2d8317887d
chore(release): fix setting as prerelease 2025-06-07 13:35:29 +07:00
Elian Doran 5dc4f0eae6
docs(readme): add badge to relative CI for client 2025-06-07 13:30:21 +07:00
JYC333 8849d482b0
Merge pull request #2193 from TriliumNext/renovate/vitest-monorepo
chore(deps): update vitest monorepo to v3.2.2
2025-06-07 12:15:23 +07:00
Jin bffb47645c fix: 🐛 fix SSO icon loading 2025-06-07 12:10:41 +07:00
Jin 4cafd83c25 feat: 🎸 set SSO login page ui 2025-06-07 12:10:41 +07:00
Jin db3bf4c12c feat: 🎸 set SSO login logic 2025-06-07 12:10:41 +07:00
Jin fa44a5343b feat: 🎸 support custon oidc server 2025-06-07 12:10:41 +07:00
renovate[bot] e5cc58b4e2
chore(deps): update dependency stylelint-config-ckeditor5 to v11 2025-06-07 10:06:51 +07:00
renovate[bot] dbaee4d29b
chore(deps): update dependency eslint-config-ckeditor5 to v11 2025-06-07 10:06:01 +07:00
renovate[bot] a6d1fd3d9f
chore(deps): update vitest monorepo to v3.2.2 2025-06-07 10:05:13 +07:00
Elian Doran c8b3cdf01b
feat(ci): introduce RelativeCI for the client 2025-06-07 13:02:37 +07:00
Elian Doran 5a53fbbb01
chore(ci): get rid of size reporters 2025-06-07 12:52:33 +07:00
SiriusXT 17885f6091 fix(tab-row): Remove smooth scrolling 2025-06-06 19:09:25 +07:00
SiriusXT 537ad1c1e5 fix(tab-row): Fix extra +1 when extraWidthRemaining is a decimal 2025-06-06 15:55:00 +07:00
SiriusXT a38c091d73 Merge branch 'develop' into tab-row-scroll 2025-06-06 09:09:56 +07:00
SiriusXT f723ca65d1 fix(tab-row): Reduce animation frames 2025-06-06 09:08:17 +07:00
SiriusXT 6d416cfe65 fix(tab-row): Reduce animation frames 2025-06-06 09:06:14 +07:00
SiriusXT ea1bce4e7b fix(tab-row): ensure similar behavior between horizontal/vertical scrolling 2025-06-05 23:19:00 +07:00
SiriusXT 6508ef4fce fix(tab-row): Avoid this.newTabOuterWidth being a decimal in Electron 2025-06-05 20:27:58 +07:00
SiriusXT 7b1c6807ba fix(tab-row): Remove shift+wheel tab switching 2025-06-05 20:20:29 +07:00
SiriusXT be8ee350cb fix(tab-row): ensure similar behavior between horizontal/vertical scrolling 2025-06-05 19:54:37 +07:00
SiriusXT 99d26b794d Merge branch 'develop' into tab-row-scroll 2025-06-05 18:27:05 +07:00
SiriusXT b721bb4cfc fix(tab-row): ensure similar behavior between horizontal/vertical scrolling 2025-06-05 17:54:34 +07:00
Zerebos 2cdd2a0a54
Fix several accessibility issues 2024-06-10 02:17:50 +07:00
Zerebos 01a8d0f864
Make reloading in light theme less janky 2024-06-08 15:21:59 +07:00
Zerebos 81e986db30
Fix multiple cases of overflow 2024-06-08 14:42:39 +07:00
Zerebos 3dc3f1579b
Fix theme switcher showing wrong value 2024-06-08 14:32:06 +07:00
Zerebos a7ed566645
Improve page performance and rendering
- Moves expanding the tree navigation to the template so the rendering is already done before page load
- Adds a way to explicitly set the logo size to prevent the page moving after it loads in
2024-06-08 14:31:40 +07:00
Zerebos 2485f20d5c
Minor fixes for new options 2024-06-06 19:10:56 +07:00
Zerebos 465468a5a3
Add more customization in template 2024-06-06 12:21:38 +07:00
Zerebos 930632e6fe
Template bugfixes 2024-06-06 11:14:43 +07:00
Zack Rauen 0016b43c09 Update to Trilium 0.62.3 2023-12-05 20:50:29 +07:00
Zack Rauen 5391521c08 Enable sliding categories and extract TODOs 2023-09-30 00:13:37 +07:00
Zack Rauen e7a3f6d17e Generate ToC in templates 2023-09-29 01:43:39 +07:00
Zack Rauen 074ac0b725 Make swagger load from trilium directly 2023-09-28 20:59:17 +07:00
Zack Rauen bde6d83625 Fix css priority for mobile 2023-09-28 20:59:04 +07:00
Zack Rauen bdfe86ba1a Remove dead code and reorganize slightly 2023-09-28 00:24:52 +07:00
Zack Rauen a8bb2f110b Some important QoL changes 2023-09-28 00:14:44 +07:00
Zack Rauen 4147f2b8d8 Switch scripts to redesign 2023-09-27 23:18:03 +07:00
Zack Rauen a7edc5e03e Slightly adjust page template 2023-09-27 22:54:13 +07:00
Zack Rauen 1a40658345 Rework css for new design 2023-09-27 22:53:59 +07:00
Zack Rauen 124fd480b7 Update build script for templates 2023-09-27 16:49:30 +07:00
Zack Rauen 667cd64f3b Rename main to scripts 2023-09-27 16:28:08 +07:00
Zack Rauen 2ae6d4c5a4 Add initial templates 2023-09-27 16:20:04 +07:00
Zack Rauen d9e33270b0 Add README 2023-09-24 06:03:35 +07:00
Zack Rauen ad59080307 Some more finishing touches 2023-09-23 22:39:38 +07:00
Zack Rauen 34fa9a1f01 Some final touches 2023-09-23 07:21:38 +07:00
Zack Rauen cb19ed36bc Make mobile compatible 2023-09-23 02:56:59 +07:00
Zack Rauen 039a5ac2e3 Modularize more and add swagger-ui 2023-09-22 23:57:17 +07:00
Zack Rauen 568ea271a4 Modularize proof of concept 2023-09-21 03:18:11 +07:00
Zack Rauen a584a5c296 Port from trilium notes 2023-09-20 03:38:55 +07:00
506 changed files with 26008 additions and 16873 deletions

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

@ -39,76 +39,7 @@ jobs:
- uses: nrwl/nx-set-shas@v4
- name: Check affected
run: pnpm nx affected --verbose -t typecheck build rebuild-deps
report-electron-size:
name: Report Electron size
runs-on: ubuntu-latest
needs:
- check-affected
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- 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
- name: Run the build
uses: ./.github/actions/build-electron
with:
os: linux
arch: x64
shell: bash
forge_platform: linux
- name: Run the Electron size report
uses: ./.github/actions/report-size
with:
paths: 'upload/**/*'
onlyDiff: 'true'
branch: 'develop'
header: 'Electron size report'
unit: "MB"
ghToken: ${{ secrets.GITHUB_TOKEN }}
report-server-size:
name: Report server size
runs-on: ubuntu-latest
needs:
- check-affected
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- name: Run the build
uses: ./.github/actions/build-server
with:
os: linux
arch: x64
- name: Run the server size report
uses: ./.github/actions/report-size
with:
paths: 'upload/**/*'
onlyDiff: 'true'
branch: 'develop'
header: 'Server size report'
unit: "MB"
ghToken: ${{ secrets.GITHUB_TOKEN }}
run: pnpm nx affected --verbose -t typecheck build rebuild-deps test-build
test_dev:
name: Test development
@ -143,7 +74,14 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Update build info
run: pnpm run chore:update-build-info
- name: Trigger build
- name: Trigger client build
run: pnpm nx run client:build
- name: Send client bundle stats to RelativeCI
uses: relative-ci/agent-action@v3
with:
webpackStatsFile: ./apps/client/dist/webpack-stats.json
key: ${{ secrets.RELATIVE_CI_CLIENT_KEY }}
- name: Trigger server build
run: pnpm nx run server:build
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6

@ -53,7 +53,7 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Install Playwright Browsers
run: pnpx playwright install --with-deps
run: pnpm exec playwright install --with-deps
- name: Run the TypeScript build
run: pnpm run server:build
@ -82,7 +82,15 @@ jobs:
require-healthy: true
- name: Run Playwright tests
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpx nx run server-e2e:e2e
run: TRILIUM_DOCKER=1 TRILIUM_PORT=8082 pnpm exec nx run server-e2e:e2e
- name: Upload Playwright trace
if: failure()
uses: actions/upload-artifact@v4
with:
name: Playwright trace (${{ matrix.dockerfile }})
path: test-output/playwright/output
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:

@ -11,7 +11,8 @@ on:
pull_request:
paths:
- .github/actions/build-electron/*
- forge.config.cjs
- .github/workflows/nightly.yml
- forge.config.ts
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@ -76,7 +77,7 @@ jobs:
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
- name: Publish release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v2.3.2
if: ${{ github.event_name != 'pull_request' }}
with:
make_latest: false
@ -116,7 +117,7 @@ jobs:
arch: ${{ matrix.arch }}
- name: Publish release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v2.3.2
if: ${{ github.event_name != 'pull_request' }}
with:
make_latest: false

@ -33,11 +33,11 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- run: pnpx playwright install --with-deps
- 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: pnpx nx affected -t e2e
- run: pnpm exec nx affected -t e2e

@ -114,7 +114,7 @@ jobs:
path: upload
- name: Publish stable release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v2.3.2
with:
draft: false
body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md
@ -122,5 +122,5 @@ jobs:
files: upload/*.*
discussion_category_name: Announcements
make_latest: ${{ !contains(github.ref, 'rc') }}
prerelease: ${{ !contains(github.ref, 'rc') }}
prerelease: ${{ contains(github.ref, 'rc') }}
token: ${{ secrets.RELEASE_PAT }}

3
.gitignore vendored

@ -45,4 +45,5 @@ upload
.rollup.cache
*.tsbuildinfo
/result
/result
.svelte-kit

@ -1,7 +1,2 @@
_regroup
_regroup_monorepo
# Asset copying respects .gitignore / .nxignore for some reason.
# See https://github.com/nrwl/nx/issues/20309
!dist
!node_modules
_regroup_monorepo

@ -9,6 +9,8 @@
"redhat.vscode-yaml",
"tobermory.es6-string-html",
"vitest.explorer",
"yzhang.markdown-all-in-one"
"yzhang.markdown-all-in-one",
"svelte.svelte-vscode",
"bradlc.vscode-tailwindcss"
]
}

@ -1,168 +1,2 @@
# TriliumNext Notes
![GitHub Sponsors](https://img.shields.io/github/sponsors/eliandoran) ![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes) ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total)
[English](./README.md) | [Chinese](./docs/README-ZH_CN.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
TriliumNext Notes is a free and open-source, cross-platform hierarchical note taking application with focus on building large personal knowledge bases.
See [screenshots](https://triliumnext.github.io/Docs/Wiki/screenshot-tour) for quick overview:
<a href="https://triliumnext.github.io/Docs/Wiki/screenshot-tour"><img src="./docs/app.png" alt="Trilium Screenshot" width="1000"></a>
## 🎁 Features
* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://triliumnext.github.io/Docs/Wiki/cloning-notes))
* Rich WYSIWYG note editor including e.g. tables, images and [math](https://triliumnext.github.io/Docs/Wiki/text-notes) with markdown [autoformat](https://triliumnext.github.io/Docs/Wiki/text-notes#autoformat)
* Support for editing [notes with source code](https://triliumnext.github.io/Docs/Wiki/code-notes), including syntax highlighting
* Fast and easy [navigation between notes](https://triliumnext.github.io/Docs/Wiki/note-navigation), full text search and [note hoisting](https://triliumnext.github.io/Docs/Wiki/note-hoisting)
* Seamless [note versioning](https://triliumnext.github.io/Docs/Wiki/note-revisions)
* Note [attributes](https://triliumnext.github.io/Docs/Wiki/attributes) can be used for note organization, querying and advanced [scripting](https://triliumnext.github.io/Docs/Wiki/scripts)
* UI available in English, German, Spanish, French, Romanian, and Chinese (simplified and traditional)
* Direct [OpenID and TOTP integration](.docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Server%20Installation/Multi-Factor%20Authentication.md") for more secure login
* [Synchronization](https://triliumnext.github.io/Docs/Wiki/synchronization) with self-hosted sync server
* there's a [3rd party service for hosting synchronisation server](https://trilium.cc/paid-hosting)
* [Sharing](https://triliumnext.github.io/Docs/Wiki/sharing) (publishing) notes to public internet
* Strong [note encryption](https://triliumnext.github.io/Docs/Wiki/protected-notes) with per-note granularity
* Sketching diagrams, based on [Excalidraw](https://excalidraw.com/) (note type "canvas")
* [Relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map) and [link maps](https://triliumnext.github.io/Docs/Wiki/link-map) for visualizing notes and their relations
* Mind maps, based on [Mind Elixir](https://docs.mind-elixir.com/)
* [Geo maps](./docs/User%20Guide/User%20Guide/Note%20Types/Geo%20Map.md) with location pins and GPX tracks
* [Scripting](https://triliumnext.github.io/Docs/Wiki/scripts) - see [Advanced showcases](https://triliumnext.github.io/Docs/Wiki/advanced-showcases)
* [REST API](https://triliumnext.github.io/Docs/Wiki/etapi) for automation
* Scales well in both usability and performance upwards of 100 000 notes
* Touch optimized [mobile frontend](https://triliumnext.github.io/Docs/Wiki/mobile-frontend) for smartphones and tablets
* Built-in [dark theme](https://triliumnext.github.io/Docs/Wiki/themes), support for user themes
* [Evernote](https://triliumnext.github.io/Docs/Wiki/evernote-import) and [Markdown import & export](https://triliumnext.github.io/Docs/Wiki/markdown)
* [Web Clipper](https://triliumnext.github.io/Docs/Wiki/web-clipper) for easy saving of web content
* Customizable UI (sidebar buttons, user-defined widgets, ...)
* [Metrics](./docs/User%20Guide/User%20Guide/Advanced%20Usage/Metrics.md), along with a [Grafana Dashboard](./docs/User%20Guide/User%20Guide/Advanced%20Usage/Metrics/grafana-dashboard.json)
✨ Check out the following third-party resources/communities for more TriliumNext related goodies:
- [awesome-trilium](https://github.com/Nriver/awesome-trilium) for 3rd party themes, scripts, plugins and more.
- [TriliumRocks!](https://trilium.rocks/) for tutorials, guides, and much more.
## ⚠️ Why TriliumNext?
[The original Trilium project is in maintenance mode](https://github.com/zadam/trilium/issues/4620).
### Migrating from Trilium?
There are no special migration steps to migrate from a zadam/Trilium instance to a TriliumNext/Notes instance. Simply [install TriliumNext/Notes](#-installation) as usual and it will use your existing database.
Versions up to and including [v0.90.4](https://github.com/TriliumNext/Notes/releases/tag/v0.90.4) are compatible with the latest zadam/trilium version of [v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later versions of TriliumNext have their sync versions incremented.
## 📖 Documentation
We're currently in the progress of moving the documentation to in-app (hit the `F1` key within Trilium). As a result, there may be some missing parts until we've completed the migration. If you'd prefer to navigate through the documentation within GitHub, you can navigate the [User Guide](./docs/User%20Guide/User%20Guide/) documentation.
Below are some quick links for your convenience to navigate the documentation:
- [Server installation](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation.md)
- [Docker installation](./docs/User%20Guide/User%20Guide/Installation%20&%20Setup/Server%20Installation/1.%20Installing%20the%20server/Using%20Docker.md)
- [Upgrading TriliumNext](./docs/User%20Guide/User%20Guide/Installation%20%26%20Setup/Upgrading%20TriliumNext.md)
- [Concepts and Features - Note](./docs/User%20Guide/User%20Guide/Basic%20Concepts%20and%20Features/Notes.md)
- [Patterns of personal knowledge base](https://triliumnext.github.io/Docs/Wiki/patterns-of-personal-knowledge)
Until we finish reorganizing the documentation, you may also want to [browse the old documentation](https://triliumnext.github.io/Docs).
## 💬 Discuss with us
Feel free to join our official conversations. We would love to hear what features, suggestions, or issues you may have!
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous discussions.)
- The `General` Matrix room is also bridged to [XMPP](xmpp:discuss@trilium.thisgreat.party?join)
- [Github Discussions](https://github.com/TriliumNext/Notes/discussions) (For asynchronous discussions.)
- [Github Issues](https://github.com/TriliumNext/Notes/issues) (For bug reports and feature requests.)
## 🏗 Installation
### Windows / MacOS
Download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
### Linux
If your distribution is listed in the table below, use your distribution's package.
[![Packaging status](https://repology.org/badge/vertical-allrepos/triliumnext.svg)](https://repology.org/project/triliumnext/versions)
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.
TriliumNext is also provided as a Flatpak, but not yet published on FlatHub.
### Browser (any OS)
If you use a server installation (see below), you can directly access the web interface (which is almost identical to the desktop app).
Currently only the latest versions of Chrome & Firefox are supported (and tested).
### Mobile
To use TriliumNext on a mobile device, you can use a mobile web browser to access the mobile interface of a server installation (see below).
If you prefer a native Android app, you can use [TriliumDroid](https://apt.izzysoft.de/fdroid/index/apk/eu.fliegendewurst.triliumdroid). Report bugs and missing features at [their repository](https://github.com/FliegendeWurst/TriliumDroid).
See issue https://github.com/TriliumNext/Notes/issues/72 for more information on mobile app support.
### Server
To install TriliumNext on your own server (including via Docker from [Dockerhub](https://hub.docker.com/r/triliumnext/notes)) follow [the server installation docs](https://triliumnext.github.io/Docs/Wiki/server-installation).
## 💻 Contribute
### Code
Download the repository, install dependencies using `pnpm` and then run the server (available at http://localhost:8080):
```shell
git clone https://github.com/TriliumNext/Notes.git
cd Notes
pnpm install
pnpm run server:start
```
### Documentation
Download the repository, install dependencies using `pnpm` and then run the environment required to edit the documentation:
```shell
git clone https://github.com/TriliumNext/Notes.git
cd Notes
pnpm install
pnpm nx run edit-docs:edit-docs
```
### Building the Executable
Download the repository, install dependencies using `pnpm` and then build the desktop app for Windows:
```shell
git clone https://github.com/TriliumNext/Notes.git
cd Notes
pnpm install
pnpm nx --project=desktop electron-forge:make -- --arch=x64 --platform=win32
```
For more details, see the [development docs](https://github.com/TriliumNext/Notes/blob/develop/docs/Developer%20Guide/Developer%20Guide/Building%20and%20deployment/Running%20a%20development%20build.md).
### Developer Documentation
Please view the [documentation guide](./docs/Developer%20Guide/Developer%20Guide/Environment%20Setup.md) for details. If you have more questions, feel free to reach out via the links described in the "Discuss with us" section above.
## 👏 Shoutouts
* [CKEditor 5](https://github.com/ckeditor/ckeditor5) - best WYSIWYG editor on the market, very interactive and listening team
* [FancyTree](https://github.com/mar10/fancytree) - very feature rich tree library without real competition. TriliumNext Notes would not be the same without it.
* [CodeMirror](https://github.com/codemirror/CodeMirror) - code editor with support for huge amount of languages
* [jsPlumb](https://github.com/jsplumb/jsplumb) - visual connectivity library without competition. Used in [relation maps](https://triliumnext.github.io/Docs/Wiki/relation-map.html) and [link maps](https://triliumnext.github.io/Docs/Wiki/note-map.html#link-map)
## 🤝 Support
Support for the TriliumNext organization will be possible in the near future. For now, you can:
- Support continued development on TriliumNext by supporting our developers: [eliandoran](https://github.com/sponsors/eliandoran) (See the [repository insights]([developers]([url](https://github.com/TriliumNext/Notes/graphs/contributors))) for a full list)
- Show a token of gratitude to the original Trilium developer ([zadam](https://github.com/sponsors/zadam)) via [PayPal](https://paypal.me/za4am) or Bitcoin (bitcoin:bc1qv3svjn40v89mnkre5vyvs2xw6y8phaltl385d2).
## 🔑 License
Copyright 2017-2025 zadam, Elian Doran, and other contributors
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
> [!IMPORTANT]
> TriliumNext started as a fork of the original Trilium repository (`zadam/trilium`). @zadam transferred the original repo to us so the work will continue in https://github.com/TriliumNext/Trilium.

@ -25,15 +25,16 @@ stats() {
# Print the number of existing strings on the JSON files for each locale
s=$(number_of_keys "${paths[0]}/en/server.json")
c=$(number_of_keys "${paths[1]}/en/translation.json")
echo "| locale |server strings |client strings |"
echo "|--------|---------------|---------------|"
echo "| en | ${s} | ${c} |"
echo "| locale | server strings | client strings |"
echo "|--------|----------------|----------------|"
echo "| en | ${s} | ${c} |"
echo "|--------|----------------|----------------|"
for locale in "${locales[@]}"; do
s=$(number_of_keys "${paths[0]}/${locale}/server.json")
c=$(number_of_keys "${paths[1]}/${locale}/translation.json")
n1=$(((8 - ${#locale}) / 2))
n2=$((n1 == 1 ? n1 + 1 : n1))
echo "|$(printf "%${n1}s")${locale}$(printf "%${n2}s")| ${s} | ${c} |"
echo "|$(printf "%${n1}s")${locale}$(printf "%${n2}s")| ${s} | ${c} |"
done
}
@ -78,7 +79,10 @@ file_path="$(
cd -- "$(dirname "${0}")" >/dev/null 2>&1 || exit
pwd -P
)"
paths=("${file_path}/../translations/" "${file_path}/../src/public/translations/")
paths=(
"${file_path}/../../apps/server/src/assets/translations/"
"${file_path}/../../apps/client/src/translations/"
)
locales=(cn de es fr pt_br ro tw)
if [ $# -eq 1 ]; then

@ -44,7 +44,6 @@ export default tseslint.config(
"dist/*",
"docs/*",
"demo/*",
"libraries/*",
"src/public/app-dist/*",
"src/public/app/doc_notes/*"
]

@ -38,7 +38,6 @@ export default [
"dist/*",
"docs/*",
"demo/*",
"libraries/*",
// TriliumNextTODO: check if we want to format packages here as well - for now skipping it
"packages/*",
"src/public/app-dist/*",

@ -35,13 +35,13 @@
"chore:generate-openapi": "tsx bin/generate-openapi.js"
},
"devDependencies": {
"@playwright/test": "1.52.0",
"@playwright/test": "1.53.1",
"@stylistic/eslint-plugin": "4.4.1",
"@types/express": "5.0.1",
"@types/node": "22.15.30",
"@types/express": "5.0.3",
"@types/node": "22.15.32",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.2.2",
"eslint": "9.28.0",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.29.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25",
"jsdoc": "4.0.4",

@ -0,0 +1,4 @@
# The development license key for premium CKEditor features.
# Note: This key must only be used for the Trilium Notes project.
# Expires on: 2025-09-13
VITE_CKEDITOR_KEY=eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE3NTc3MjE1OTksImp0aSI6ImFiN2E0NjZmLWJlZGMtNDNiYy1iMzU4LTk0NGQ0YWJhY2I3ZiIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsic2giLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwiZmVhdHVyZXMiOlsiRFJVUCIsIkNNVCIsIkRPIiwiRlAiLCJTQyIsIlRPQyIsIlRQTCIsIlBPRSIsIkNDIiwiTUYiLCJTRUUiLCJFQ0giLCJFSVMiXSwidmMiOiI1MzlkOWY5YyJ9.2rvKPql4hmukyXhEtWPZ8MLxKvzPIwzCdykO653g7IxRRZy2QJpeRszElZx9DakKYZKXekVRAwQKgHxwkgbE_w

@ -0,0 +1 @@
VITE_CKEDITOR_ENABLE_INSPECTOR=false

@ -1,6 +1,6 @@
{
"name": "@triliumnext/client",
"version": "0.94.1",
"version": "0.95.0",
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
"private": true,
"license": "AGPL-3.0-only",
@ -10,7 +10,7 @@
"url": "https://github.com/TriliumNext/Notes"
},
"dependencies": {
"@eslint/js": "9.28.0",
"@eslint/js": "9.29.0",
"@excalidraw/excalidraw": "0.18.0",
"@fullcalendar/core": "6.1.17",
"@fullcalendar/daygrid": "6.1.17",
@ -25,8 +25,9 @@
"@triliumnext/codemirror": "workspace:*",
"@triliumnext/commons": "workspace:*",
"@triliumnext/highlightjs": "workspace:*",
"@triliumnext/share-theme": "workspace:*",
"autocomplete.js": "0.38.1",
"bootstrap": "5.3.6",
"bootstrap": "5.3.7",
"boxicons": "2.1.4",
"dayjs": "1.11.13",
"dayjs-plugin-utc": "0.1.2",
@ -47,11 +48,10 @@
"mark.js": "8.11.1",
"marked": "15.0.12",
"mermaid": "11.6.0",
"mind-elixir": "4.6.0",
"mind-elixir": "4.6.1",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"preact": "10.26.9",
"split.js": "1.6.5",
"svg-pan-zoom": "3.6.2",
"vanilla-js-wheel-zoom": "9.0.4"
@ -63,14 +63,22 @@
"@types/leaflet": "1.9.18",
"@types/leaflet-gpx": "1.3.7",
"@types/mark.js": "8.11.12",
"@types/react": "19.1.6",
"@types/react-dom": "19.1.6",
"copy-webpack-plugin": "13.0.0",
"happy-dom": "17.6.3",
"happy-dom": "18.0.1",
"script-loader": "0.7.2",
"vite-plugin-static-copy": "3.0.0"
"vite-plugin-static-copy": "3.0.2"
},
"nx": {
"name": "client"
"name": "client",
"targets": {
"serve": {
"dependsOn": [
"^build"
]
},
"circular-deps": {
"command": "pnpx dpdm -T {projectRoot}/src/**/*.ts --tree=false --warning=false --skip-dynamic-imports=circular"
}
}
}
}

@ -1,424 +0,0 @@
/*
* Remove template code below
*/
html {
-webkit-text-size-adjust: 100%;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
line-height: 1.5;
tab-size: 4;
scroll-behavior: smooth;
}
body {
font-family: inherit;
line-height: inherit;
margin: 0;
}
h1,
h2,
p,
pre {
margin: 0;
}
*,
::before,
::after {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: currentColor;
}
h1,
h2 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
text-decoration: inherit;
}
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
}
svg {
display: block;
vertical-align: middle;
}
svg {
shape-rendering: auto;
text-rendering: optimizeLegibility;
}
pre {
background-color: rgba(55, 65, 81, 1);
border-radius: 0.25rem;
color: rgba(229, 231, 235, 1);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
overflow: scroll;
padding: 0.5rem 0.75rem;
}
.shadow {
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.rounded {
border-radius: 1.5rem;
}
.wrapper {
width: 100%;
}
.container {
margin-left: auto;
margin-right: auto;
max-width: 768px;
padding-bottom: 3rem;
padding-left: 1rem;
padding-right: 1rem;
color: rgba(55, 65, 81, 1);
width: 100%;
}
#welcome {
margin-top: 2.5rem;
}
#welcome h1 {
font-size: 3rem;
font-weight: 500;
letter-spacing: -0.025em;
line-height: 1;
}
#welcome span {
display: block;
font-size: 1.875rem;
font-weight: 300;
line-height: 2.25rem;
margin-bottom: 0.5rem;
}
#hero {
align-items: center;
background-color: hsla(214, 62%, 21%, 1);
border: none;
box-sizing: border-box;
color: rgba(55, 65, 81, 1);
display: grid;
grid-template-columns: 1fr;
margin-top: 3.5rem;
}
#hero .text-container {
color: rgba(255, 255, 255, 1);
padding: 3rem 2rem;
}
#hero .text-container h2 {
font-size: 1.5rem;
line-height: 2rem;
position: relative;
}
#hero .text-container h2 svg {
color: hsla(162, 47%, 50%, 1);
height: 2rem;
left: -0.25rem;
position: absolute;
top: 0;
width: 2rem;
}
#hero .text-container h2 span {
margin-left: 2.5rem;
}
#hero .text-container a {
background-color: rgba(255, 255, 255, 1);
border-radius: 0.75rem;
color: rgba(55, 65, 81, 1);
display: inline-block;
margin-top: 1.5rem;
padding: 1rem 2rem;
text-decoration: inherit;
}
#hero .logo-container {
display: none;
justify-content: center;
padding-left: 2rem;
padding-right: 2rem;
}
#hero .logo-container svg {
color: rgba(255, 255, 255, 1);
width: 66.666667%;
}
#middle-content {
align-items: flex-start;
display: grid;
gap: 4rem;
grid-template-columns: 1fr;
margin-top: 3.5rem;
}
#learning-materials {
padding: 2.5rem 2rem;
}
#learning-materials h2 {
font-weight: 500;
font-size: 1.25rem;
letter-spacing: -0.025em;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
.list-item-link {
align-items: center;
border-radius: 0.75rem;
display: flex;
margin-top: 1rem;
padding: 1rem;
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
width: 100%;
}
.list-item-link svg:first-child {
margin-right: 1rem;
height: 1.5rem;
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
width: 1.5rem;
}
.list-item-link > span {
flex-grow: 1;
font-weight: 400;
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.list-item-link > span > span {
color: rgba(107, 114, 128, 1);
display: block;
flex-grow: 1;
font-size: 0.75rem;
font-weight: 300;
line-height: 1rem;
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.list-item-link svg:last-child {
height: 1rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
width: 1rem;
}
.list-item-link:hover {
color: rgba(255, 255, 255, 1);
background-color: hsla(162, 47%, 50%, 1);
}
.list-item-link:hover > span {
}
.list-item-link:hover > span > span {
color: rgba(243, 244, 246, 1);
}
.list-item-link:hover svg:last-child {
transform: translateX(0.25rem);
}
#other-links {
}
.button-pill {
padding: 1.5rem 2rem;
transition-duration: 300ms;
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
align-items: center;
display: flex;
}
.button-pill svg {
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
flex-shrink: 0;
width: 3rem;
}
.button-pill > span {
letter-spacing: -0.025em;
font-weight: 400;
font-size: 1.125rem;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
.button-pill span span {
display: block;
font-size: 0.875rem;
font-weight: 300;
line-height: 1.25rem;
}
.button-pill:hover svg,
.button-pill:hover {
color: rgba(255, 255, 255, 1) !important;
}
#nx-console:hover {
background-color: rgba(0, 122, 204, 1);
}
#nx-console svg {
color: rgba(0, 122, 204, 1);
}
#nx-console-jetbrains {
margin-top: 2rem;
}
#nx-console-jetbrains:hover {
background-color: rgba(255, 49, 140, 1);
}
#nx-console-jetbrains svg {
color: rgba(255, 49, 140, 1);
}
#nx-repo:hover {
background-color: rgba(24, 23, 23, 1);
}
#nx-repo svg {
color: rgba(24, 23, 23, 1);
}
#nx-cloud {
margin-bottom: 2rem;
margin-top: 2rem;
padding: 2.5rem 2rem;
}
#nx-cloud > div {
align-items: center;
display: flex;
}
#nx-cloud > div svg {
border-radius: 0.375rem;
flex-shrink: 0;
width: 3rem;
}
#nx-cloud > div h2 {
font-size: 1.125rem;
font-weight: 400;
letter-spacing: -0.025em;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
#nx-cloud > div h2 span {
display: block;
font-size: 0.875rem;
font-weight: 300;
line-height: 1.25rem;
}
#nx-cloud p {
font-size: 1rem;
line-height: 1.5rem;
margin-top: 1rem;
}
#nx-cloud pre {
margin-top: 1rem;
}
#nx-cloud a {
color: rgba(107, 114, 128, 1);
display: block;
font-size: 0.875rem;
line-height: 1.25rem;
margin-top: 1.5rem;
text-align: right;
}
#nx-cloud a:hover {
text-decoration: underline;
}
#commands {
padding: 2.5rem 2rem;
margin-top: 3.5rem;
}
#commands h2 {
font-size: 1.25rem;
font-weight: 400;
letter-spacing: -0.025em;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
#commands p {
font-size: 1rem;
font-weight: 300;
line-height: 1.5rem;
margin-top: 1rem;
padding-left: 1rem;
padding-right: 1rem;
}
details {
align-items: center;
display: flex;
margin-top: 1rem;
padding-left: 1rem;
padding-right: 1rem;
width: 100%;
}
details pre > span {
color: rgba(181, 181, 181, 1);
}
summary {
border-radius: 0.5rem;
display: flex;
font-weight: 400;
padding: 0.5rem;
cursor: pointer;
transition-property: background-color, border-color, color, fill, stroke,
opacity, box-shadow, transform, filter, backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
summary:hover {
background-color: rgba(243, 244, 246, 1);
}
summary svg {
height: 1.5rem;
margin-right: 1rem;
width: 1.5rem;
}
#love {
color: rgba(107, 114, 128, 1);
font-size: 0.875rem;
line-height: 1.25rem;
margin-top: 3.5rem;
opacity: 0.6;
text-align: center;
}
#love svg {
color: rgba(252, 165, 165, 1);
width: 1.25rem;
height: 1.25rem;
display: inline;
margin-top: -0.25rem;
}
@media screen and (min-width: 768px) {
#hero {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
#hero .logo-container {
display: flex;
}
#middle-content {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}

@ -1,21 +0,0 @@
import { AppElement } from './app.element';
describe('AppElement', () => {
let app: AppElement;
beforeEach(() => {
app = new AppElement();
});
it('should create successfully', () => {
expect(app).toBeTruthy();
});
it('should have a greeting', () => {
app.connectedCallback();
expect(app.querySelector('h1').innerHTML).toContain(
'Welcome @triliumnext/client'
);
});
});

@ -1,409 +0,0 @@
import './app.element.css';
export class AppElement extends HTMLElement {
public static observedAttributes = [
];
connectedCallback() {
const title = '@triliumnext/client';
this.innerHTML = `
<div class="wrapper">
<div class="container">
<!-- WELCOME -->
<div id="welcome">
<h1>
<span> Hello there, </span>
Welcome ${title} 👋
</h1>
</div>
<!-- HERO -->
<div id="hero" class="rounded">
<div class="text-container">
<h2>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"
/>
</svg>
<span>You&apos;re up and running</span>
</h2>
<a href="#commands"> What&apos;s next? </a>
</div>
<div class="logo-container">
<svg
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"
/>
</svg>
</div>
</div>
<!-- MIDDLE CONTENT -->
<div id="middle-content">
<div id="learning-materials" class="rounded shadow">
<h2>Learning materials</h2>
<a href="https://nx.dev/getting-started/intro?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
/>
</svg>
<span>
Documentation
<span> Everything is in there </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a href="https://nx.dev/blog/?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
/>
</svg>
<span>
Blog
<span> Changelog, features & events </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a href="https://www.youtube.com/@NxDevtools/videos?utm_source=nx-project&sub_confirmation=1" target="_blank" rel="noreferrer" class="list-item-link">
<svg
role="img"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<title>YouTube</title>
<path
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
/>
</svg>
<span>
YouTube channel
<span> Nx Show, talks & tutorials </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a href="https://nx.dev/react-tutorial/1-code-generation?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
/>
</svg>
<span>
Interactive tutorials
<span> Create an app, step-by-step </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a href="https://nxplaybook.com/?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M12 14l9-5-9-5-9 5 9 5z" />
<path
d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"
/>
</svg>
<span>
Video courses
<span> Nx custom courses </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
</div>
<div id="other-links">
<a id="nx-console" class="button-pill rounded shadow" href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console&utm_source=nx-project" target="_blank" rel="noreferrer">
<svg
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>Visual Studio Code</title>
<path
d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"
/>
</svg>
<span>
Install Nx Console for VSCode
<span>The official VSCode extension for Nx.</span>
</span>
</a>
<a
id="nx-console-jetbrains"
class="button-pill rounded shadow"
href="https://plugins.jetbrains.com/plugin/21060-nx-console"
target="_blank"
rel="noreferrer"
>
<svg
height="48"
width="48"
viewBox="20 20 60 60"
xmlns="http://www.w3.org/2000/svg"
>
<path d="m22.5 22.5h60v60h-60z" />
<g fill="#fff">
<path d="m29.03 71.25h22.5v3.75h-22.5z" />
<path d="m28.09 38 1.67-1.58a1.88 1.88 0 0 0 1.47.87c.64 0 1.06-.44 1.06-1.31v-5.98h2.58v6a3.48 3.48 0 0 1 -.87 2.6 3.56 3.56 0 0 1 -2.57.95 3.84 3.84 0 0 1 -3.34-1.55z" />
<path d="m36 30h7.53v2.19h-5v1.44h4.49v2h-4.42v1.49h5v2.21h-7.6z" />
<path d="m47.23 32.29h-2.8v-2.29h8.21v2.27h-2.81v7.1h-2.6z" />
<path d="m29.13 43.08h4.42a3.53 3.53 0 0 1 2.55.83 2.09 2.09 0 0 1 .6 1.53 2.16 2.16 0 0 1 -1.44 2.09 2.27 2.27 0 0 1 1.86 2.29c0 1.61-1.31 2.59-3.55 2.59h-4.44zm5 2.89c0-.52-.42-.8-1.18-.8h-1.29v1.64h1.24c.79 0 1.25-.26 1.25-.81zm-.9 2.66h-1.57v1.73h1.62c.8 0 1.24-.31 1.24-.86 0-.5-.4-.87-1.27-.87z" />
<path d="m38 43.08h4.1a4.19 4.19 0 0 1 3 1 2.93 2.93 0 0 1 .9 2.19 3 3 0 0 1 -1.93 2.89l2.24 3.27h-3l-1.88-2.84h-.87v2.84h-2.56zm4 4.5c.87 0 1.39-.43 1.39-1.11 0-.75-.54-1.12-1.4-1.12h-1.44v2.26z" />
<path d="m49.59 43h2.5l4 9.44h-2.79l-.67-1.69h-3.63l-.67 1.69h-2.71zm2.27 5.73-1-2.65-1.06 2.65z" />
<path d="m56.46 43.05h2.6v9.37h-2.6z" />
<path d="m60.06 43.05h2.42l3.37 5v-5h2.57v9.37h-2.26l-3.53-5.14v5.14h-2.57z" />
<path d="m68.86 51 1.45-1.73a4.84 4.84 0 0 0 3 1.13c.71 0 1.08-.24 1.08-.65 0-.4-.31-.6-1.59-.91-2-.46-3.53-1-3.53-2.93 0-1.74 1.37-3 3.62-3a5.89 5.89 0 0 1 3.86 1.25l-1.26 1.84a4.63 4.63 0 0 0 -2.62-.92c-.63 0-.94.25-.94.6 0 .42.32.61 1.63.91 2.14.46 3.44 1.16 3.44 2.91 0 1.91-1.51 3-3.79 3a6.58 6.58 0 0 1 -4.35-1.5z" />
</g>
</svg>
<span>
Install Nx Console for JetBrains
<span>
Available for WebStorm, Intellij IDEA Ultimate and more!
</span>
</span>
</a>
<div id="nx-cloud" class="rounded shadow">
<div>
<svg id="nx-cloud-logo" role="img" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" fill="transparent" viewBox="0 0 24 24">
<path stroke-width="2" d="M23 3.75V6.5c-3.036 0-5.5 2.464-5.5 5.5s-2.464 5.5-5.5 5.5-5.5 2.464-5.5 5.5H3.75C2.232 23 1 21.768 1 20.25V3.75C1 2.232 2.232 1 3.75 1h16.5C21.768 1 23 2.232 23 3.75Z" />
<path stroke-width="2" d="M23 6v14.1667C23 21.7307 21.7307 23 20.1667 23H6c0-3.128 2.53867-5.6667 5.6667-5.6667 3.128 0 5.6666-2.5386 5.6666-5.6666C17.3333 8.53867 19.872 6 23 6Z" />
</svg>
<h2>
Nx Cloud
<span>
Enable faster CI & better DX
</span>
</h2>
</div>
<p>
You can activate distributed tasks executions and caching by
running:
</p>
<pre>nx connect</pre>
<a href="https://nx.app/?utm_source=nx-project" target="_blank" rel="noreferrer"> What is Nx Cloud? </a>
</div>
<a id="nx-repo" class="button-pill rounded shadow" href="https://github.com/nrwl/nx?utm_source=nx-project" target="_blank" rel="noreferrer">
<svg
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
/>
</svg>
<span>
Nx is open source
<span> Love Nx? Give us a star! </span>
</span>
</a>
</div>
</div>
<!-- COMMANDS -->
<div id="commands" class="rounded shadow">
<h2>Next steps</h2>
<p>Here are some things you can do with Nx:</p>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Add UI library
</summary>
<pre><span># Generate UI lib</span>
nx g @nx/angular:lib ui
<span># Add a component</span>
nx g @nx/angular:component ui/src/lib/button</pre>
</details>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
View interactive project graph
</summary>
<pre>nx graph</pre>
</details>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Run affected commands
</summary>
<pre><span># see what&apos;s been affected by changes</span>
nx affected:graph
<span># run tests for current changes</span>
nx affected:test
<span># run e2e tests for current changes</span>
nx affected:e2e</pre>
</details>
</div>
<p id="love">
Carefully crafted with
<svg
fill="currentColor"
stroke="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
</p>
</div>
</div>
`;
}
}
customElements.define('triliumnext-root', AppElement);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Client</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<triliumnext-root></triliumnext-root>
</body>
</html>

@ -1 +0,0 @@
import './app/app.element';

@ -1 +0,0 @@
/* You can add global styles to this file, and also import other style files */

@ -1,5 +1,4 @@
import froca from "../services/froca.js";
import bundleService from "../services/bundle.js";
import RootCommandExecutor from "./root_command_executor.js";
import Entrypoints, { type SqlExecuteResults } from "./entrypoints.js";
import options from "../services/options.js";
@ -28,6 +27,7 @@ import type { NativeImage, TouchBar } from "electron";
import TouchBarComponent from "./touch_bar.js";
import type { CKTextEditor } from "@triliumnext/ckeditor5";
import type CodeMirror from "@triliumnext/codemirror";
import { StartupChecks } from "./startup_checks.js";
interface Layout {
getRootWidget: (appContext: AppContext) => RootWidget;
@ -128,6 +128,7 @@ export type CommandMappings = {
openAboutDialog: CommandData;
hideFloatingButtons: {};
hideLeftPane: CommandData;
showCpuArchWarning: CommandData;
showLeftPane: CommandData;
hoistNote: CommandData & { noteId: string };
leaveProtectedSession: CommandData;
@ -279,6 +280,7 @@ export type CommandMappings = {
buildIcon(name: string): NativeImage;
};
refreshTouchBar: CommandData;
reloadTextEditor: CommandData;
};
type EventMappings = {
@ -467,13 +469,21 @@ export class AppContext extends Component {
this.tabManager.loadTabs();
const bundleService = (await import("../services/bundle.js")).default;
setTimeout(() => bundleService.executeStartupBundles(), 2000);
}
initComponents() {
this.tabManager = new TabManager();
this.components = [this.tabManager, new RootCommandExecutor(), new Entrypoints(), new MainTreeExecutors(), new ShortcutComponent()];
this.components = [
this.tabManager,
new RootCommandExecutor(),
new Entrypoints(),
new MainTreeExecutors(),
new ShortcutComponent(),
new StartupChecks()
];
if (utils.isMobile()) {
this.components.push(new MobileScreenSwitcherExecutor());

@ -12,6 +12,7 @@ import type FNote from "../entities/fnote.js";
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
import type { CKTextEditor } from "@triliumnext/ckeditor5";
import type CodeMirror from "@triliumnext/codemirror";
import { closeActiveDialog } from "../services/dialog.js";
export interface SetNoteOpts {
triggerSwitchEvent?: unknown;
@ -83,7 +84,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
await this.triggerEvent("beforeNoteSwitch", { noteContext: this });
utils.closeActiveDialog();
closeActiveDialog();
this.notePath = resolvedNotePath;
this.viewScope = opts.viewScope;

@ -0,0 +1,26 @@
import server from "../services/server";
import Component from "./component";
// TODO: Deduplicate.
interface CpuArchResponse {
isCpuArchMismatch: boolean;
}
export class StartupChecks extends Component {
constructor() {
super();
this.checkCpuArchMismatch();
}
async checkCpuArchMismatch() {
try {
const response = await server.get("system-checks") as CpuArchResponse;
if (response.isCpuArchMismatch) {
this.triggerCommand("showCpuArchWarning", {});
}
} catch (error) {
console.warn("Could not check CPU arch status:", error);
}
}
}

@ -8,6 +8,7 @@ import electronContextMenu from "./menus/electron_context_menu.js";
import glob from "./services/glob.js";
import { t } from "./services/i18n.js";
import options from "./services/options.js";
import server from "./services/server.js";
import type ElectronRemote from "@electron/remote";
import type Electron from "electron";
import "./stylesheets/bootstrap.scss";

@ -1,7 +1,6 @@
import server from "../services/server.js";
import noteAttributeCache from "../services/note_attribute_cache.js";
import ws from "../services/ws.js";
import froca from "../services/froca.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import cssClassManager from "../services/css_class_manager.js";
import type { Froca } from "../services/froca-interface.js";
@ -410,8 +409,8 @@ class FNote {
const notePaths: NotePathRecord[] = this.getAllNotePaths().map((path) => ({
notePath: path,
isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
isArchived: path.some((noteId) => froca.notes[noteId].isArchived),
isSearch: path.some((noteId) => froca.notes[noteId].type === "search"),
isArchived: path.some((noteId) => this.froca.notes[noteId].isArchived),
isSearch: path.some((noteId) => this.froca.notes[noteId].type === "search"),
isHidden: path.includes("_hidden")
}));
@ -982,7 +981,7 @@ class FNote {
continue;
}
const parentNote = froca.notes[parentNoteId];
const parentNote = this.froca.notes[parentNoteId];
if (!parentNote || parentNote.type === "search") {
continue;

@ -21,6 +21,7 @@ import ConfirmDialog from "../widgets/dialogs/confirm.js";
import RevisionsDialog from "../widgets/dialogs/revisions.js";
import DeleteNotesDialog from "../widgets/dialogs/delete_notes.js";
import InfoDialog from "../widgets/dialogs/info.js";
import IncorrectCpuArchDialog from "../widgets/dialogs/incorrect_cpu_arch.js";
export function applyModals(rootContainer: RootContainer) {
rootContainer
@ -45,4 +46,5 @@ export function applyModals(rootContainer: RootContainer) {
.child(new InfoDialog())
.child(new ConfirmDialog())
.child(new PromptDialog())
.child(new IncorrectCpuArchDialog())
}

@ -1,204 +0,0 @@
// Source: https://github.com/codemirror/codemirror5/pull/7080/files
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function (CodeMirror) {
"use strict";
CodeMirror.defineMode("hcl", function (config) {
var indentUnit = config.indentUnit;
var keywords = {
"resource": true,
"variable": true,
"output": true,
"module": true,
"provider": true,
"data": true,
"locals": true,
"terraform": true,
"if": true,
"else": true,
"for": true,
"foreach": true,
"in": true,
"true": true,
"false": true,
"null": true,
};
var atoms = {
"true": true,
"false": true,
"null": true,
};
var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
var curPunc;
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'" || ch == "`") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
if (/[\d\.]/.test(ch)) {
if (ch == ".") {
stream.match(/^[0-9_]+([eE][\-+]?[0-9_]+)?/);
} else {
stream.match(/^[0-9_]*\.?[0-9_]*([eE][\-+]?[0-9_]+)?/);
}
return "number";
}
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
curPunc = ch;
return null;
}
if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
}
if (stream.eat("/")) {
stream.skipToEnd();
return "comment";
}
}
if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return "operator";
}
stream.eatWhile(/[\w\$_\xa1-\uffff]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
return "keyword";
}
if (atoms.propertyIsEnumerable(cur)) return "atom";
return "variable";
}
function tokenString(quote) {
return function (stream, state) {
var escaped = false,
next,
end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {
end = true;
break;
}
escaped = !escaped && quote != "`" && next == "\\";
}
if (end || !(escaped || quote == "`"))
state.tokenize = tokenBase;
return "string";
};
}
function tokenComment(stream, state) {
var maybeEnd = false,
ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return "comment";
}
function Context(indented, column, type, align, prev) {
this.indented = indented;
this.column = column;
this.type = type;
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
return state.context = new Context(state.indented, col, type, null, state.context);
}
function popContext(state) {
if (!state.context.prev) return;
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
state.indented = state.context.indented;
return state.context = state.context.prev;
}
// Interface
return {
startState: function (basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
},
token: function (stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
state.indented = stream.indentation();
state.startOfLine = true;
}
if (stream.eatSpace()) return null;
curPunc = null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style == "comment") return style;
if (ctx.align == null) ctx.align = true;
if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "}" && ctx.type == "}") popContext(state);
else if (curPunc == ctx.type) popContext(state);
state.startOfLine = false;
return style;
},
indent: function (state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (firstChar == "#" || firstChar == ";") return 0;
if (stream.sol()) {
if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
state.context.type = "}";
return ctx.indented;
}
var closing = firstChar == ctx.type;
if (ctx.align) return ctx.column + (closing ? 0 : 1);
else return ctx.indented + (closing ? 0 : indentUnit);
}
},
electricChars: "{}):",
closeBrackets: "()[]{}''\"\"``",
fold: "brace",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
};
});
CodeMirror.defineMIME("text/x-hcl", "hcl");
CodeMirror.modeInfo.push({
ext: [ "hcl " ],
mime: "text/x-hcl",
mode: "hcl",
name: "Terraform (HCL)"
});
});

@ -194,14 +194,15 @@ class ContextMenu {
return false;
});
if (!this.isMobile) {
$item.on("mouseup", (e) =>{
$item.on("mouseup", (e) => {
// Prevent submenu from failing to expand on mobile
if (!this.isMobile || !("items" in item && item.items)) {
e.stopPropagation();
// Hide the content menu on mouse up to prevent the mouse event from propagating to the elements below.
this.hide();
return false;
});
}
}
});
if ("enabled" in item && item.enabled !== undefined && !item.enabled) {
$item.addClass("disabled");

@ -23,4 +23,4 @@ export interface EntityChange {
instanceId?: string | null;
}
export type EntityType = "notes" | "branches" | "attributes" | "note_reordering" | "revisions" | "options" | "attachments" | "blobs" | "etapi_tokens" | "note_embeddings";
export type EntityType = "notes" | "branches" | "attributes" | "note_reordering" | "revisions" | "options" | "attachments" | "blobs" | "etapi_tokens";

@ -1,6 +1,6 @@
import ScriptContext from "./script_context.js";
import server from "./server.js";
import toastService from "./toast.js";
import toastService, { showError } from "./toast.js";
import froca from "./froca.js";
import utils from "./utils.js";
import { t } from "./i18n.js";
@ -37,7 +37,9 @@ async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $cont
} catch (e: any) {
const note = await froca.getNote(bundle.noteId);
toastService.showAndLogError(`Execution of JS note "${note?.title}" with ID ${bundle.noteId} failed with error: ${e?.message}`);
const message = `Execution of JS note "${note?.title}" with ID ${bundle.noteId} failed with error: ${e?.message}`;
showError(message);
logError(message);
}
}

@ -4,7 +4,7 @@ import froca from "./froca.js";
import linkService from "./link.js";
import utils from "./utils.js";
import { t } from "./i18n.js";
import toast from "./toast.js";
import { throwError } from "./ws.js";
let clipboardBranchIds: string[] = [];
let clipboardMode: string | null = null;
@ -37,7 +37,7 @@ async function pasteAfter(afterBranchId: string) {
// copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places
} else {
toastService.throwError(`Unrecognized clipboard mode=${clipboardMode}`);
throwError(`Unrecognized clipboard mode=${clipboardMode}`);
}
}
@ -69,7 +69,7 @@ async function pasteInto(parentBranchId: string) {
// copy will keep clipboardBranchIds and clipboardMode, so it's possible to paste into multiple places
} else {
toastService.throwError(`Unrecognized clipboard mode=${clipboardMode}`);
throwError(`Unrecognized clipboard mode=${clipboardMode}`);
}
}
@ -109,39 +109,6 @@ function isClipboardEmpty() {
return clipboardBranchIds.length === 0;
}
export function copyText(text: string) {
if (!text) {
return;
}
let succeeded = false;
try {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
succeeded = true;
} else {
// Fallback method: https://stackoverflow.com/a/72239825
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
succeeded = document.execCommand('copy');
document.body.removeChild(textArea);
}
} catch (e) {
console.warn(e);
succeeded = false;
}
if (succeeded) {
toast.showMessage(t("clipboard.copy_success"));
} else {
toast.showError(t("clipboard.copy_failed"));
}
}
export default {
pasteAfter,
pasteInto,

@ -0,0 +1,37 @@
export function copyText(text: string) {
if (!text) {
return;
}
try {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
return true;
} else {
// Fallback method: https://stackoverflow.com/a/72239825
const textArea = document.createElement("textarea");
textArea.value = text;
try {
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
return document.execCommand('copy');
} finally {
document.body.removeChild(textArea);
}
}
} catch (e) {
console.warn(e);
return false;
}
}
export async function copyTextWithToast(text: string) {
const t = (await import("./i18n.js")).t;
const toast = (await import("./toast.js")).default;
if (copyText(text)) {
toast.showMessage(t("clipboard.copy_success"));
} else {
toast.showError(t("clipboard.copy_failed"));
}
}

@ -1,6 +1,41 @@
import { Modal } from "bootstrap";
import appContext from "../components/app_context.js";
import type { ConfirmDialogOptions, ConfirmDialogResult, ConfirmWithMessageOptions } from "../widgets/dialogs/confirm.js";
import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
import { focusSavedElement, saveFocusedElement } from "./focus.js";
export async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
if (closeActDialog) {
closeActiveDialog();
glob.activeDialog = $dialog;
}
saveFocusedElement();
Modal.getOrCreateInstance($dialog[0]).show();
$dialog.on("hidden.bs.modal", () => {
const $autocompleteEl = $(".aa-input");
if ("autocomplete" in $autocompleteEl) {
$autocompleteEl.autocomplete("close");
}
if (!glob.activeDialog || glob.activeDialog === $dialog) {
focusSavedElement();
}
});
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
keyboardActionsService.updateDisplayedShortcuts($dialog);
return $dialog;
}
export function closeActiveDialog() {
if (glob.activeDialog) {
Modal.getOrCreateInstance(glob.activeDialog[0]).hide();
glob.activeDialog = null;
}
}
async function info(message: string) {
return new Promise((res) => appContext.triggerCommand("showInfoDialog", { message, callback: res }));

@ -0,0 +1,29 @@
let $lastFocusedElement: JQuery<HTMLElement> | null;
// perhaps there should be saved focused element per tab?
export function saveFocusedElement() {
$lastFocusedElement = $(":focus");
}
export function focusSavedElement() {
if (!$lastFocusedElement) {
return;
}
if ($lastFocusedElement.hasClass("ck")) {
// must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
// the bug manifests itself in resetting the cursor position to the first character - jumping above
const editor = $lastFocusedElement.closest(".ck-editor__editable").prop("ckeditorInstance");
if (editor) {
editor.editing.view.focus();
} else {
console.log("Could not find CKEditor instance to focus last element");
}
} else {
$lastFocusedElement.focus();
}
$lastFocusedElement = null;
}

@ -245,6 +245,10 @@ class FrocaImpl implements Froca {
}
async getNotes(noteIds: string[] | JQuery<string>, silentNotFoundError = false): Promise<FNote[]> {
if (noteIds.length === 0) {
return [];
}
noteIds = Array.from(new Set(noteIds)); // make unique
const missingNoteIds = noteIds.filter((noteId) => !this.notes[noteId]);

@ -35,7 +35,7 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
loadResults.addOption(attributeEntity.name);
} else if (ec.entityName === "attachments") {
processAttachment(loadResults, ec);
} else if (ec.entityName === "blobs" || ec.entityName === "etapi_tokens" || ec.entityName === "note_embeddings") {
} else if (ec.entityName === "blobs" || ec.entityName === "etapi_tokens") {
// NOOP - these entities are handled at the backend level and don't require frontend processing
} else {
throw new Error(`Unknown entityName '${ec.entityName}'`);

@ -26,12 +26,18 @@ function setupGlobs() {
window.onerror = function (msg, url, lineNo, columnNo, error) {
const string = String(msg).toLowerCase();
let errorObjectString = "";
try {
errorObjectString = JSON.stringify(error);
} catch (e: any) {
errorObjectString = e.toString();
}
let message = "Uncaught error: ";
if (string.includes("script error")) {
message += "No details available";
} else {
message += [`Message: ${msg}`, `URL: ${url}`, `Line: ${lineNo}`, `Column: ${columnNo}`, `Error object: ${JSON.stringify(error)}`, `Stack: ${error && error.stack}`].join(", ");
message += [`Message: ${msg}`, `URL: ${url}`, `Line: ${lineNo}`, `Column: ${columnNo}`, `Error object: ${errorObjectString}`, `Stack: ${error && error.stack}`].join(", ");
}
ws.logError(message);

@ -1,6 +1,7 @@
import { LOCALES } from "@triliumnext/commons";
import { readFileSync } from "fs";
import { join } from "path";
import { describe, expect, it } from "vitest";
describe("i18n", () => {
it("translations are valid JSON", () => {

@ -1,5 +1,5 @@
import { t } from "./i18n.js";
import toastService from "./toast.js";
import toastService, { showError } from "./toast.js";
function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
try {
@ -11,7 +11,9 @@ function copyImageReferenceToClipboard($imageWrapper: JQuery<HTMLElement>) {
if (success) {
toastService.showMessage(t("image.copied-to-clipboard"));
} else {
toastService.showAndLogError(t("image.cannot-copy"));
const message = t("image.cannot-copy");
showError(message);
logError(message);
}
} finally {
window.getSelection()?.removeAllRanges();

@ -22,6 +22,11 @@ describe("Link", () => {
expect(output).toMatchObject({ notePath: "root/WWaBNf3SSA1b/mQ2tIzLVFKHL", noteId: "mQ2tIzLVFKHL" });
});
it("parses notePath with extraWindow", () => {
const output = parseNavigationStateFromUrl(`127.0.0.1:8080/?extraWindow=1#root/QZGqKB7wVZF8?ntxId=0XPvXG`);
expect(output).toMatchObject({ notePath: "root/QZGqKB7wVZF8", noteId: "QZGqKB7wVZF8" });
});
it("ignores external URL with internal hash anchor", () => {
const output = parseNavigationStateFromUrl(`https://en.wikipedia.org/wiki/Bearded_Collie#Health`);
expect(output).toMatchObject({});

@ -218,7 +218,7 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
}
// Exclude external links that contain #
if (hashIdx !== 0 && !url.includes("/#root") && !url.includes("/#?searchString")) {
if (hashIdx !== 0 && !url.includes("/#root") && !url.includes("/#?searchString") && !url.includes("/?extraWindow")) {
return {};
}

@ -44,18 +44,7 @@ interface OptionRow {}
interface NoteReorderingRow {}
interface NoteEmbeddingRow {
embedId: string;
noteId: string;
providerId: string;
modelId: string;
dimension: number;
version: number;
dateCreated: string;
utcDateCreated: string;
dateModified: string;
utcDateModified: string;
}
type EntityRowMappings = {
notes: NoteRow;
@ -64,7 +53,6 @@ type EntityRowMappings = {
options: OptionRow;
revisions: RevisionRow;
note_reordering: NoteReorderingRow;
note_embeddings: NoteEmbeddingRow;
};
export type EntityRowNames = keyof EntityRowMappings;

@ -289,13 +289,11 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
}
if (suggestion.action === "create-note") {
const { success, noteType, templateNoteId } = await noteCreateService.chooseNoteType();
const { success, noteType, templateNoteId, notePath } = await noteCreateService.chooseNoteType();
if (!success) {
return;
}
const { note } = await noteCreateService.createNote(suggestion.parentNoteId, {
const { note } = await noteCreateService.createNote( notePath || suggestion.parentNoteId, {
title: suggestion.noteTitle,
activate: false,
type: noteType,

@ -116,7 +116,7 @@ async function chooseNoteType() {
}
async function createNoteWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) {
const { success, noteType, templateNoteId } = await chooseNoteType();
const { success, noteType, templateNoteId, notePath } = await chooseNoteType();
if (!success) {
return;
@ -125,7 +125,7 @@ async function createNoteWithTypePrompt(parentNotePath: string, options: CreateN
options.type = noteType;
options.templateNoteId = templateNoteId;
return await createNote(parentNotePath, options);
return await createNote(notePath || parentNotePath, options);
}
/* If the first element is heading, parse it out and use it as a new heading. */

@ -4,6 +4,8 @@ import { t } from "./i18n.js";
import type { MenuItem } from "../menus/context_menu.js";
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
const SEPARATOR = { title: "----" };
async function getNoteTypeItems(command?: TreeCommandNames) {
const items: MenuItem<TreeCommandNames>[] = [
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
@ -18,25 +20,59 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
...await getBuiltInTemplates(command),
...await getUserTemplates(command)
];
return items;
}
async function getUserTemplates(command?: TreeCommandNames) {
const templateNoteIds = await server.get<string[]>("search-templates");
const templateNotes = await froca.getNotes(templateNoteIds);
if (templateNotes.length === 0) {
return [];
}
const items: MenuItem<TreeCommandNames>[] = [
SEPARATOR
];
for (const templateNote of templateNotes) {
items.push({
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
});
}
return items;
}
async function getBuiltInTemplates(command?: TreeCommandNames) {
const templatesRoot = await froca.getNote("_templates");
if (!templatesRoot) {
console.warn("Unable to find template root.");
return [];
}
if (templateNotes.length > 0) {
items.push({ title: "----" });
for (const templateNote of templateNotes) {
items.push({
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
});
}
const childNotes = await templatesRoot.getChildNotes();
if (childNotes.length === 0) {
return [];
}
const items: MenuItem<TreeCommandNames>[] = [
SEPARATOR
];
for (const templateNote of childNotes) {
items.push({
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
});
}
return items;
}

@ -1,4 +1,5 @@
import server from "./server.js";
import { isShare } from "./utils.js";
type OptionValue = number | string;
@ -7,7 +8,11 @@ class Options {
private arr!: Record<string, OptionValue>;
constructor() {
this.initializedPromise = server.get<Record<string, OptionValue>>("options").then((data) => this.load(data));
if (!isShare) {
this.initializedPromise = server.get<Record<string, OptionValue>>("options").then((data) => this.load(data));
} else {
this.initializedPromise = Promise.resolve();
}
}
load(arr: Record<string, OptionValue>) {

@ -1,4 +1,4 @@
import FrontendScriptApi, { type Entity } from "./frontend_script_api.js";
import type { Entity } from "./frontend_script_api.js";
import utils from "./utils.js";
import froca from "./froca.js";
@ -14,6 +14,8 @@ async function ScriptContext(startNoteId: string, allNoteIds: string[], originEn
throw new Error(`Could not find start note ${startNoteId}.`);
}
const FrontendScriptApi = (await import("./frontend_script_api.js")).default;
return {
modules: modules,
notes: utils.toObject(allNotes, (note) => [note.noteId, note]),

@ -1,4 +1,4 @@
import utils from "./utils.js";
import utils, { isShare } from "./utils.js";
import ValidationError from "./validation_error.js";
type Headers = Record<string, string | null | undefined>;
@ -28,6 +28,10 @@ export interface StandardResponse {
}
async function getHeaders(headers?: Headers) {
if (isShare) {
return {};
}
const appContext = (await import("../components/app_context.js")).default;
const activeNoteContext = appContext.tabManager ? appContext.tabManager.getActiveContext() : null;
@ -272,7 +276,8 @@ async function reportError(method: string, url: string, statusCode: number, resp
} else {
const title = `${statusCode} ${method} ${url}`;
toastService.showErrorTitleAndMessage(title, messageStr);
toastService.throwError(`${title} - ${message}`);
const { throwError } = await import("./ws.js");
throwError(`${title} - ${message}`);
}
}

@ -2,7 +2,9 @@ import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes, type Auto
import mime_types from "./mime_types.js";
import options from "./options.js";
import { t } from "./i18n.js";
import { copyText } from "./clipboard.js";
import { copyText, copyTextWithToast } from "./clipboard_ext.js";
import { isShare } from "./utils.js";
import { MimeType } from "@triliumnext/commons";
let highlightingLoaded = false;
@ -14,9 +16,6 @@ let highlightingLoaded = false;
*/
export async function formatCodeBlocks($container: JQuery<HTMLElement>) {
const syntaxHighlightingEnabled = isSyntaxHighlightEnabled();
if (syntaxHighlightingEnabled) {
await ensureMimeTypesForHighlighting();
}
const codeBlocks = $container.find("pre code");
for (const codeBlock of codeBlocks) {
@ -37,7 +36,13 @@ export function applyCopyToClipboardButton($codeBlock: JQuery<HTMLElement>) {
const $copyButton = $("<button>")
.addClass("bx component icon-action tn-tool-button bx-copy copy-button")
.attr("title", t("code_block.copy_title"))
.on("click", () => copyText($codeBlock.text()));
.on("click", () => {
if (!isShare) {
copyTextWithToast($codeBlock.text());
} else {
copyText($codeBlock.text());
}
});
$codeBlock.parent().append($copyButton);
}
@ -49,11 +54,11 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle
const text = $codeBlock.text();
let highlightedText: HighlightResult | AutoHighlightResult | null = null;
if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) {
if (normalizedMimeType === mime_types.MIME_TYPE_AUTO && !isShare) {
await ensureMimeTypesForHighlighting();
highlightedText = highlightAuto(text);
} else if (normalizedMimeType) {
await ensureMimeTypesForHighlighting();
await ensureMimeTypesForHighlighting(normalizedMimeType);
highlightedText = highlight(text, { language: normalizedMimeType });
}
@ -62,7 +67,7 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle
}
}
export async function ensureMimeTypesForHighlighting() {
export async function ensureMimeTypesForHighlighting(mimeTypeHint?: string) {
if (highlightingLoaded) {
return;
}
@ -72,7 +77,20 @@ export async function ensureMimeTypesForHighlighting() {
loadHighlightingTheme(currentThemeName);
// Load mime types.
const mimeTypes = mime_types.getMimeTypes();
let mimeTypes: MimeType[];
if (mimeTypeHint) {
mimeTypes = [
{
title: mimeTypeHint,
enabled: true,
mime: mimeTypeHint.replace("-", "/")
}
]
} else {
mimeTypes = mime_types.getMimeTypes();
}
await ensureMimeTypes(mimeTypes);
highlightingLoaded = true;
@ -96,8 +114,12 @@ export function loadHighlightingTheme(themeName: string) {
* @returns whether syntax highlighting should be enabled for code blocks.
*/
export function isSyntaxHighlightEnabled() {
const theme = options.get("codeBlockTheme");
return !!theme && theme !== "none";
if (!isShare) {
const theme = options.get("codeBlockTheme");
return !!theme && theme !== "none";
} else {
return true;
}
}
/**

@ -78,13 +78,7 @@ function showMessage(message: string, delay = 2000) {
});
}
function showAndLogError(message: string, delay = 10000) {
showError(message, delay);
ws.logError(message);
}
function showError(message: string, delay = 10000) {
export function showError(message: string, delay = 10000) {
console.log(utils.now(), "error: ", message);
toast({
@ -108,18 +102,10 @@ function showErrorTitleAndMessage(title: string, message: string, delay = 10000)
});
}
function throwError(message: string) {
ws.logError(message);
throw new Error(message);
}
export default {
showMessage,
showError,
showErrorTitleAndMessage,
showAndLogError,
throwError,
showPersistent,
closePersistent
};

@ -1,9 +1,10 @@
import dayjs from "dayjs";
import { Modal } from "bootstrap";
import type { ViewScope } from "./link.js";
const SVG_MIME = "image/svg+xml";
export const isShare = !window.glob;
function reloadFrontendApp(reason?: string) {
if (reason) {
logInfo(`Frontend app reload: ${reason}`);
@ -273,71 +274,6 @@ function getMimeTypeClass(mime: string) {
return `mime-${mime.toLowerCase().replace(/[\W_]+/g, "-")}`;
}
function closeActiveDialog() {
if (glob.activeDialog) {
Modal.getOrCreateInstance(glob.activeDialog[0]).hide();
glob.activeDialog = null;
}
}
let $lastFocusedElement: JQuery<HTMLElement> | null;
// perhaps there should be saved focused element per tab?
function saveFocusedElement() {
$lastFocusedElement = $(":focus");
}
function focusSavedElement() {
if (!$lastFocusedElement) {
return;
}
if ($lastFocusedElement.hasClass("ck")) {
// must handle CKEditor separately because of this bug: https://github.com/ckeditor/ckeditor5/issues/607
// the bug manifests itself in resetting the cursor position to the first character - jumping above
const editor = $lastFocusedElement.closest(".ck-editor__editable").prop("ckeditorInstance");
if (editor) {
editor.editing.view.focus();
} else {
console.log("Could not find CKEditor instance to focus last element");
}
} else {
$lastFocusedElement.focus();
}
$lastFocusedElement = null;
}
async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
if (closeActDialog) {
closeActiveDialog();
glob.activeDialog = $dialog;
}
saveFocusedElement();
Modal.getOrCreateInstance($dialog[0]).show();
$dialog.on("hidden.bs.modal", () => {
const $autocompleteEl = $(".aa-input");
if ("autocomplete" in $autocompleteEl) {
$autocompleteEl.autocomplete("close");
}
if (!glob.activeDialog || glob.activeDialog === $dialog) {
focusSavedElement();
}
});
// TODO: Fix once keyboard_actions is ported.
// @ts-ignore
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
keyboardActionsService.updateDisplayedShortcuts($dialog);
return $dialog;
}
function isHtmlEmpty(html: string) {
if (!html) {
return true;
@ -823,10 +759,6 @@ export default {
setCookie,
getNoteTypeClass,
getMimeTypeClass,
closeActiveDialog,
openDialog,
saveFocusedElement,
focusSavedElement,
isHtmlEmpty,
clearBrowserCache,
copySelectionToClipboard,

@ -17,7 +17,7 @@ let lastProcessedEntityChangeId = window.glob.maxEntityChangeIdAtLoad;
let lastPingTs: number;
let frontendUpdateDataQueue: EntityChange[] = [];
function logError(message: string) {
export function logError(message: string) {
console.error(utils.now(), message); // needs to be separate from .trace()
if (ws && ws.readyState === 1) {
@ -301,6 +301,12 @@ setTimeout(() => {
setInterval(sendPing, 1000);
}, 0);
export function throwError(message: string) {
logError(message);
throw new Error(message);
}
export default {
logError,
subscribeToMessages,

@ -1,5 +1,33 @@
import "normalize.css";
import "boxicons/css/boxicons.min.css";
import "@triliumnext/ckeditor5/content.css";
import "@triliumnext/share-theme/styles/index.css";
import "@triliumnext/share-theme/scripts/index.js";
async function ensureJQuery() {
const $ = (await import("jquery")).default;
(window as any).$ = $;
}
async function applyMath() {
const anyMathBlock = document.querySelector("#content .math-tex");
if (!anyMathBlock) {
return;
}
const renderMathInElement = (await import("./services/math.js")).renderMathInElement;
renderMathInElement(document.getElementById("content"));
}
async function formatCodeBlocks() {
const anyCodeBlock = document.querySelector("#content pre");
if (!anyCodeBlock) {
return;
}
await ensureJQuery();
const { formatCodeBlocks } = await import("./services/syntax_highlight.js");
await formatCodeBlocks($("#content"));
}
/**
* Fetch note with given ID from backend
@ -19,6 +47,9 @@ async function fetchNote(noteId: string | null = null) {
document.addEventListener(
"DOMContentLoaded",
() => {
formatCodeBlocks();
applyMath();
const toggleMenuButton = document.getElementById("toggleMenuButton");
const layout = document.getElementById("layout");

@ -25,6 +25,7 @@
--bs-body-font-weight: var(--main-font-weight) !important;
--bs-body-color: var(--main-text-color) !important;
--bs-body-bg: var(--main-background-color) !important;
--ck-mention-list-max-height: 500px;
}
.table {
@ -391,7 +392,7 @@ body.desktop .dropdown-menu {
}
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
body.desktop #context-menu-container .dropdown-item > span {
body #context-menu-container .dropdown-item > span {
display: flex;
align-items: center;
}
@ -439,10 +440,11 @@ body.desktop #context-menu-container .dropdown-item > span {
border-radius: 6px;
overflow: hidden;
margin: 4px;
font-size: var(--monospace-font-size);
}
body .cm-editor {
font-size: var(--monospace-font-size);
.cm-scroller {
font-family: var(--monospace-font-family) !important;
}
body .cm-editor .cm-gutters {
@ -1273,6 +1275,29 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
white-space: normal !important;
}
/* Slash commands */
.ck.ck-slash-command-button {
padding: 0.5em 1em !important;
}
.ck.ck-slash-command-button__text-part,
.ck.ck-template-form__text-part {
margin-left: 0.5em;
line-height: 1.2em !important;
}
.ck.ck-slash-command-button__text-part > span,
.ck.ck-template-form__text-part > span {
line-height: inherit !important;
}
.ck.ck-slash-command-button__text-part .ck.ck-slash-command-button__description,
.ck.ck-template-form__text-part .ck-template-form__description {
display: block;
opacity: 0.8;
}
.area-expander {
display: flex;
flex-direction: row;
@ -1788,7 +1813,9 @@ body.zen #left-pane,
body.zen #right-pane,
body.zen .tab-row-container,
body.zen .tab-row-widget,
body.zen .ribbon-container,
body.zen .ribbon-container:not(:has(.classic-toolbar-widget.visible)),
body.zen .ribbon-container:has(.classic-toolbar-widget.visible) .ribbon-top-row,
body.zen .ribbon-container .ribbon-body:not(:has(.classic-toolbar-widget.visible)),
body.zen .note-icon-widget,
body.zen .title-row .button-widget,
body.zen .floating-buttons-children > *:not(.bx-edit-alt) {

@ -395,4 +395,20 @@ div.tn-tool-dialog {
padding-right: 12px;
font-weight: normal;
white-space: nowrap;
}
/*
* NOTE TYPE CHOOSER DIALOG
*/
.note-type-chooser-dialog div.note-type-dropdown {
/* Disable the active item highlighting since there is no use for it here */
--active-item-text-color: initial;
--active-item-background-color: initial;
font-size: unset;
}
.note-type-chooser-dialog div.note-type-dropdown .dropdown-item span.bx {
margin-right: .25em;
}

@ -267,7 +267,7 @@ input::selection,
}
.input-group button:focus-visible,
.input-group a:focus-visible {
.input-group a:focus-visible:not(.dropdown-item) {
box-shadow: unset;
outline: transparent;
border: transparent;
@ -349,7 +349,7 @@ select:hover,
select.form-select:hover,
select.form-control:hover,
.select-button.dropdown-toggle.btn:hover {
background: var(--input-hover-background) var(--dropdown-arrow);
background: var(--input-hover-background) var(--dropdown-arrow,);
color: var(--input-hover-color);
}

@ -201,6 +201,11 @@
color: var(--menu-item-icon-color);
}
/* Slash commands */
.ck.ck-slash-command-button__text-part .ck.ck-button__label {
font-weight: bold;
}
/* Separator */
:root .ck .ck-list__separator {
margin: .5em 0;

@ -142,6 +142,12 @@ div.note-detail-empty {
border: unset;
}
/* NOTE ATTACHMENTS */
.attachment-list div.links-wrapper {
font-size: unset;
}
/*
* OPTIONS PAGES
*/

@ -354,7 +354,7 @@ body.layout-horizontal > .horizontal {
}
.calendar-dropdown-widget .calendar-header .calendar-month-selector .select-button {
--select-arrow-svg: ""; /* Disable the dropdown arrow */
--select-arrow-svg: initial; /* Disable the dropdown arrow */
}
@media (max-width: 992px) {
@ -1145,12 +1145,18 @@ body.mobile .note-title {
/* The "Change note icon" button */
.note-icon-widget .note-icon {
:root .note-icon-widget button.note-icon,
:root .note-icon-widget button.note-icon:hover {
border: none;
border-radius: 8px;
}
.note-icon-widget .note-icon:hover {
/* Dropdown open */
:root .note-icon-widget button.note-icon.show {
background: var(--ck-editor-toolbar-dropdown-button-open-background);
}
:root .note-icon-widget button.note-icon:not(:disabled):hover {
background: var(--icon-button-hover-background);
color: var(--icon-button-hover-color);
}

@ -1333,7 +1333,7 @@
"recovery_keys_used": "已使用: {{date}}",
"recovery_keys_unused": "恢复代码 {{index}} 未使用",
"oauth_title": "OAuth/OpenID 认证",
"oauth_description": "OpenID 是一种标准化方式,允许您使用其他服务(如 Google的账户登录网站,以验证您的身份。请参阅这些 <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">指南</a> 通过 Google 设置 OpenID 服务。",
"oauth_description": "OpenID 是一种标准化方式,允许您使用其他服务(如 Google的账号登录网站来验证您的身份。默认的身份提供者是 Google但您可以更改为任何其他 OpenID 提供者。点击<a href=\"#root/_hidden/_help/_help_Otzi9La2YAUX/_help_WOcw2SLH6tbX/_help_7DAiwaf8Z7Rz\">这里</a>了解更多信息。请参阅这些 <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">指南</a> 通过 Google 设置 OpenID 服务。",
"oauth_description_warning": "要启用 OAuth/OpenID您需要设置 config.ini 文件中的 OAuth/OpenID 基础 URL、客户端 ID 和客户端密钥,并重新启动应用程序。如果要从环境变量设置,请设置 TRILIUM_OAUTH_BASE_URL、TRILIUM_OAUTH_CLIENT_ID 和 TRILIUM_OAUTH_CLIENT_SECRET 环境变量。",
"oauth_missing_vars": "缺少以下设置项: {{missingVars}}",
"oauth_user_account": "用户账号:",

@ -233,6 +233,8 @@
"move_success_message": "Selected notes have been moved into "
},
"note_type_chooser": {
"change_path_prompt": "Change where to create the new note:",
"search_placeholder": "search path by name (default if empty)",
"modal_title": "Choose note type",
"close": "Close",
"modal_body": "Choose note type / template of the new note:",
@ -1124,10 +1126,8 @@
"layout-horizontal-description": "launcher bar is underneath the tab bar, the tab bar is now full width."
},
"ai_llm": {
"embeddings_configuration": "Embeddings Configuration",
"not_started": "Not started",
"title": "AI & Embedding Settings",
"embedding_statistics": "Embedding Statistics",
"title": "AI Settings",
"processed_notes": "Processed Notes",
"total_notes": "Total Notes",
"progress": "Progress",
@ -1135,7 +1135,6 @@
"failed_notes": "Failed Notes",
"last_processed": "Last Processed",
"refresh_stats": "Refresh Statistics",
"no_failed_embeddings": "No failed embeddings found.",
"enable_ai_features": "Enable AI/LLM features",
"enable_ai_description": "Enable AI features like note summarization, content generation, and other LLM capabilities",
"openai_tab": "OpenAI",
@ -1160,20 +1159,16 @@
"anthropic_api_key_description": "Your Anthropic API key for accessing Claude models",
"default_model": "Default Model",
"openai_model_description": "Examples: gpt-4o, gpt-4-turbo, gpt-3.5-turbo",
"embedding_model": "Embedding Model",
"openai_embedding_model_description": "Model used for generating embeddings (text-embedding-3-small recommended)",
"base_url": "Base URL",
"openai_url_description": "Default: https://api.openai.com/v1",
"anthropic_settings": "Anthropic Settings",
"anthropic_url_description": "Base URL for the Anthropic API (default: https://api.anthropic.com)",
"anthropic_model_description": "Anthropic Claude models for chat completion",
"voyage_settings": "Voyage AI Settings",
"voyage_api_key_description": "Your Voyage AI API key for accessing embeddings services",
"ollama_settings": "Ollama Settings",
"ollama_url_description": "URL for the Ollama API (default: http://localhost:11434)",
"ollama_model_description": "Ollama model to use for chat completion",
"anthropic_configuration": "Anthropic Configuration",
"voyage_embedding_model_description": "Voyage AI embedding models for text embeddings (voyage-2 recommended)",
"voyage_configuration": "Voyage AI Configuration",
"voyage_url_description": "Default: https://api.voyageai.com/v1",
"ollama_configuration": "Ollama Configuration",
@ -1181,28 +1176,10 @@
"enable_ollama_description": "Enable Ollama for local AI model usage",
"ollama_url": "Ollama URL",
"ollama_model": "Ollama Model",
"ollama_embedding_model": "Embedding Model",
"ollama_embedding_model_description": "Specialized model for generating embeddings (vector representations)",
"refresh_models": "Refresh Models",
"refreshing_models": "Refreshing...",
"embedding_configuration": "Embeddings Configuration",
"embedding_default_provider": "Default Provider",
"embedding_default_provider_description": "Select the default provider used for generating note embeddings",
"embedding_provider_precedence": "Embedding Provider Precedence",
"embedding_providers_order": "Embedding Provider Order",
"embedding_providers_order_description": "Set the order of embedding providers in comma-separated format (e.g., \"openai,voyage,ollama,local\")",
"enable_automatic_indexing": "Enable Automatic Indexing",
"enable_automatic_indexing_description": "Automatically generate embeddings for new and updated notes",
"embedding_auto_update_enabled": "Auto-update Embeddings",
"embedding_auto_update_enabled_description": "Automatically update embeddings when notes are modified",
"recreate_embeddings": "Recreate All Embeddings",
"recreate_embeddings_description": "Regenerate all note embeddings from scratch (may take a long time for large note collections)",
"recreate_embeddings_started": "Embeddings regeneration started. This may take a long time for large note collections.",
"recreate_embeddings_error": "Error starting embeddings regeneration. Check logs for details.",
"recreate_embeddings_confirm": "Are you sure you want to recreate all embeddings? This may take a long time for large note collections.",
"rebuild_index": "Rebuild Index",
"rebuild_index_description": "Rebuild the vector search index for better performance (much faster than recreating embeddings)",
"rebuild_index_started": "Embedding index rebuild started. This may take several minutes.",
"rebuild_index_error": "Error starting index rebuild. Check logs for details.",
"note_title": "Note Title",
"error": "Error",
@ -1212,43 +1189,16 @@
"partial": "{{ percentage }}% completed",
"retry_queued": "Note queued for retry",
"retry_failed": "Failed to queue note for retry",
"embedding_provider_precedence_description": "Comma-separated list of providers in order of precedence for embeddings search (e.g., 'openai,ollama,anthropic')",
"embedding_dimension_strategy": "Embedding Dimension Strategy",
"embedding_dimension_auto": "Auto (Recommended)",
"embedding_dimension_fixed": "Fixed",
"embedding_similarity_threshold": "Similarity Threshold",
"embedding_similarity_threshold_description": "Minimum similarity score for notes to be included in search results (0-1)",
"max_notes_per_llm_query": "Max Notes Per Query",
"max_notes_per_llm_query_description": "Maximum number of similar notes to include in AI context",
"embedding_dimension_strategy_description": "Choose how embeddings are handled. 'Native' preserves maximum information by adapting smaller vectors to match larger ones (recommended). 'Regenerate' creates new embeddings with the target model for specific search needs.",
"drag_providers_to_reorder": "Drag providers up or down to set your preferred order for embedding searches",
"active_providers": "Active Providers",
"disabled_providers": "Disabled Providers",
"remove_provider": "Remove provider from search",
"restore_provider": "Restore provider to search",
"embedding_generation_location": "Generation Location",
"embedding_generation_location_description": "Select where embedding generation should happen",
"embedding_generation_location_client": "Client/Server",
"embedding_generation_location_sync_server": "Sync Server",
"enable_auto_update_embeddings": "Auto-update Embeddings",
"enable_auto_update_embeddings_description": "Automatically update embeddings when notes are modified",
"auto_update_embeddings": "Auto-update Embeddings",
"auto_update_embeddings_desc": "Automatically update embeddings when notes are modified",
"similarity_threshold": "Similarity Threshold",
"similarity_threshold_description": "Minimum similarity score (0-1) for notes to be included in context for LLM queries",
"embedding_batch_size": "Batch Size",
"embedding_batch_size_description": "Number of notes to process in a single batch (1-50)",
"embedding_update_interval": "Update Interval (ms)",
"embedding_update_interval_description": "Time between processing batches of embeddings (in milliseconds)",
"embedding_default_dimension": "Default Dimension",
"embedding_default_dimension_description": "Default embedding vector dimension when creating new embeddings",
"reprocess_all_embeddings": "Reprocess All Embeddings",
"reprocess_all_embeddings_description": "Queue all notes for embedding processing. This may take some time depending on your number of notes.",
"reprocessing_embeddings": "Reprocessing...",
"reprocess_started": "Embedding reprocessing started in the background",
"reprocess_error": "Error starting embedding reprocessing",
"reprocess_index": "Rebuild Search Index",
"reprocess_index_description": "Optimize the search index for better performance. This uses existing embeddings without regenerating them (much faster than reprocessing all embeddings).",
"reprocessing_index": "Rebuilding...",
"reprocess_index_started": "Search index optimization started in the background",
"reprocess_index_error": "Error rebuilding search index",
@ -1261,7 +1211,6 @@
"incomplete": "Incomplete ({{percentage}}%)",
"complete": "Complete (100%)",
"refreshing": "Refreshing...",
"stats_error": "Error fetching embedding statistics",
"auto_refresh_notice": "Auto-refreshes every {{seconds}} seconds",
"note_queued_for_retry": "Note queued for retry",
"failed_to_retry_note": "Failed to retry note",
@ -1269,7 +1218,6 @@
"failed_to_retry_all": "Failed to retry notes",
"ai_settings": "AI Settings",
"api_key_tooltip": "API key for accessing the service",
"confirm_delete_embeddings": "Are you sure you want to delete all AI embeddings? This will remove all semantic search capabilities until notes are reindexed, which can take a significant amount of time.",
"empty_key_warning": {
"anthropic": "Anthropic API key is empty. Please enter a valid API key.",
"openai": "OpenAI API key is empty. Please enter a valid API key.",
@ -1302,7 +1250,6 @@
"note_chat": "Note Chat",
"notes_indexed": "{{ count }} note indexed",
"notes_indexed_plural": "{{ count }} notes indexed",
"reset_embeddings": "Reset Embeddings",
"sources": "Sources",
"start_indexing": "Start Indexing",
"use_advanced_context": "Use Advanced Context",
@ -1315,24 +1262,11 @@
},
"create_new_ai_chat": "Create new AI Chat",
"configuration_warnings": "There are some issues with your AI configuration. Please check your settings.",
"embeddings_started": "Embedding generation started",
"embeddings_stopped": "Embedding generation stopped",
"embeddings_toggle_error": "Error toggling embeddings",
"local_embedding_description": "Uses local embedding models for offline text embedding generation",
"local_embedding_settings": "Local Embedding Settings",
"ollama_embedding_settings": "Ollama Embedding Settings",
"ollama_embedding_url_description": "URL for the Ollama API for embedding generation (default: http://localhost:11434)",
"openai_embedding_api_key_description": "Your OpenAI API key for embedding generation (can be different from chat API key)",
"openai_embedding_settings": "OpenAI Embedding Settings",
"openai_embedding_url_description": "Base URL for OpenAI embedding API (default: https://api.openai.com/v1)",
"selected_embedding_provider": "Selected Embedding Provider",
"selected_embedding_provider_description": "Choose the provider for generating note embeddings",
"experimental_warning": "The LLM feature is currently experimental - you have been warned.",
"selected_provider": "Selected Provider",
"selected_provider_description": "Choose the AI provider for chat and completion features",
"select_embedding_provider": "Select embedding provider...",
"select_model": "Select model...",
"select_provider": "Select provider...",
"voyage_embedding_url_description": "Base URL for the Voyage AI embedding API (default: https://api.voyageai.com/v1)"
"select_provider": "Select provider..."
},
"zoom_factor": {
"title": "Zoom Factor (desktop build only)",
@ -1561,7 +1495,7 @@
"recovery_keys_used": "Used: {{date}}",
"recovery_keys_unused": "Recovery code {{index}} is unused",
"oauth_title": "OAuth/OpenID",
"oauth_description": "OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity. Follow these <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">instructions</a> to setup an OpenID service through Google.",
"oauth_description": "OpenID is a standardized way to let you log into websites using an account from another service, like Google, to verify your identity. The default issuer is Google, but you can change it to any other OpenID provider. Check <a href=\"#root/_hidden/_help/_help_Otzi9La2YAUX/_help_WOcw2SLH6tbX/_help_7DAiwaf8Z7Rz\">here</a> for more information. Follow these <a href=\"https://developers.google.com/identity/openid-connect/openid-connect\">instructions</a> to setup an OpenID service through Google.",
"oauth_description_warning": "To enable OAuth/OpenID, you need to set the OAuth/OpenID base URL, client ID and client secret in the config.ini file and restart the application. If you want to set from environment variables, please set TRILIUM_OAUTH_BASE_URL, TRILIUM_OAUTH_CLIENT_ID and TRILIUM_OAUTH_CLIENT_SECRET.",
"oauth_missing_vars": "Missing settings: {{variables}}",
"oauth_user_account": "User Account: ",
@ -1986,5 +1920,14 @@
"title": "Appearance",
"word_wrapping": "Word wrapping",
"color-scheme": "Color scheme"
},
"cpu_arch_warning": {
"title": "Please download the ARM64 version",
"message_macos": "TriliumNext is currently running under Rosetta 2 translation, which means you're using the Intel (x64) version on Apple Silicon Mac. This will significantly impact performance and battery life.",
"message_windows": "TriliumNext is currently running emulation, which means you're using the Intel (x64) version on a Windows on ARM device. This will significantly impact performance and battery life.",
"recommendation": "For the best experience, please download the native ARM64 version of TriliumNext from our releases page.",
"download_link": "Download Native Version",
"continue_anyway": "Continue Anyway",
"dont_show_again": "Don't show this warning again"
}
}

@ -16,7 +16,7 @@
"message": "Ha ocurrido un error crítico que previene que el cliente de la aplicación inicie:\n\n{{message}}\n\nMuy probablemente es causado por un script que falla de forma inesperada. Intente iniciar la aplicación en modo seguro y atienda el error."
},
"widget-error": {
"title": "No se pudo inicializar un widget",
"title": "Hubo un fallo al inicializar un widget",
"message-custom": "El widget personalizado de la nota con ID \"{{id}}\", titulada \"{{title}}\" no pudo ser inicializado debido a:\n\n{{message}}",
"message-unknown": "Un widget no pudo ser inicializado debido a:\n\n{{message}}"
},
@ -127,6 +127,7 @@
"collapseSubTree": "colapsar subárbol",
"tabShortcuts": "Atajos de pestañas",
"newTabNoteLink": "<kbd>CTRL+clic</kbd> - (o clic central del mouse) en el enlace de la nota abre la nota en una nueva pestaña",
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+clic</kbd> - (o <kbd>Shift+clic de rueda de ratón</kbd>) en el enlace de la nota abre y activa la nota en una nueva pestaña",
"onlyInDesktop": "Solo en escritorio (compilación con Electron)",
"openEmptyTab": "abrir pestaña vacía",
"closeActiveTab": "cerrar pestaña activa",
@ -232,6 +233,8 @@
"move_success_message": "Las notas seleccionadas se han movido a "
},
"note_type_chooser": {
"change_path_prompt": "Cambiar donde se creará la nueva nota:",
"search_placeholder": "ruta de búsqueda por nombre (por defecto si está vacío)",
"modal_title": "Elija el tipo de nota",
"close": "Cerrar",
"modal_body": "Elija el tipo de nota/plantilla de la nueva nota:",
@ -274,9 +277,9 @@
"revision_last_edited": "Esta revisión se editó por última vez en {{date}}",
"confirm_delete_all": "¿Quiere eliminar todas las revisiones de esta nota?",
"no_revisions": "Aún no hay revisiones para esta nota...",
"restore_button": "",
"restore_button": "Restaurar",
"confirm_restore": "¿Quiere restaurar esta revisión? Esto sobrescribirá el título actual y el contenido de la nota con esta revisión.",
"delete_button": "",
"delete_button": "Eliminar",
"confirm_delete": "¿Quieres eliminar esta revisión?",
"revisions_deleted": "Se han eliminado las revisiones de nota.",
"revision_restored": "Se ha restaurado la revisión de nota.",
@ -588,6 +591,7 @@
"sat": "Sáb",
"sun": "Dom",
"cannot_find_day_note": "No se puede encontrar la nota del día",
"cannot_find_week_note": "No se puede encontrar la nota de la semana",
"january": "Enero",
"febuary": "Febrero",
"march": "Marzo",
@ -1121,6 +1125,148 @@
"layout-vertical-description": "la barra del lanzador está en la izquierda (por defecto)",
"layout-horizontal-description": "la barra de lanzamiento está debajo de la barra de pestañas, la barra de pestañas ahora tiene ancho completo."
},
"ai_llm": {
"not_started": "No iniciado",
"title": "IA y ajustes de embeddings",
"processed_notes": "Notas procesadas",
"total_notes": "Notas totales",
"progress": "Progreso",
"queued_notes": "Notas en fila",
"failed_notes": "Notas fallidas",
"last_processed": "Última procesada",
"refresh_stats": "Recargar estadísticas",
"enable_ai_features": "Habilitar características IA/LLM",
"enable_ai_description": "Habilitar características de IA como resumen de notas, generación de contenido y otras capacidades LLM",
"openai_tab": "OpenAI",
"anthropic_tab": "Anthropic",
"voyage_tab": "Voyage AI",
"ollama_tab": "Ollama",
"enable_ai": "Habilitar características IA/LLM",
"enable_ai_desc": "Habilitar características de IA como resumen de notas, generación de contenido y otras capacidades LLM",
"provider_configuration": "Configuración de proveedor de IA",
"provider_precedence": "Precedencia de proveedor",
"provider_precedence_description": "Lista de proveedores en orden de precedencia separada por comas (p.e., 'openai,anthropic,ollama')",
"temperature": "Temperatura",
"temperature_description": "Controla la aleatoriedad de las respuestas (0 = determinista, 2 = aleatoriedad máxima)",
"system_prompt": "Mensaje de sistema",
"system_prompt_description": "Mensaje de sistema predeterminado utilizado para todas las interacciones de IA",
"openai_configuration": "Configuración de OpenAI",
"openai_settings": "Ajustes de OpenAI",
"api_key": "Clave API",
"url": "URL base",
"model": "Modelo",
"openai_api_key_description": "Tu clave API de OpenAI para acceder a sus servicios de IA",
"anthropic_api_key_description": "Tu clave API de Anthropic para acceder a los modelos Claude",
"default_model": "Modelo por defecto",
"openai_model_description": "Ejemplos: gpt-4o, gpt-4-turbo, gpt-3.5-turbo",
"base_url": "URL base",
"openai_url_description": "Por defecto: https://api.openai.com/v1",
"anthropic_settings": "Ajustes de Anthropic",
"anthropic_url_description": "URL base para la API de Anthropic (por defecto: https://api.anthropic.com)",
"anthropic_model_description": "Modelos Claude de Anthropic para el completado de chat",
"voyage_settings": "Ajustes de Voyage AI",
"ollama_settings": "Ajustes de Ollama",
"ollama_url_description": "URL para la API de Ollama (por defecto: http://localhost:11434)",
"ollama_model_description": "Modelo de Ollama a usar para el completado de chat",
"anthropic_configuration": "Configuración de Anthropic",
"voyage_configuration": "Configuración de Voyage AI",
"voyage_url_description": "Por defecto: https://api.voyageai.com/v1",
"ollama_configuration": "Configuración de Ollama",
"enable_ollama": "Habilitar Ollama",
"enable_ollama_description": "Habilitar Ollama para uso de modelo de IA local",
"ollama_url": "URL de Ollama",
"ollama_model": "Modelo de Ollama",
"refresh_models": "Refrescar modelos",
"refreshing_models": "Refrescando...",
"enable_automatic_indexing": "Habilitar indexado automático",
"rebuild_index": "Recrear índice",
"rebuild_index_error": "Error al comenzar la reconstrucción del índice. Consulte los registros para más detalles.",
"note_title": "Título de nota",
"error": "Error",
"last_attempt": "Último intento",
"actions": "Acciones",
"retry": "Reintentar",
"partial": "{{ percentage }}% completado",
"retry_queued": "Nota en la cola para reintento",
"retry_failed": "Hubo un fallo al poner en la cola a la nota para reintento",
"max_notes_per_llm_query": "Máximo de notas por consulta",
"max_notes_per_llm_query_description": "Número máximo de notas similares a incluir en el contexto IA",
"active_providers": "Proveedores activos",
"disabled_providers": "Proveedores deshabilitados",
"remove_provider": "Eliminar proveedor de la búsqueda",
"restore_provider": "Restaurar proveedor a la búsqueda",
"similarity_threshold": "Bias de similaridad",
"similarity_threshold_description": "Puntuación de similaridad mínima (0-1) para incluir notas en el contexto para consultas LLM",
"reprocess_index": "Reconstruir el índice de búsqueda",
"reprocessing_index": "Reconstruyendo...",
"reprocess_index_started": "La optimización de índice de búsqueda comenzó en segundo plano",
"reprocess_index_error": "Error al reconstruir el índice de búsqueda",
"index_rebuild_progress": "Progreso de reconstrucción de índice",
"index_rebuilding": "Optimizando índice ({{percentage}}%)",
"index_rebuild_complete": "Optimización de índice completa",
"index_rebuild_status_error": "Error al comprobar el estado de reconstrucción del índice",
"never": "Nunca",
"processing": "Procesando ({{percentage}}%)",
"incomplete": "Incompleto ({{percentage}}%)",
"complete": "Completo (100%)",
"refreshing": "Refrescando...",
"auto_refresh_notice": "Refrescar automáticamente cada {{seconds}} segundos",
"note_queued_for_retry": "Nota en la cola para reintento",
"failed_to_retry_note": "Hubo un fallo al reintentar nota",
"all_notes_queued_for_retry": "Todas las notas con fallo agregadas a la cola para reintento",
"failed_to_retry_all": "Hubo un fallo al reintentar notas",
"ai_settings": "Ajustes de IA",
"api_key_tooltip": "Clave API para acceder al servicio",
"empty_key_warning": {
"anthropic": "La clave API de Anthropic está vacía. Por favor, ingrese una clave API válida.",
"openai": "La clave API de OpenAI está vacía. Por favor, ingrese una clave API válida.",
"voyage": "La clave API de Voyage está vacía. Por favor, ingrese una clave API válida.",
"ollama": "La clave API de Ollama está vacía. Por favor, ingrese una clave API válida."
},
"agent": {
"processing": "Procesando...",
"thinking": "Pensando...",
"loading": "Cargando...",
"generating": "Generando..."
},
"name": "IA",
"openai": "OpenAI",
"use_enhanced_context": "Utilizar contexto mejorado",
"enhanced_context_description": "Provee a la IA con más contexto de la nota y sus notas relacionadas para obtener mejores respuestas",
"show_thinking": "Mostrar pensamiento",
"show_thinking_description": "Mostrar la cadena del proceso de pensamiento de la IA",
"enter_message": "Ingrese su mensaje...",
"error_contacting_provider": "Error al contactar con su proveedor de IA. Por favor compruebe sus ajustes y conexión a internet.",
"error_generating_response": "Error al generar respuesta de IA",
"index_all_notes": "Indexar todas las notas",
"index_status": "Estado de índice",
"indexed_notes": "Notas indexadas",
"indexing_stopped": "Indexado detenido",
"indexing_in_progress": "Indexado en progreso...",
"last_indexed": "Último indexado",
"n_notes_queued": "{{ count }} nota agregada a la cola para indexado",
"n_notes_queued_plural": "{{ count }} notas agregadas a la cola para indexado",
"note_chat": "Chat de nota",
"notes_indexed": "{{ count }} nota indexada",
"notes_indexed_plural": "{{ count }} notas indexadas",
"sources": "Fuentes",
"start_indexing": "Comenzar indexado",
"use_advanced_context": "Usar contexto avanzado",
"ollama_no_url": "Ollama no está configurado. Por favor ingrese una URL válida.",
"chat": {
"root_note_title": "Chats de IA",
"root_note_content": "Esta nota contiene tus conversaciones de chat de IA guardadas.",
"new_chat_title": "Nuevo chat",
"create_new_ai_chat": "Crear nuevo chat de IA"
},
"create_new_ai_chat": "Crear nuevo chat de IA",
"configuration_warnings": "Hay algunos problemas con su configuración de IA. Por favor compruebe sus ajustes.",
"experimental_warning": "La característica de LLM aún es experimental - ha sido advertido.",
"selected_provider": "Proveedor seleccionado",
"selected_provider_description": "Elija el proveedor de IA para el chat y características de completado",
"select_model": "Seleccionar modelo...",
"select_provider": "Seleccionar proveedor..."
},
"zoom_factor": {
"title": "Factor de zoom (solo versión de escritorio)",
"description": "El zoom también se puede controlar con los atajos CTRL+- y CTRL+=."
@ -1236,12 +1382,26 @@
"label": "Tamaño para modo de solo lectura automático (notas de texto)",
"unit": "caracteres"
},
"custom_date_time_format": {
"title": "Formato de fecha/hora personalizada",
"description": "Personalizar el formado de fecha y la hora insertada vía <kbd></kbd> o la barra de herramientas. Véa la <a href=\"https://day.js.org/docs/en/display/format\" target=\"_blank\" rel=\"noopener noreferrer\">documentación de Day.js</a> para más tokens de formato disponibles.",
"format_string": "Cadena de formato:",
"formatted_time": "Fecha/hora personalizada:"
},
"i18n": {
"title": "Localización",
"language": "Idioma",
"first-day-of-the-week": "Primer día de la semana",
"sunday": "Domingo",
"monday": "Lunes"
"monday": "Lunes",
"first-week-of-the-year": "Primer semana del año",
"first-week-contains-first-day": "Primer semana que contiene al primer día del año",
"first-week-contains-first-thursday": "Primer semana que contiene al primer jueves del año",
"first-week-has-minimum-days": "Primer semana que contiene un mínimo de días",
"min-days-in-first-week": "Días mínimos en la primer semana",
"first-week-info": "Primer semana que contiene al primer jueves del año está basado en el estándar<a href=\"https://en.wikipedia.org/wiki/ISO_week_date#First_week\">ISO 8601</a>.",
"first-week-warning": "Cambiar las opciones de primer semana puede causar duplicados con las Notas Semanales existentes y las Notas Semanales existentes no serán actualizadas respectivamente.",
"formatting-locale": "Fecha y formato de número"
},
"backup": {
"automatic_backup": "Copia de seguridad automática",
@ -1308,6 +1468,39 @@
"password_mismatch": "Las nuevas contraseñas no son las mismas.",
"password_changed_success": "La contraseña ha sido cambiada. Trilium se recargará después de presionar Aceptar."
},
"multi_factor_authentication": {
"title": "Autenticación Multi-Factor",
"description": "La autenticación multifactor (MFA) agrega una capa adicional de seguridad a su cuenta. En lugar de solo ingresar una contraseña para iniciar sesión, MFA requiere que proporcione una o más pruebas adicionales para verificar su identidad. De esta manera, incluso si alguien se apodera de su contraseña, aún no puede acceder a su cuenta sin la segunda pieza de información. Es como agregar una cerradura adicional a su puerta, lo que hace que sea mucho más difícil para cualquier otra persona entrar.<br><br>Por favor siga las instrucciones a continuación para habilitar MFA. Si no lo configura correctamente, el inicio de sesión volverá a solo contraseña.",
"mfa_enabled": "Habilitar la autenticación multifactor",
"mfa_method": "Método MFA",
"electron_disabled": "Actualmente la autenticación multifactor no está soportada en la compilación de escritorio.",
"totp_title": "Contraseña de un solo uso basada en el tiempo (TOTP)",
"totp_description": "TOTP (contraseña de un solo uso basada en el tiempo) es una característica de seguridad que genera un código temporal único que cambia cada 30 segundos. Utiliza este código, junto con su contraseña para iniciar sesión en su cuenta, lo que hace que sea mucho más difícil para cualquier otra persona acceder a ella.",
"totp_secret_title": "Generar secreto TOTP",
"totp_secret_generate": "Generar secreto TOTP",
"totp_secret_regenerate": "Regenerar secreto TOTP",
"no_totp_secret_warning": "Para habilitar TOTP, primero debe de generar un secreto TOTP.",
"totp_secret_description_warning": "Después de generar un nuevo secreto TOTP, le será requerido que inicie sesión otra vez con el nuevo secreto TOTP.",
"totp_secret_generated": "Secreto TOTP generado",
"totp_secret_warning": "Por favor guarde el secreto generado en una ubicación segura. No será mostrado de nuevo.",
"totp_secret_regenerate_confirm": "¿Está seguro que desea regenerar el secreto TOTP? Esto va a invalidar el secreto TOTP previo y todos los códigos de recuperación existentes.",
"recovery_keys_title": "Claves de recuperación para un solo inicio de sesión",
"recovery_keys_description": "Las claves de recuperación para un solo inicio de sesión son usadas para iniciar sesión incluso cuando no puede acceder a los códigos de su autentificador.",
"recovery_keys_description_warning": "Las claves de recuperación no son mostrada de nuevo después de dejar esta página, manténgalas en un lugar seguro.<br>Después de que una clave de recuperación es utilizada ya no puede utilizarse de nuevo.",
"recovery_keys_error": "Error al generar códigos de recuperación",
"recovery_keys_no_key_set": "No hay códigos de recuperación establecidos",
"recovery_keys_generate": "Generar códigos de recuperación",
"recovery_keys_regenerate": "Regenerar códigos de recuperación",
"recovery_keys_used": "Usado: {{date}}",
"recovery_keys_unused": "El código de recuperación {{index}} está sin usar",
"oauth_title": "OAuth/OpenID",
"oauth_description": "OpenID es una forma estandarizada de permitirle iniciar sesión en sitios web utilizando una cuenta de otro servicio, como Google, para verificar su identidad. Siga estas <a href = \"https://developers.google.com/identity/openid-connect/openid-connect\">instrucciones</a> para configurar un servicio OpenID a través de Google.",
"oauth_description_warning": "Para habilitar OAuth/OpenID, necesita establecer la URL base de OAuth/OpenID, ID de cliente y secreto de cliente en el archivo config.ini y reiniciar la aplicación. Si desea establecerlas desde variables de ambiente, por favor establezca TRILIUM_OAUTH_BASE_URL, TRILIUM_OAUTH_CLIENT_ID y TRILIUM_OAUTH_CLIENT_SECRET.",
"oauth_missing_vars": "Ajustes faltantes: {{variables}}",
"oauth_user_account": "Cuenta de usuario: ",
"oauth_user_email": "Correo electrónico de usuario: ",
"oauth_user_not_logged_in": "¡No ha iniciado sesión!"
},
"shortcuts": {
"keyboard_shortcuts": "Atajos de teclado",
"multiple_shortcuts": "Varios atajos para la misma acción se pueden separar mediante comas.",
@ -1431,7 +1624,9 @@
"widget": "Widget",
"confirm-change": "No es recomendado cambiar el tipo de nota cuando el contenido de la nota no está vacío. ¿Desea continuar de cualquier manera?",
"geo-map": "Mapa Geo",
"beta-feature": "Beta"
"beta-feature": "Beta",
"ai-chat": "Chat de IA",
"task-list": "Lista de tareas"
},
"protect_note": {
"toggle-on": "Proteger la nota",
@ -1541,7 +1736,9 @@
},
"clipboard": {
"cut": "La(s) notas(s) han sido cortadas al portapapeles.",
"copied": "La(s) notas(s) han sido copiadas al portapapeles."
"copied": "La(s) notas(s) han sido copiadas al portapapeles.",
"copy_failed": "No se puede copiar al portapapeles debido a problemas de permisos.",
"copy_success": "Copiado al portapapeles."
},
"entrypoints": {
"note-revision-created": "Una revisión de nota ha sido creada.",
@ -1584,7 +1781,7 @@
"auto-detect-language": "Detectado automáticamente"
},
"highlighting": {
"title": "",
"title": "Bloques de código",
"description": "Controla el resaltado de sintaxis para bloques de código dentro de las notas de texto, las notas de código no serán afectadas.",
"color-scheme": "Esquema de color"
},
@ -1592,7 +1789,8 @@
"word_wrapping": "Ajuste de palabras",
"theme_none": "Sin resaltado de sintaxis",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas oscuros"
"theme_group_dark": "Temas oscuros",
"copy_title": "Copiar al portapapeles"
},
"classic_editor_toolbar": {
"title": "Formato"
@ -1713,5 +1911,22 @@
},
"png_export_button": {
"button_title": "Exportar diagrama como PNG"
},
"svg": {
"export_to_png": "El diagrama no pudo ser exportado a PNG."
},
"code_theme": {
"title": "Apariencia",
"word_wrapping": "Ajuste de palabras",
"color-scheme": "Esquema de color"
},
"cpu_arch_warning": {
"title": "Por favor descargue la versión ARM64",
"message_macos": "TriliumNext está siendo ejecutado bajo traducción Rosetta 2, lo que significa que está usando la versión Intel (x64) en Apple Silicon Mac. Esto impactará significativamente en el rendimiento y la vida de la batería.",
"message_windows": "TriliumNext está siendo ejecutado bajo emulación, lo que significa que está usando la version Intel (x64) en Windows en un dispositivo ARM. Esto impactará significativamente en el rendimiento y la vida de la batería.",
"recommendation": "Para la mejor experiencia, por favor descargue la versión nativa ARM64 de TriliumNext desde nuestra página de lanzamientos.",
"download_link": "Descargar versión nativa",
"continue_anyway": "Continuar de todas maneras",
"dont_show_again": "No mostrar esta advertencia otra vez"
}
}

@ -3,7 +3,14 @@ declare module "*.png" {
export default path;
}
declare module "@triliumnext/ckeditor5/emoji_definitions/en.json?url" {
declare module "*?url" {
var path: string;
export default path;
}
declare module "*?raw" {
var content: string;
export default content;
}
declare module "boxicons/css/boxicons.min.css" { }

@ -57,6 +57,8 @@ declare global {
process?: ElectronProcess;
glob?: CustomGlobals;
EXCALIDRAW_ASSET_PATH?: string;
}
interface AutoCompleteConfig {

@ -0,0 +1,16 @@
/// <reference types="vite/client" />
interface ViteTypeOptions {
strictImportMetaEnv: unknown
}
interface ImportMetaEnv {
/** The license key for CKEditor premium features. */
readonly VITE_CKEDITOR_KEY?: string;
/** Whether to enable the CKEditor inspector (see https://ckeditor.com/docs/ckeditor5/latest/framework/develpment-tools/inspector.html). */
readonly VITE_CKEDITOR_ENABLE_INSPECTOR?: "true" | "false";
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

@ -11,6 +11,7 @@ import utils from "../../services/utils.js";
import shortcutService from "../../services/shortcuts.js";
import appContext from "../../components/app_context.js";
import type { Attribute } from "../../services/attribute_parser.js";
import { focusSavedElement, saveFocusedElement } from "../../services/focus.js";
const TPL = /*html*/`
<div class="attr-detail tn-tool-dialog">
@ -483,7 +484,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
return;
}
utils.saveFocusedElement();
saveFocusedElement();
this.attrType = this.getAttrType(attribute);
@ -605,7 +606,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.hide();
utils.focusSavedElement();
focusSavedElement();
}
async cancelAndClose() {
@ -613,7 +614,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.hide();
utils.focusSavedElement();
focusSavedElement();
}
userEditedAttribute() {

@ -4,6 +4,7 @@ import BasicWidget from "../basic_widget.js";
import openService from "../../services/open.js";
import server from "../../services/server.js";
import utils from "../../services/utils.js";
import { openDialog } from "../../services/dialog.js";
interface AppInfo {
appVersion: string;
@ -111,6 +112,6 @@ export default class AboutDialog extends BasicWidget {
async openAboutDialogEvent() {
await this.refresh();
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
}

@ -1,11 +1,11 @@
import { t } from "../../services/i18n.js";
import treeService from "../../services/tree.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import type { Suggestion } from "../../services/note_autocomplete.js";
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="add-link-dialog modal mx-auto" tabindex="-1" role="dialog">
@ -111,7 +111,7 @@ export default class AddLinkDialog extends BasicWidget {
this.updateTitleSettingsVisibility();
utils.openDialog(this.$widget);
await openDialog(this.$widget);
this.$autoComplete.val("");
this.$linkTitle.val("");

@ -2,11 +2,11 @@ import treeService from "../../services/tree.js";
import server from "../../services/server.js";
import froca from "../../services/froca.js";
import toastService from "../../services/toast.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import appContext from "../../components/app_context.js";
import { t } from "../../services/i18n.js";
import { Modal } from "bootstrap";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`<div class="branch-prefix-dialog modal fade mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
@ -93,7 +93,7 @@ export default class BranchPrefixDialog extends BasicWidget {
}
await this.refresh(notePath);
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
async savePrefix() {

@ -1,11 +1,11 @@
import BasicWidget from "../basic_widget.js";
import froca from "../../services/froca.js";
import bulkActionService from "../../services/bulk_action.js";
import utils from "../../services/utils.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
@ -104,7 +104,7 @@ export default class BulkActionsDialog extends BasicWidget {
});
toastService.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
utils.closeActiveDialog();
closeActiveDialog();
});
}
@ -170,6 +170,6 @@ export default class BulkActionsDialog extends BasicWidget {
this.$includeDescendants.prop("checked", false);
await this.refresh();
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
}

@ -1,5 +1,4 @@
import noteAutocompleteService from "../../services/note_autocomplete.js";
import utils from "../../services/utils.js";
import treeService from "../../services/tree.js";
import toastService from "../../services/toast.js";
import froca from "../../services/froca.js";
@ -8,6 +7,7 @@ import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
@ -94,7 +94,7 @@ export default class CloneToDialog extends BasicWidget {
}
}
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.$noteAutoComplete.val("").trigger("focus");
this.$noteList.empty();

@ -1,10 +1,10 @@
import server from "../../services/server.js";
import froca from "../../services/froca.js";
import linkService from "../../services/link.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import type { FAttributeRow } from "../../entities/fattribute.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
// TODO: Use common with server.
interface Response {
@ -119,13 +119,13 @@ export default class DeleteNotesDialog extends BasicWidget {
this.$widget.on("shown.bs.modal", () => this.$okButton.trigger("focus"));
this.$cancelButton.on("click", () => {
utils.closeActiveDialog();
closeActiveDialog();
this.resolve({ proceed: false });
});
this.$okButton.on("click", () => {
utils.closeActiveDialog();
closeActiveDialog();
this.resolve({
proceed: true,
@ -179,7 +179,7 @@ export default class DeleteNotesDialog extends BasicWidget {
await this.renderDeletePreview();
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.$deleteAllClones.prop("checked", !!forceDeleteAllClones).prop("disabled", !!forceDeleteAllClones);

@ -8,6 +8,7 @@ import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
import { Modal } from "bootstrap";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="export-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -214,7 +215,7 @@ export default class ExportDialog extends BasicWidget {
this.$widget.find(".opml-v2").prop("checked", true); // setting default
utils.openDialog(this.$widget);
openDialog(this.$widget);
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);

@ -1,6 +1,6 @@
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="help-dialog modal use-tn-links" tabindex="-1" role="dialog">
@ -155,6 +155,6 @@ export default class HelpDialog extends BasicWidget {
}
showCheatsheetEvent() {
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
}

@ -1,4 +1,4 @@
import utils, { escapeQuotes } from "../../services/utils.js";
import { escapeQuotes } from "../../services/utils.js";
import treeService from "../../services/tree.js";
import importService, { type UploadFilesOptions } from "../../services/import.js";
import options from "../../services/options.js";
@ -6,6 +6,7 @@ import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import { Modal, Tooltip } from "bootstrap";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -155,7 +156,7 @@ export default class ImportDialog extends BasicWidget {
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
async importIntoNote(parentNoteId: string) {

@ -1,12 +1,12 @@
import { t } from "../../services/i18n.js";
import treeService from "../../services/tree.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
import utils from "../../services/utils.js";
import froca from "../../services/froca.js";
import BasicWidget from "../basic_widget.js";
import { Modal } from "bootstrap";
import type { EventData } from "../../components/app_context.js";
import type EditableTextTypeWidget from "../type_widgets/editable_text.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="include-note-dialog modal mx-auto" tabindex="-1" role="dialog">
@ -83,7 +83,7 @@ export default class IncludeNoteDialog extends BasicWidget {
async showIncludeNoteDialogEvent({ textTypeWidget }: EventData<"showIncludeDialog">) {
this.textTypeWidget = textTypeWidget;
await this.refresh();
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
}

@ -0,0 +1,59 @@
import BasicWidget from "../basic_widget.js";
import { Modal } from "bootstrap";
import utils from "../../services/utils.js";
import { t } from "../../services/i18n.js";
const TPL = /*html*/`
<div class="cpu-arch-dialog modal mx-auto" tabindex="-1" role="dialog" style="z-index: 2000;">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${t("cpu_arch_warning.title")}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>${utils.isMac() ? t("cpu_arch_warning.message_macos") : t("cpu_arch_warning.message_windows")}</p>
<p>${t("cpu_arch_warning.recommendation")}</p>
</div>
<div class="modal-footer d-flex justify-content-between align-items-center">
<button class="download-correct-version-button btn btn-primary btn-lg me-2">
<span class="bx bx-download"></span>
${t("cpu_arch_warning.download_link")}
</button>
<button class="btn btn-secondary" data-bs-dismiss="modal">${t("cpu_arch_warning.continue_anyway")}</button>
</div>
</div>
</div>
</div>`;
export default class IncorrectCpuArchDialog extends BasicWidget {
private modal!: Modal;
private $downloadButton!: JQuery<HTMLElement>;
doRender() {
this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$downloadButton = this.$widget.find(".download-correct-version-button");
this.$downloadButton.on("click", () => {
// Open the releases page where users can download the correct version
if (utils.isElectron()) {
const { shell } = utils.dynamicRequire("electron");
shell.openExternal("https://github.com/TriliumNext/Notes/releases/latest");
} else {
window.open("https://github.com/TriliumNext/Notes/releases/latest", "_blank");
}
});
// Auto-focus the download button when shown
this.$widget.on("shown.bs.modal", () => {
this.$downloadButton.trigger("focus");
});
}
showCpuArchWarningEvent() {
this.modal.show();
}
}

@ -1,9 +1,9 @@
import type { EventData } from "../../components/app_context.js";
import { t } from "../../services/i18n.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import { Modal } from "bootstrap";
import type { ConfirmDialogCallback } from "./confirm.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="info-dialog modal mx-auto" tabindex="-1" role="dialog" style="z-index: 2000;">
@ -72,7 +72,7 @@ export default class InfoDialog extends BasicWidget {
}
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.resolve = callback;
}

@ -5,6 +5,7 @@ import appContext from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
import { Modal } from "bootstrap";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`<div class="jump-to-note-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
@ -54,7 +55,7 @@ export default class JumpToNoteDialog extends BasicWidget {
}
async jumpToNoteEvent() {
const dialogPromise = utils.openDialog(this.$widget);
const dialogPromise = openDialog(this.$widget);
if (utils.isMobile()) {
dialogPromise.then(($dialog) => {
const el = $dialog.find(">.modal-dialog")[0];

@ -6,6 +6,7 @@ import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
import server from "../../services/server.js";
import { Modal } from "bootstrap";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="markdown-import-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -69,6 +70,7 @@ export default class MarkdownImportDialog extends BasicWidget {
const modelFragment = textEditor.data.toModel(viewFragment);
textEditor.model.insertContent(modelFragment, textEditor.model.document.selection);
textEditor.editing.view.focus();
toastService.showMessage(t("markdown_import.import_success"));
}
@ -88,7 +90,7 @@ export default class MarkdownImportDialog extends BasicWidget {
this.convertMarkdownToHtml(text);
} else {
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
}

@ -1,5 +1,4 @@
import noteAutocompleteService from "../../services/note_autocomplete.js";
import utils from "../../services/utils.js";
import toastService from "../../services/toast.js";
import froca from "../../services/froca.js";
import branchService from "../../services/branches.js";
@ -7,6 +6,7 @@ import treeService from "../../services/tree.js";
import BasicWidget from "../basic_widget.js";
import { t } from "../../services/i18n.js";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="move-to-dialog modal mx-auto" tabindex="-1" role="dialog">
@ -83,7 +83,7 @@ export default class MoveToDialog extends BasicWidget {
async moveBranchIdsToEvent({ branchIds }: EventData<"moveBranchIdsTo">) {
this.movedBranchIds = branchIds;
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.$noteAutoComplete.val("").trigger("focus");

@ -2,6 +2,7 @@ import type { CommandNames } from "../../components/app_context.js";
import type { MenuCommandItem } from "../../menus/context_menu.js";
import { t } from "../../services/i18n.js";
import noteTypesService from "../../services/note_types.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
import BasicWidget from "../basic_widget.js";
import { Dropdown, Modal } from "bootstrap";
@ -13,6 +14,11 @@ const TPL = /*html*/`
z-index: 1100 !important;
}
.note-type-chooser-dialog .input-group {
margin-top: 15px;
margin-bottom: 15px;
}
.note-type-chooser-dialog .note-type-dropdown {
position: relative;
font-size: large;
@ -30,6 +36,12 @@ const TPL = /*html*/`
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("note_type_chooser.close")}"></button>
</div>
<div class="modal-body">
${t("note_type_chooser.change_path_prompt")}
<div class="input-group">
<input class="choose-note-path form-control" placeholder="${t("note_type_chooser.search_placeholder")}">
</div>
${t("note_type_chooser.modal_body")}
<div class="dropdown" style="display: flex;">
@ -37,7 +49,7 @@ const TPL = /*html*/`
data-bs-toggle="dropdown" data-bs-display="static">
</button>
<div class="note-type-dropdown dropdown-menu"></div>
<div class="note-type-dropdown dropdown-menu static"></div>
</div>
</div>
</div>
@ -48,6 +60,7 @@ export interface ChooseNoteTypeResponse {
success: boolean;
noteType?: string;
templateNoteId?: string;
notePath?: string;
}
type Callback = (data: ChooseNoteTypeResponse) => void;
@ -57,6 +70,7 @@ export default class NoteTypeChooserDialog extends BasicWidget {
private dropdown!: Dropdown;
private modal!: Modal;
private $noteTypeDropdown!: JQuery<HTMLElement>;
private $autoComplete!: JQuery<HTMLElement>;
private $originalFocused: JQuery<HTMLElement> | null;
private $originalDialog: JQuery<HTMLElement> | null;
@ -71,7 +85,8 @@ export default class NoteTypeChooserDialog extends BasicWidget {
doRender() {
this.$widget = $(TPL);
this.modal = Modal.getOrCreateInstance(this.$widget[0]);
this.$autoComplete = this.$widget.find(".choose-note-path");
this.$noteTypeDropdown = this.$widget.find(".note-type-dropdown");
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find(".note-type-dropdown-trigger")[0]);
@ -116,9 +131,20 @@ export default class NoteTypeChooserDialog extends BasicWidget {
});
}
async refresh() {
noteAutocompleteService
.initNoteAutocomplete(this.$autoComplete, {
allowCreatingNotes: false,
hideGoToSelectedNoteButton: true,
allowJumpToSearchNotes: false,
})
}
async chooseNoteTypeEvent({ callback }: { callback: Callback }) {
this.$originalFocused = $(":focus");
await this.refresh();
const noteTypes = await noteTypesService.getNoteTypeItems();
this.$noteTypeDropdown.empty();
@ -153,12 +179,14 @@ export default class NoteTypeChooserDialog extends BasicWidget {
const $item = $(e.target).closest(".dropdown-item");
const noteType = $item.attr("data-note-type");
const templateNoteId = $item.attr("data-template-note-id");
const notePath = this.$autoComplete.getSelectedNotePath() || undefined;
if (this.resolve) {
this.resolve({
success: true,
noteType,
templateNoteId
templateNoteId,
notePath
});
}
this.resolve = null;

@ -1,5 +1,5 @@
import { openDialog } from "../../services/dialog.js";
import { t } from "../../services/i18n.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import { Modal } from "bootstrap";
@ -37,6 +37,6 @@ export default class PasswordNoteSetDialog extends BasicWidget {
}
showPasswordNotSetEvent() {
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
}

@ -1,5 +1,5 @@
import { openDialog } from "../../services/dialog.js";
import { t } from "../../services/i18n.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import { Modal } from "bootstrap";
@ -110,6 +110,6 @@ export default class PromptDialog extends BasicWidget {
this.$dialogBody.empty().append($("<div>").addClass("form-group").append(this.$question).append(this.$answer));
utils.openDialog(this.$widget, false);
openDialog(this.$widget, false);
}
}

@ -1,6 +1,6 @@
import { openDialog } from "../../services/dialog.js";
import { t } from "../../services/i18n.js";
import protectedSessionService from "../../services/protected_session.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
import { Modal } from "bootstrap";
@ -49,7 +49,7 @@ export default class ProtectedSessionPasswordDialog extends BasicWidget {
}
showProtectedSessionPasswordDialogEvent() {
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.$passwordInput.trigger("focus");
}

@ -2,13 +2,12 @@ import { formatDateTime } from "../../utils/formatters.js";
import { t } from "../../services/i18n.js";
import appContext, { type EventData } from "../../components/app_context.js";
import BasicWidget from "../basic_widget.js";
import dialogService from "../../services/dialog.js";
import dialogService, { openDialog } from "../../services/dialog.js";
import froca from "../../services/froca.js";
import hoistedNoteService from "../../services/hoisted_note.js";
import linkService from "../../services/link.js";
import server from "../../services/server.js";
import toastService from "../../services/toast.js";
import utils from "../../services/utils.js";
import ws from "../../services/ws.js";
import { Modal } from "bootstrap";
@ -62,7 +61,7 @@ export default class RecentChangesDialog extends BasicWidget {
await this.refresh();
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
async refresh() {

@ -6,7 +6,7 @@ import appContext from "../../components/app_context.js";
import openService from "../../services/open.js";
import protectedSessionHolder from "../../services/protected_session_holder.js";
import BasicWidget from "../basic_widget.js";
import dialogService from "../../services/dialog.js";
import dialogService, { openDialog } from "../../services/dialog.js";
import options from "../../services/options.js";
import type FNote from "../../entities/fnote.js";
import type { NoteType } from "../../entities/fnote.js";
@ -182,7 +182,7 @@ export default class RevisionsDialog extends BasicWidget {
return;
}
utils.openDialog(this.$widget);
openDialog(this.$widget);
await this.loadRevisions(noteId);
}

@ -1,7 +1,7 @@
import type { EventData } from "../../components/app_context.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
import { t } from "../../services/i18n.js";
import server from "../../services/server.js";
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
const TPL = /*html*/`<div class="sort-child-notes-dialog modal mx-auto" tabindex="-1" role="dialog">
@ -97,14 +97,14 @@ export default class SortChildNotesDialog extends BasicWidget {
await server.put(`notes/${this.parentNoteId}/sort-children`, { sortBy, sortDirection, foldersFirst, sortNatural, sortLocale });
utils.closeActiveDialog();
closeActiveDialog();
});
}
async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
this.parentNoteId = node.data.noteId;
utils.openDialog(this.$widget);
openDialog(this.$widget);
this.$form.find("input:first").focus();
}

@ -1,11 +1,12 @@
import { t } from "../../services/i18n.js";
import utils, { escapeQuotes } from "../../services/utils.js";
import { escapeQuotes } from "../../services/utils.js";
import treeService from "../../services/tree.js";
import importService from "../../services/import.js";
import options from "../../services/options.js";
import BasicWidget from "../basic_widget.js";
import { Modal, Tooltip } from "bootstrap";
import type { EventData } from "../../components/app_context.js";
import { openDialog } from "../../services/dialog.js";
const TPL = /*html*/`
<div class="upload-attachments-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -98,7 +99,7 @@ export default class UploadAttachmentsDialog extends BasicWidget {
this.$noteTitle.text(await treeService.getNoteTitle(this.parentNoteId));
utils.openDialog(this.$widget);
openDialog(this.$widget);
}
async uploadAttachments(parentNoteId: string) {

@ -52,9 +52,9 @@ export default class CodeButtonsWidget extends NoteContextAwareWidget {
toastService.showMessage(t("code_buttons.opening_api_docs_message"));
if (this.note?.mime.endsWith("frontend")) {
window.open("https://zadam.github.io/trilium/frontend_api/FrontendScriptApi.html", "_blank");
window.open("https://triliumnext.github.io/Notes/Script%20API/interfaces/Frontend_Script_API.Api.html", "_blank");
} else {
window.open("https://zadam.github.io/trilium/backend_api/BackendScriptApi.html", "_blank");
window.open("https://triliumnext.github.io/Notes/Script%20API/interfaces/Backend_Script_API.Api.html", "_blank");
}
});

@ -56,6 +56,8 @@ export default class ContextualHelpButton extends NoteContextAwareWidget {
return byNoteType[note.type];
} else if (note?.hasLabel("calendarRoot")) {
return "l0tKav7yLHGF";
} else if (note?.hasLabel("textSnippet")) {
return "pwc194wlRzcH";
} else if (note && note.type === "book") {
return byBookType[note.getAttributeValue("label", "viewType") as ViewTypeOptions ?? ""]
}

@ -258,9 +258,3 @@ export async function getDirectResponse(noteId: string, messageParams: any): Pro
}
}
/**
* Get embedding statistics
*/
export async function getEmbeddingStats(): Promise<any> {
return server.get('llm/embeddings/stats');
}

@ -11,7 +11,7 @@ import { TPL, addMessageToChat, showSources, hideSources, showLoadingIndicator,
import { formatMarkdown } from "./utils.js";
import { createChatSession, checkSessionExists, setupStreamingResponse, getDirectResponse } from "./communication.js";
import { extractInChatToolSteps } from "./message_processor.js";
import { validateEmbeddingProviders } from "./validation.js";
import { validateProviders } from "./validation.js";
import type { MessageData, ToolExecutionStep, ChatData } from "./types.js";
import { formatCodeBlocks } from "../../services/syntax_highlight.js";
import { ClassicEditor, type CKTextEditor, type MentionFeed } from "@triliumnext/ckeditor5";
@ -350,6 +350,115 @@ export default class LlmChatPanel extends BasicWidget {
}
}
/**
* Save current chat data to a specific note ID
*/
async saveCurrentDataToSpecificNote(targetNoteId: string | null) {
if (!this.onSaveData || !targetNoteId) {
console.warn('Cannot save chat data: no saveData callback or no targetNoteId available');
return;
}
try {
// Extract current tool execution steps if any exist
const toolSteps = extractInChatToolSteps(this.noteContextChatMessages);
// Get tool executions from both UI and any cached executions in metadata
let toolExecutions: Array<{
id: string;
name: string;
arguments: any;
result: any;
error?: string;
timestamp: string;
}> = [];
// First include any tool executions already in metadata (from streaming events)
if (this.metadata?.toolExecutions && Array.isArray(this.metadata.toolExecutions)) {
toolExecutions = [...this.metadata.toolExecutions];
console.log(`Including ${toolExecutions.length} tool executions from metadata`);
}
// Also extract any visible tool steps from the UI
const extractedExecutions = toolSteps.map(step => {
// Parse tool execution information
if (step.type === 'tool-execution') {
try {
const content = JSON.parse(step.content);
return {
id: content.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
name: content.tool || 'unknown',
arguments: content.args || {},
result: content.result || {},
error: content.error,
timestamp: new Date().toISOString()
};
} catch (e) {
// If we can't parse it, create a basic record
return {
id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
name: 'unknown',
arguments: {},
result: step.content,
timestamp: new Date().toISOString()
};
}
} else if (step.type === 'result' && step.name) {
// Handle result steps with a name
return {
id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
name: step.name,
arguments: {},
result: step.content,
timestamp: new Date().toISOString()
};
}
return {
id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
name: 'unknown',
arguments: {},
result: 'Unrecognized tool step',
timestamp: new Date().toISOString()
};
});
// Merge the tool executions, keeping only unique IDs
const existingIds = new Set(toolExecutions.map((t: {id: string}) => t.id));
for (const exec of extractedExecutions) {
if (!existingIds.has(exec.id)) {
toolExecutions.push(exec);
existingIds.add(exec.id);
}
}
const dataToSave = {
messages: this.messages,
noteId: targetNoteId,
chatNoteId: targetNoteId, // For backward compatibility
toolSteps: toolSteps,
// Add sources if we have them
sources: this.sources || [],
// Add metadata
metadata: {
model: this.metadata?.model || undefined,
provider: this.metadata?.provider || undefined,
temperature: this.metadata?.temperature || 0.7,
lastUpdated: new Date().toISOString(),
// Add tool executions
toolExecutions: toolExecutions
}
};
console.log(`Saving chat data to specific note ${targetNoteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`);
// Save the data to the note attribute via the callback
// This is the ONLY place we should save data, letting the container widget handle persistence
await this.onSaveData(dataToSave);
} catch (error) {
console.error('Error saving chat data to specific note:', error);
}
}
/**
* Load saved chat data from the note attribute
*/
@ -616,7 +725,7 @@ export default class LlmChatPanel extends BasicWidget {
}
// Check for any provider validation issues when refreshing
await validateEmbeddingProviders(this.validationWarning);
await validateProviders(this.validationWarning);
// Get current note context if needed
const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null;
@ -767,7 +876,7 @@ export default class LlmChatPanel extends BasicWidget {
*/
private async processUserMessage(content: string) {
// Check for validation issues first
await validateEmbeddingProviders(this.validationWarning);
await validateProviders(this.validationWarning);
// Make sure we have a valid session
if (!this.noteId) {
@ -867,8 +976,8 @@ export default class LlmChatPanel extends BasicWidget {
this.showSources(postResponse.sources);
}
// Process the assistant response
this.processAssistantResponse(postResponse.content, postResponse);
// Process the assistant response with original chat note ID
this.processAssistantResponse(postResponse.content, postResponse, this.noteId);
hideLoadingIndicator(this.loadingIndicator);
return true;
@ -884,7 +993,7 @@ export default class LlmChatPanel extends BasicWidget {
/**
* Process an assistant response - add to UI and save
*/
private async processAssistantResponse(content: string, fullResponse?: any) {
private async processAssistantResponse(content: string, fullResponse?: any, originalChatNoteId?: string | null) {
// Add the response to the chat UI
this.addMessageToChat('assistant', content);
@ -910,8 +1019,8 @@ export default class LlmChatPanel extends BasicWidget {
];
}
// Save to note
this.saveCurrentData().catch(err => {
// Save to note - use original chat note ID if provided
this.saveCurrentDataToSpecificNote(originalChatNoteId || this.noteId).catch(err => {
console.error("Failed to save assistant response to note:", err);
});
}
@ -936,12 +1045,15 @@ export default class LlmChatPanel extends BasicWidget {
timestamp: string;
}> = [];
// Store the original chat note ID to ensure we save to the correct note even if user switches
const originalChatNoteId = this.noteId;
return setupStreamingResponse(
this.noteId,
messageParams,
// Content update handler
(content: string, isDone: boolean = false) => {
this.updateStreamingUI(content, isDone);
this.updateStreamingUI(content, isDone, originalChatNoteId);
// Update session data with additional metadata when streaming is complete
if (isDone) {
@ -1067,13 +1179,13 @@ export default class LlmChatPanel extends BasicWidget {
/**
* Update the UI with streaming content
*/
private updateStreamingUI(assistantResponse: string, isDone: boolean = false) {
private updateStreamingUI(assistantResponse: string, isDone: boolean = false, originalChatNoteId?: string | null) {
// Track if we have a streaming message in progress
const hasStreamingMessage = !!this.noteContextChatMessages.querySelector('.assistant-message.streaming');
// Create a new message element or use the existing streaming one
let assistantMessageEl: HTMLElement;
if (hasStreamingMessage) {
// Use the existing streaming message
assistantMessageEl = this.noteContextChatMessages.querySelector('.assistant-message.streaming')!;
@ -1103,7 +1215,7 @@ export default class LlmChatPanel extends BasicWidget {
if (isDone) {
// Remove the streaming class to mark this message as complete
assistantMessageEl.classList.remove('streaming');
// Apply syntax highlighting
formatCodeBlocks($(assistantMessageEl as HTMLElement));
@ -1118,8 +1230,8 @@ export default class LlmChatPanel extends BasicWidget {
timestamp: new Date()
});
// Save the updated message list
this.saveCurrentData();
// Save the updated message list to the original chat note
this.saveCurrentDataToSpecificNote(originalChatNoteId || this.noteId);
}
// Scroll to bottom

@ -2,12 +2,12 @@
* Validation functions for LLM Chat
*/
import options from "../../services/options.js";
import { getEmbeddingStats } from "./communication.js";
import { t } from "../../services/i18n.js";
/**
* Validate embedding providers configuration
* Validate providers configuration
*/
export async function validateEmbeddingProviders(validationWarning: HTMLElement): Promise<void> {
export async function validateProviders(validationWarning: HTMLElement): Promise<void> {
try {
// Check if AI is enabled
const aiEnabled = options.is('aiEnabled');
@ -38,6 +38,9 @@ export async function validateEmbeddingProviders(validationWarning: HTMLElement)
// Check for configuration issues with providers in the precedence list
const configIssues: string[] = [];
// Always add experimental warning as the first item
configIssues.push(t("ai_llm.experimental_warning"));
// Check each provider in the precedence list for proper configuration
for (const provider of precedenceList) {
if (provider === 'openai') {
@ -62,23 +65,8 @@ export async function validateEmbeddingProviders(validationWarning: HTMLElement)
// Add checks for other providers as needed
}
// Fetch embedding stats to check if there are any notes being processed
const embeddingStats = await getEmbeddingStats() as {
success: boolean,
stats: {
totalNotesCount: number;
embeddedNotesCount: number;
queuedNotesCount: number;
failedNotesCount: number;
lastProcessedDate: string | null;
percentComplete: number;
}
};
const queuedNotes = embeddingStats?.stats?.queuedNotesCount || 0;
const hasEmbeddingsInQueue = queuedNotes > 0;
// Show warning if there are configuration issues or embeddings in queue
if (configIssues.length > 0 || hasEmbeddingsInQueue) {
// Show warning if there are configuration issues
if (configIssues.length > 0) {
let message = '<i class="bx bx-error-circle me-2"></i><strong>AI Provider Configuration Issues</strong>';
message += '<ul class="mb-1 ps-4">';
@ -87,11 +75,6 @@ export async function validateEmbeddingProviders(validationWarning: HTMLElement)
for (const issue of configIssues) {
message += `<li>${issue}</li>`;
}
// Show warning about embeddings queue if applicable
if (hasEmbeddingsInQueue) {
message += `<li>Currently processing embeddings for ${queuedNotes} notes. Some AI features may produce incomplete results until processing completes.</li>`;
}
message += '</ul>';
message += '<div class="mt-2"><a href="javascript:" class="settings-link btn btn-sm btn-outline-secondary"><i class="bx bx-cog me-1"></i>Open AI Settings</a></div>';
@ -103,7 +86,7 @@ export async function validateEmbeddingProviders(validationWarning: HTMLElement)
validationWarning.style.display = 'none';
}
} catch (error) {
console.error('Error validating embedding providers:', error);
console.error('Error validating providers:', error);
validationWarning.style.display = 'none';
}
}

@ -1507,6 +1507,12 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
);
this.toggleHiddenNode(true); // hoisting will handle hidden note visibility
// Automatically expand the hoisted note by default
const node = this.getActiveNode();
if (node.data.noteId === this.noteContext.hoistedNoteId){
this.setExpanded(node.data.branchId, true);
}
}
}

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