Compare commits

...

1104 Commits

Author SHA1 Message Date
Elian Doran 7a1ec266ad chore(release): prepare for v0.97.2
Checks / main (push) Failing after 1s
/ Check Docker build (Dockerfile) (push) Failing after 31s
/ Check Docker build (Dockerfile.alpine) (push) Failing after 44s
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm/v7) (push) Has been skipped
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm/v8) (push) Has been skipped
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Has been skipped
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Has been skipped
/ Merge manifest lists (push) Has been skipped
Release / Make Electron (arm64, map[forge_platform:linux image:ubuntu-22.04 name:linux shell:bash]) (push) Failing after 35s
Release / Make Electron (x64, map[forge_platform:linux image:ubuntu-22.04 name:linux shell:bash]) (push) Failing after 42s
Release / Build Linux Server (x64, ubuntu-22.04) (push) Failing after 26s
Release / Make Electron (arm64, map[forge_platform:darwin image:macos-latest name:macos shell:bash]) (push) Has been cancelled
Release / Make Electron (arm64, map[forge_platform:win32 image:win-signing name:windows shell:cmd]) (push) Has been cancelled
Release / Make Electron (x64, map[forge_platform:darwin image:macos-latest name:macos shell:bash]) (push) Has been cancelled
Release / Make Electron (x64, map[forge_platform:win32 image:win-signing name:windows shell:cmd]) (push) Has been cancelled
Release / Build Linux Server (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Release / Publish release (push) Has been cancelled
2025-08-02 23:34:39 +03:00
Elian Doran 42fedaa241 chore(deps): update nx monorepo to v21.3.11 (#6523) 2025-08-02 10:38:29 +03:00
renovate[bot] 4387bd4c6f chore(deps): update nx monorepo to v21.3.11 2025-08-02 07:22:22 +00:00
Elian Doran 51e1367b82 chore(deps): update dependency typescript to v5.9.2 (#6525) 2025-08-02 10:17:05 +03:00
renovate[bot] 8bea3f4422 chore(deps): update dependency typescript to v5.9.2 2025-08-02 07:00:23 +00:00
Elian Doran 0eb2e405ff chore(deps): update node.js to v22.18.0 (#6527) 2025-08-02 09:57:18 +03:00
Elian Doran 5dbd4a765f fix(deps): update dependency @codemirror/lang-markdown to v6.3.4 (#6524) 2025-08-02 09:57:04 +03:00
Elian Doran f6961c7e06 chore(deps): update dependency typedoc to v0.28.9 (#6522) 2025-08-02 09:56:38 +03:00
Elian Doran 8d3ba90072 chore(deps): update dependency electron to v37.2.5 (#6521) 2025-08-02 09:55:22 +03:00
Elian Doran 3772412d82 chore(deps): update dependency @playwright/test to v1.54.2 (#6520) 2025-08-02 09:55:05 +03:00
Elian Doran 84389f467e chore(deps): update pnpm to v10.14.0 (#6528) 2025-08-02 09:54:42 +03:00
Elian Doran eb41e0f96f chore(deps): update svelte monorepo (#6529) 2025-08-02 09:53:44 +03:00
renovate[bot] 2d44dff997 chore(deps): update svelte monorepo 2025-08-02 02:29:36 +00:00
renovate[bot] 1483bf3d46 chore(deps): update pnpm to v10.14.0 2025-08-02 02:28:49 +00:00
renovate[bot] 064cf6a3ee chore(deps): update node.js to v22.18.0 2025-08-02 02:28:39 +00:00
renovate[bot] 0c0d5eaa0a fix(deps): update dependency @codemirror/lang-markdown to v6.3.4 2025-08-02 02:27:03 +00:00
renovate[bot] afecb33b5c chore(deps): update dependency typedoc to v0.28.9 2025-08-02 02:25:23 +00:00
renovate[bot] fbb1e3a302 chore(deps): update dependency electron to v37.2.5 2025-08-02 02:25:17 +00:00
renovate[bot] 8704350359 chore(deps): update dependency @playwright/test to v1.54.2 2025-08-02 02:24:27 +00:00
Elian Doran d09e725d98 fix(note_list): copy to clipboard button also opening note 2025-08-01 13:07:58 +03:00
Elian Doran 8be5b149c4 fix(note_list): note tooltip showing up 2025-08-01 13:05:17 +03:00
Elian Doran faeea6af18 Merge branch 'main' of github.com:TriliumNext/trilium 2025-08-01 00:23:13 +03:00
Elian Doran 3fa5ea1010 docs(readme): mention translations 2025-08-01 00:23:09 +03:00
Elian Doran 6aa31ae125 Translations update from Hosted Weblate (#6516) 2025-08-01 00:13:50 +03:00
wild 27f2e9c286 Translated using Weblate (Serbian)
Currently translated at 9.1% (142 of 1559 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/sr/
2025-07-31 21:05:37 +00:00
Aitanuqui 67cc36fdd2 Translated using Weblate (Spanish)
Currently translated at 89.4% (338 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/es/
2025-07-31 21:05:36 +00:00
Aitanuqui ef7297e03b Translated using Weblate (Spanish)
Currently translated at 96.4% (1503 of 1559 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
2025-07-31 21:05:35 +00:00
wild 97a5314cdb Added translation using Weblate (Serbian) 2025-07-31 21:05:34 +00:00
Elian Doran a1195a2856 feat(search): support doc notes (closes #6515) 2025-08-01 00:05:17 +03:00
Elian Doran 81419c6fe3 Translations update from Hosted Weblate (#6514) 2025-07-31 11:52:22 +03:00
Elian Doran b8da793353 Translated using Weblate (Romanian)
Currently translated at 99.7% (377 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ro/
2025-07-31 10:51:13 +02:00
Adorian Doran 8140fa79cc Translated using Weblate (Romanian)
Currently translated at 99.7% (377 of 378 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/ro/
2025-07-31 10:51:12 +02:00
Elian Doran abff4fe67d Translated using Weblate (Romanian)
Currently translated at 100.0% (1559 of 1559 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ro/
2025-07-31 10:51:12 +02:00
Elian Doran ec8f737eba Translations update from Hosted Weblate (#6513) 2025-07-31 09:22:49 +03:00
Hosted Weblate cc6688ea00 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/
2025-07-31 08:09:52 +02:00
Elian Doran c448b29be7 chore(deps): update nx monorepo to v21.3.10 (#6511) 2025-07-31 08:04:27 +03:00
Elian Doran 61bde294b3 chore(deps): update dependency openai to v5.11.0 (#6512) 2025-07-31 08:03:35 +03:00
Elian Doran acab81c61e chore(deps): update dependency eslint-plugin-playwright to v2.2.2 (#6510) 2025-07-31 08:03:20 +03:00
Elian Doran 1dd965973b chore(deps): update dependency @types/tabulator-tables to v6.2.9 (#6509) 2025-07-31 08:03:07 +03:00
renovate[bot] d61981033f chore(deps): update dependency openai to v5.11.0 2025-07-31 01:17:46 +00:00
renovate[bot] 30197ba7ce chore(deps): update nx monorepo to v21.3.10 2025-07-31 01:17:03 +00:00
renovate[bot] 1b6c957334 chore(deps): update dependency eslint-plugin-playwright to v2.2.2 2025-07-31 01:16:19 +00:00
renovate[bot] fb7a397bf9 chore(deps): update dependency @types/tabulator-tables to v6.2.9 2025-07-31 01:15:39 +00:00
Elian Doran 133c9c5a7b Remove unmaintained hotkeys dependency (#6507) 2025-07-31 00:02:29 +03:00
Elian Doran 8a587d4d21 chore(client): fix typecheck issues 2025-07-30 23:46:43 +03:00
Elian Doran 29b813fa3b Merge remote-tracking branch 'origin/main' into feature/replace_hotkeys_library 2025-07-30 23:29:16 +03:00
Elian Doran 1dfe27d3df feat(web_view): open externally from note preview 2025-07-30 23:18:05 +03:00
Elian Doran cda8fc7146 style(next): improve border for pdf notes preview 2025-07-30 23:05:46 +03:00
Elian Doran acb16f751b style(next): improve border for image notes preview 2025-07-30 23:03:02 +03:00
Elian Doran a1ac276be5 feat(web_view): hide attribute from attribute preview 2025-07-30 22:58:42 +03:00
Elian Doran 54e3ab5139 fix(command_palette): full screen not working on the browser 2025-07-30 22:50:08 +03:00
Elian Doran baf341b312 fix(command_palette): find in text not shown 2025-07-30 22:47:11 +03:00
Elian Doran 5b074c2e22 fix(command_palette): some note context-aware commands not working 2025-07-30 22:45:31 +03:00
Elian Doran 11d086ef12 fix(command_palette): text editor-based issues not working 2025-07-30 22:39:37 +03:00
Elian Doran 0e6b10e400 feat(command_palette): active tab-related commands on browser 2025-07-30 22:33:22 +03:00
Elian Doran 0240222998 chore(command_palette): disable two unsupported commands 2025-07-30 19:54:19 +03:00
Elian Doran 7fc739487f chore(command_palette): hide jump to note / command palette 2025-07-30 19:50:07 +03:00
Elian Doran f6e275709f fix(command_palette): sort child notes not working 2025-07-30 19:47:01 +03:00
Elian Doran 7e01dfd220 fix(sort): refresh when sorting notes via dialog 2025-07-30 19:45:01 +03:00
Elian Doran d5866a99ec test(hotkeys): add some basic tests 2025-07-30 19:30:27 +03:00
Elian Doran 5289d41b12 fix(hotkeys): shortcuts with number keys not working 2025-07-30 14:43:37 +03:00
Elian Doran 030178cad2 fix(hotkeys): errors on mouse clicks 2025-07-30 14:29:59 +03:00
Elian Doran 5d00630452 refactor(hotkeys): simplify normalization 2025-07-30 14:26:51 +03:00
Elian Doran eb805bfa2a refactor(hotkeys): remove no longer necessary library 2025-07-30 14:19:02 +03:00
Elian Doran ee3a8e105e refactor(hotkeys): remove unnecessary initialization code 2025-07-30 14:18:09 +03:00
Elian Doran 97fb273e7f fix(hotkeys): tree not using the right API 2025-07-30 14:15:29 +03:00
Elian Doran 2ef9009384 refactor(hotkeys): use own (rough) implementation 2025-07-30 14:11:41 +03:00
Elian Doran 27c7888628 fix(deps): update dependency @maplibre/maplibre-gl-leaflet to v0.1.3 (#6503) 2025-07-30 11:04:07 +03:00
Elian Doran b4de37a9f4 chore(deps): update electron-forge monorepo to v7.8.2 (#6501) 2025-07-30 11:03:43 +03:00
Elian Doran 1c5ebb54f8 chore(deps): update nx monorepo to v21.3.9 (#6502) 2025-07-30 11:03:31 +03:00
Elian Doran f3e69dd6bd Merge branch 'main' of github.com:TriliumNext/trilium 2025-07-30 11:03:17 +03:00
Elian Doran 66364f5ce0 test(server/e2e): add more assertions to try to avoid flaky test 2025-07-30 11:03:12 +03:00
renovate[bot] f25a1fb865 chore(deps): update electron-forge monorepo to v7.8.2 2025-07-30 07:43:22 +00:00
renovate[bot] 62c5b8b1fc chore(deps): update nx monorepo to v21.3.9 2025-07-30 07:41:02 +00:00
Elian Doran 2b0de37fc0 chore(deps): update dependency @types/express-http-proxy to v1.6.7 (#6497) 2025-07-30 10:38:55 +03:00
Elian Doran 23ef73fe2f chore(deps): update dependency @types/node to v22.17.0 (#6504) 2025-07-30 10:38:23 +03:00
Elian Doran 92ac3ee4ef chore(deps): update dependency stylelint to v16.23.0 (#6505) 2025-07-30 10:36:12 +03:00
Elian Doran a3ba5ca109 test(server/e2e): flaky test 2025-07-30 10:34:38 +03:00
renovate[bot] 5b4e81cf18 chore(deps): update dependency stylelint to v16.23.0 2025-07-30 06:53:17 +00:00
renovate[bot] 772e6f5ebc chore(deps): update dependency @types/node to v22.17.0 2025-07-30 06:52:25 +00:00
renovate[bot] 60a9428b8b fix(deps): update dependency @maplibre/maplibre-gl-leaflet to v0.1.3 2025-07-30 06:51:34 +00:00
renovate[bot] a7752a8421 chore(deps): update dependency @types/express-http-proxy to v1.6.7 2025-07-30 06:48:57 +00:00
Elian Doran aefa2315b7 fix(server/test): yet another cyclic import issue due to becca_loader 2025-07-30 09:19:02 +03:00
Elian Doran 37a79aeeab fix(server/test): non-platform agnostic test 2025-07-30 08:42:51 +03:00
Elian Doran 5bc4bdaeef fix(server/test): problematic cyclic dependency 2025-07-30 08:38:06 +03:00
Elian Doran 5e28df883d fix(server): migration failing due to geomap in protected mode (closes #6489) 2025-07-29 23:26:03 +03:00
Elian Doran 0a57748075 fix(deps): update dependency preact to v10.27.0 (#6500) 2025-07-29 08:49:15 +03:00
Elian Doran 45e3eee642 chore(deps): update dependency svelte to v5.37.1 (#6498) 2025-07-29 08:48:56 +03:00
Elian Doran d724a80c2a chore(deps): update nx monorepo to v21.3.8 (#6499) 2025-07-29 08:48:31 +03:00
renovate[bot] 5ea8c94d18 fix(deps): update dependency preact to v10.27.0 2025-07-29 01:29:37 +00:00
renovate[bot] 769bc760b3 chore(deps): update nx monorepo to v21.3.8 2025-07-29 01:28:50 +00:00
renovate[bot] f04f45ea62 chore(deps): update dependency svelte to v5.37.1 2025-07-29 01:28:09 +00:00
Elian Doran a5cab6a2a2 Command palette (#6491) 2025-07-28 21:20:19 +03:00
Elian Doran 138611beaf chore(client): remove unnecessary log 2025-07-28 21:18:06 +03:00
Elian Doran e1b608057a chore(deps): update dependency eslint-plugin-playwright to v2.2.1 (#6495) 2025-07-28 20:16:40 +03:00
Elian Doran fed6d8329f chore(deps): update dependency typedoc to v0.28.8 (#6496) 2025-07-28 20:03:19 +03:00
Elian Doran 9d03d52f28 fix(hidden_subtree): unable to change language 2025-07-28 20:02:46 +03:00
Elian Doran 055e11174d refactor(hidden_subtree): deduplicate restoring title 2025-07-28 19:59:10 +03:00
Elian Doran 8fda2dd7f1 test(client): fix error due to JQuery 2025-07-28 18:58:26 +03:00
renovate[bot] ea03695c75 chore(deps): update dependency typedoc to v0.28.8 2025-07-28 15:47:13 +00:00
renovate[bot] 17b206fc72 chore(deps): update dependency eslint-plugin-playwright to v2.2.1 2025-07-28 15:47:07 +00:00
Elian Doran 4ec8c5963a docs(guide): document command palette 2025-07-28 18:21:04 +03:00
Elian Doran ab2d8accf5 chore(command_palette): hide system tray from web 2025-07-28 17:20:02 +03:00
Elian Doran de8b7e9ebe feat(command_palette): sort commands by name 2025-07-28 17:17:11 +03:00
Elian Doran 18d11523a6 chore(server): add entry point for circular-deps 2025-07-28 15:19:15 +03:00
Elian Doran 7a0ab3c025 feat(command_palette): enforce title names 2025-07-28 15:19:05 +03:00
Elian Doran 3575a7dc93 fix(hidden_subtree): bring back enforcing branches for help 2025-07-28 13:15:12 +03:00
Elian Doran bb9e7b1c6e fix(hidden_subtree): visible launchers broken due to branch enforcement 2025-07-28 12:20:14 +03:00
Elian Doran 115e9e0202 chore(test): undefined import when running under vitest 2025-07-28 12:16:31 +03:00
Elian Doran e341de70c0 chore(command_palette): change placeholder 2025-07-28 11:21:18 +03:00
Elian Doran 1d1a0ac4fd fix(command_palette): print command showing modal 2025-07-28 11:15:48 +03:00
Elian Doran d48470ffb1 Merge remote-tracking branch 'origin/main' into feature/command_palette 2025-07-28 11:12:47 +03:00
Elian Doran 6574ca42a3 chore(deps): update dependency svelte to v5.37.0 (#6492) 2025-07-28 10:52:11 +03:00
renovate[bot] 303ff35a76 chore(deps): update dependency svelte to v5.37.0 2025-07-28 00:38:54 +00:00
Elian Doran e0850958b0 chore(client): type errors 2025-07-27 23:21:07 +03:00
Elian Doran 13115b9ed1 fix(keyboard_actions): missing keyboard action descriptions 2025-07-27 22:22:17 +03:00
Elian Doran 933a11e9db chore(command_palette): add translations 2025-07-27 22:16:04 +03:00
Elian Doran 6915993a35 feat(command_palette): remove duplicate actions 2025-07-27 22:12:08 +03:00
Elian Doran 237a4e9a74 feat(command_palette): hide electron-only actions on web 2025-07-27 22:05:24 +03:00
Elian Doran 1565a0fd80 feat(command_palette): differentiate tree-based operations 2025-07-27 21:47:30 +03:00
Elian Doran e8b16287e0 refactor(command_palette): reduce duplication 2025-07-27 21:39:55 +03:00
Elian Doran c09e124805 fix(command_palette): command title not updated while navigating 2025-07-27 21:36:42 +03:00
Elian Doran b6f55b0e1a refactor(command_palette): unnecessary icon mapping 2025-07-27 21:18:00 +03:00
Elian Doran 964bc74b83 refactor(command_palette): use declarative command approach 2025-07-27 21:16:23 +03:00
Elian Doran fa9b142cb7 fix(command_palette): triggering note tree actions 2025-07-27 21:03:31 +03:00
Elian Doran 7e3f412c84 fix(command_palette): missing icon 2025-07-27 20:41:01 +03:00
Elian Doran 82e16a5624 fix(command_palette): not showing after re-entering 2025-07-27 20:31:13 +03:00
Elian Doran 757488a95b feat(command_palette): improve dialog margins 2025-07-27 18:15:54 +03:00
Elian Doran d7f154cfd1 feat(command_palette): improve layout 2025-07-27 18:11:43 +03:00
Elian Doran 3517715aab feat(command_palette): add icons to all actions 2025-07-27 17:41:00 +03:00
Elian Doran d10bbdd7a7 feat(settings/keyboard_actions): display friendly name 2025-07-27 17:04:29 +03:00
Elian Doran c4ec27bb1e chore(keyboard_actions): use translations for friendly names 2025-07-27 17:04:05 +03:00
Elian Doran 0b24553ace feat(keyboard_actions): add friendly names to all actions 2025-07-27 16:50:02 +03:00
Elian Doran 793867269b refactor(command_palette): separate model for keyboard shortcuts 2025-07-27 16:40:48 +03:00
Elian Doran 9508e92676 feat(command_palette): integrate all keyboard actions 2025-07-27 16:32:39 +03:00
Elian Doran 89378eae7b feat(command_palette): improve keyboard shortcut 2025-07-27 16:15:14 +03:00
Elian Doran ace166a925 feat(command_palette): hide search in full text 2025-07-27 15:59:33 +03:00
Elian Doran d59d544c0f style(command_palette): improve layout slightly 2025-07-27 15:49:12 +03:00
Elian Doran 37461d0eb3 refactor(command_palette): use CSS for styles 2025-07-27 15:44:47 +03:00
Elian Doran 126152ff63 feat(command_palette): display commands immediately 2025-07-27 15:42:44 +03:00
Elian Doran 60e19de0d1 feat(command_palette): add keyboard shortcut 2025-07-27 15:34:51 +03:00
Elian Doran 3247a9facc feat(command_palette): hide on command execution 2025-07-27 15:30:27 +03:00
Elian Doran 7b114bed26 feat(command_palette): basic implementation 2025-07-27 15:27:13 +03:00
Elian Doran 30ffbc760e Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-27 12:17:21 +03:00
Elian Doran 4420913049 fix(export/markdown): superscript and subscript not preserved (closes #4307) 2025-07-27 12:17:13 +03:00
Elian Doran 3762690c5f Merge branch 'main' of github.com:TriliumNext/trilium 2025-07-27 11:04:03 +03:00
Elian Doran d684ac40d8 fix(forge): nightly failing due to minimatch 2025-07-27 10:55:27 +03:00
Elian Doran d217379644 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.1 (#6487) 2025-07-27 09:14:18 +03:00
renovate[bot] d5f7fa2fe5 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.1 2025-07-27 01:36:11 +00:00
Elian Doran 3e0ef10b25 fix(print): table captions not displayed properly (closes #6483) 2025-07-26 23:25:04 +03:00
Elian Doran 28f88f2407 chore(deps): update dependency dotenv to v17.2.1 (#6476) 2025-07-26 15:48:30 +03:00
Elian Doran e525a7a0ff chore(deps): update dependency svelte to v5.36.17 (#6484) 2025-07-26 15:48:12 +03:00
Elian Doran 3415f38e0a fix(deps): update dependency eslint-linter-browserify to v9.32.0 (#6485) 2025-07-26 15:47:54 +03:00
Elian Doran 910c0faade chore(deps): update dependency cross-env to v10 (#6486) 2025-07-26 15:47:41 +03:00
renovate[bot] 4ad1bb5e3a chore(deps): update dependency cross-env to v10 2025-07-26 10:38:57 +00:00
renovate[bot] 97f6f0a945 fix(deps): update dependency eslint-linter-browserify to v9.32.0 2025-07-26 10:38:01 +00:00
renovate[bot] bc78c17a11 chore(deps): update dependency svelte to v5.36.17 2025-07-26 10:37:05 +00:00
Elian Doran b8e813f7bd feat(promoted_attributes): better indicate no value 2025-07-26 10:12:46 +03:00
Elian Doran db3581eb26 feat(promoted_attributes): improve color picker aspect 2025-07-26 09:53:26 +03:00
Elian Doran d23230df68 feat(promoted_attributes): support removing color 2025-07-26 09:49:32 +03:00
Elian Doran b29781b614 feat(promoted_attributes): add color type 2025-07-26 09:29:27 +03:00
Elian Doran 7d7c3e7cdb fix(ws): new attachments' title not decrypted (closes #6473) 2025-07-25 23:21:44 +03:00
Elian Doran cbd8cb80ab chore(deps): update nx monorepo to v21.3.7 (#6479) 2025-07-25 23:20:55 +03:00
renovate[bot] bfcdc34faf chore(deps): update nx monorepo to v21.3.7 2025-07-25 20:20:42 +00:00
Elian Doran c728e6047d chore(deps): update dependency jiti to v2.5.1 (#6478) 2025-07-25 23:18:45 +03:00
renovate[bot] 4c53a9ba8c chore(deps): update dependency jiti to v2.5.1 2025-07-25 19:47:38 +00:00
Elian Doran e10a7da7e3 chore(deps): update dependency @sveltejs/kit to v2.26.1 (#6475) 2025-07-25 22:42:39 +03:00
Elian Doran 5cc431b1bf chore(deps): update dependency dotenv to v17.2.1 (#6477) 2025-07-25 22:42:22 +03:00
Elian Doran 734aa2fcb5 fix(deps): update dependency mind-elixir to v5.0.4 (#6480) 2025-07-25 22:42:06 +03:00
Elian Doran 5e37319d9b fix(deps): update eslint monorepo to v9.32.0 (#6481) 2025-07-25 22:41:56 +03:00
renovate[bot] 2e9eb6e3e9 fix(deps): update eslint monorepo to v9.32.0 2025-07-25 19:13:01 +00:00
renovate[bot] 9ce57b123a fix(deps): update dependency mind-elixir to v5.0.4 2025-07-25 19:11:23 +00:00
renovate[bot] e793168afa chore(deps): update dependency dotenv to v17.2.1 2025-07-25 19:08:36 +00:00
renovate[bot] d1513424e7 chore(deps): update dependency dotenv to v17.2.1 2025-07-25 19:07:41 +00:00
renovate[bot] 1436a01dbe chore(deps): update dependency @sveltejs/kit to v2.26.1 2025-07-25 19:06:44 +00:00
Elian Doran b9b936b92a chore(client): change book type to collection (closes #6471) 2025-07-25 19:09:03 +03:00
Elian Doran adf14bec31 fix(views/board): unable to scroll vertically 2025-07-25 19:00:12 +03:00
Elian Doran ca1403ffea docs(guide): creating collections & adding a description 2025-07-25 18:06:40 +03:00
Elian Doran 06672e439e docs(guide): board view 2025-07-25 17:57:34 +03:00
Elian Doran e851701a9e fix(views/board): unable to click while editing column 2025-07-25 16:29:25 +03:00
Elian Doran 9589164008 fix(views/board): column duplication after batch rename 2025-07-25 16:20:33 +03:00
Elian Doran a88b067081 refactor(views/board): use in-memory model 2025-07-25 16:17:54 +03:00
Elian Doran b3777e6900 fix(views/board): column desynchronising due to API management 2025-07-25 16:11:26 +03:00
Elian Doran d2646e291d chore(views/board): remove unnecessary highlight 2025-07-25 15:16:17 +03:00
Elian Doran 99ab9ee66b chore(views/board): set up context menu on the header 2025-07-25 15:15:10 +03:00
Elian Doran 08678e74e6 refactor(views/board): unnecessary fields 2025-07-25 14:57:51 +03:00
Elian Doran 62de52ab17 refactor(views/board): unnecessary API to manually refresh the board 2025-07-25 14:56:50 +03:00
Elian Doran d9820d9725 fix(views/board): column not clickable after dragging 2025-07-25 14:54:50 +03:00
Elian Doran fe8a8eeac9 feat(views/board): react to icon and color changes 2025-07-25 14:42:05 +03:00
Elian Doran dfeb414aff feat(views/board): reintroduce one-click title edit 2025-07-25 12:00:04 +03:00
Elian Doran 69f12a2916 feat(views/board): drag columns by the title and not by a handle 2025-07-25 11:56:44 +03:00
Elian Doran 2b062e938e chore(views/board): use translations 2025-07-25 11:46:18 +03:00
Elian Doran e0299bd1ae style(views/board): improve new buttons 2025-07-25 11:42:44 +03:00
Elian Doran ac2f1b56fe style(views/board): shorter cards and smaller gaps 2025-07-25 11:35:07 +03:00
Elian Doran 06d98f6fcf refactor(views/board): unnecessary imports 2025-07-25 11:31:57 +03:00
Elian Doran bb660d15b2 style(next): improve excalidraw dropdown fit 2025-07-25 11:06:20 +03:00
Elian Doran 4d73cdefef style(client): fix checkboxes override causing issues for canvas (closes #6463) 2025-07-25 10:08:14 +03:00
Elian Doran 313ba3df80 chore(deps): update dependency vite to v7.0.6 (#6465) 2025-07-25 08:24:18 +03:00
renovate[bot] 15377c32c2 chore(deps): update dependency vite to v7.0.6 2025-07-24 20:42:01 +00:00
Elian Doran 22b52f7c4a Merge branch 'main' of github.com:TriliumNext/trilium 2025-07-24 23:39:48 +03:00
Elian Doran 7055f77c91 docs(guide): document new features for geomap 2025-07-24 23:39:25 +03:00
Elian Doran 051fe67176 feat(views/geo): react to icon changes 2025-07-24 22:46:36 +03:00
Elian Doran 90accfcc48 chore(client): fix type errors 2025-07-24 22:26:29 +03:00
Elian Doran 4f99db0c90 refactor(views/geo): use a different attribute 2025-07-24 22:18:30 +03:00
Elian Doran aeb356bf54 feat(views/geo): allow displaying scale 2025-07-24 22:14:51 +03:00
Elian Doran 0dffa0f333 feat(book_properties): group dark map styles 2025-07-24 21:54:57 +03:00
Elian Doran d17f5b8447 feat(book_properties): group map style into vector & raster 2025-07-24 21:49:55 +03:00
Elian Doran b5a57b3c66 style(book_properties): align label properly 2025-07-24 21:32:02 +03:00
Elian Doran 987a3404a9 chore(deps): update ckeditor5 config packages to v12.1.0 (#6466) 2025-07-24 21:24:43 +03:00
Elian Doran eddc30769f chore(deps): update svelte monorepo (#6467) 2025-07-24 21:23:49 +03:00
Elian Doran 4d455650ba refactor(views/board): split row/column handling 2025-07-24 21:18:49 +03:00
Elian Doran e2157aab26 fix(views/board): reordering same column not working 2025-07-24 21:18:49 +03:00
Elian Doran b277f4bf3f feat(views/board): basic refresh after column change 2025-07-24 21:18:48 +03:00
Elian Doran 4047452b0f feat(views/board): drag works in between columns 2025-07-24 21:18:48 +03:00
Elian Doran cb37724879 feat(views/board): basic column drag support 2025-07-24 21:18:48 +03:00
renovate[bot] 8890893412 chore(deps): update svelte monorepo 2025-07-24 18:01:27 +00:00
renovate[bot] d0cbda7c0d chore(deps): update ckeditor5 config packages to v12.1.0 2025-07-24 18:00:32 +00:00
Elian Doran 60e7b9ffb0 feat(views/geo): set default theme 2025-07-24 15:52:01 +03:00
Elian Doran 45457c6f76 feat(views/geo): invert marker label on dark themes 2025-07-24 15:46:11 +03:00
Elian Doran 737f41d92b refactor(views/geo): get rid of empty theme 2025-07-24 15:36:06 +03:00
Elian Doran 180841f364 refactor(views/geo): remove dependency to leaflet in map layer 2025-07-24 15:35:03 +03:00
Elian Doran bea40d4c2f feat(views/geo): add the rest of the map layers 2025-07-24 15:33:39 +03:00
Elian Doran 5f9a054441 refactor(book_properties): use translations 2025-07-24 15:20:32 +03:00
Elian Doran f90bf1ce7c feat(views/geo): add combobox to adjust style 2025-07-24 15:14:43 +03:00
Elian Doran 8c4ed2d4da feat(views/geo): support vector maps 2025-07-24 15:07:47 +03:00
Elian Doran 0e590a1bbf chore(views/geo): add versatiles vector styles 2025-07-24 15:07:34 +03:00
Elian Doran 218a096135 chore(nx): update instructions 2025-07-24 13:57:41 +03:00
Elian Doran 8407bce370 chore(package): add output style to server:start 2025-07-24 13:57:23 +03:00
Elian Doran 43229f0b99 chore(deps): update nx monorepo to v21.3.5 (#6455) 2025-07-24 10:53:08 +03:00
renovate[bot] 84fa0002b9 chore(deps): update nx monorepo to v21.3.5 2025-07-24 07:22:18 +00:00
Elian Doran e79c705b20 chore(deps): update dependency webdriverio to v9.18.4 (#6459) 2025-07-24 10:20:10 +03:00
Elian Doran 894d7ce15d chore(deps): update dependency express-openid-connect to v2.19.2 (#6456) 2025-07-24 10:05:07 +03:00
renovate[bot] 5830880582 chore(deps): update dependency webdriverio to v9.18.4 2025-07-24 06:52:40 +00:00
renovate[bot] caab0f70ff chore(deps): update dependency express-openid-connect to v2.19.2 2025-07-24 06:51:51 +00:00
Elian Doran 641966fcdd chore(deps): update dependency @ckeditor/ckeditor5-package-tools to v4.0.2 (#6449) 2025-07-24 09:48:34 +03:00
renovate[bot] 24c22e9bbf chore(deps): update dependency @ckeditor/ckeditor5-package-tools to v4.0.2 2025-07-24 06:24:28 +00:00
Elian Doran 795f597bda chore(deps): update dependency jiti to v2.5.0 (#6457) 2025-07-24 09:22:19 +03:00
renovate[bot] 2228663a7e chore(deps): update dependency jiti to v2.5.0 2025-07-24 06:10:37 +00:00
Elian Doran 0c97df357d chore(deps): update dependency eslint-config-prettier to v10.1.8 (#6453) 2025-07-24 09:03:52 +03:00
Elian Doran 19f63f1be0 chore(deps): update dependency esbuild to v0.25.8 (#6452) 2025-07-24 09:03:26 +03:00
Elian Doran fc000caf73 chore(deps): update svelte monorepo (#6436) 2025-07-24 09:03:08 +03:00
renovate[bot] 78929e0293 chore(deps): update svelte monorepo 2025-07-24 05:49:14 +00:00
renovate[bot] 71e22da987 chore(deps): update dependency esbuild to v0.25.8 2025-07-24 05:45:25 +00:00
renovate[bot] 24e99d9654 chore(deps): update dependency eslint-config-prettier to v10.1.8 2025-07-24 05:39:35 +00:00
Elian Doran 98299da424 chore(deps): update dependency @types/tabulator-tables to v6.2.8 (#6450) 2025-07-24 08:39:04 +03:00
Elian Doran 7014af66b6 chore(deps): update dependency electron to v37.2.4 (#6451) 2025-07-24 08:38:36 +03:00
Elian Doran 659bd90027 chore(deps): update dependency vite to v7.0.5 (#6454) 2025-07-24 08:37:54 +03:00
Elian Doran 146b0c284b chore(deps): update dependency stylelint to v16.22.0 (#6458) 2025-07-24 08:36:59 +03:00
Elian Doran 4a0ac8807f chore(deps): update typescript-eslint monorepo to v8.38.0 (#6460) 2025-07-24 08:36:20 +03:00
renovate[bot] d67734832e chore(deps): update typescript-eslint monorepo to v8.38.0 2025-07-24 02:16:52 +00:00
renovate[bot] 1673bf026a chore(deps): update dependency stylelint to v16.22.0 2025-07-24 02:14:32 +00:00
renovate[bot] 1f29b000a9 chore(deps): update dependency vite to v7.0.5 2025-07-24 02:10:42 +00:00
renovate[bot] a6d024123e chore(deps): update dependency electron to v37.2.4 2025-07-24 02:08:10 +00:00
renovate[bot] fb1a7239ce chore(deps): update dependency @types/tabulator-tables to v6.2.8 2025-07-24 02:07:19 +00:00
Elian Doran 4f71d508cb chore(deps): audit 2025-07-23 22:32:49 +03:00
Elian Doran 2072bd61d1 fix(mermaid): lag during editing (closes #6443) 2025-07-23 22:28:15 +03:00
Elian Doran 6021178b7d feat(hidden_subtree): enforce original title in help 2025-07-23 21:22:58 +03:00
Elian Doran 179b0be2bb chore(deps): update dependency axios to v1.11.0 [security] (#6446) 2025-07-23 21:19:12 +03:00
renovate[bot] bf2b45dd4a chore(deps): update dependency axios to v1.11.0 [security] 2025-07-23 16:53:39 +00:00
Elian Doran 513561234c chore(deps): update nx monorepo to v21.3.2 (#6438) 2025-07-23 08:57:04 +03:00
renovate[bot] 33da990ae7 chore(deps): update nx monorepo to v21.3.2 2025-07-23 05:43:38 +00:00
Elian Doran 4003946e68 chore(deps): fix pnpm-lock 2025-07-23 08:40:44 +03:00
Elian Doran 21f8d40789 chore(deps): update dependency @stylistic/eslint-plugin to v5.2.2 (#6432) 2025-07-23 08:30:28 +03:00
Elian Doran d6c698e1d6 chore(deps): update dependency cheerio to v1.1.2 (#6433) 2025-07-23 08:30:04 +03:00
Elian Doran 6c227852ae chore(deps): update dependency openai to v5.10.2 (#6434) 2025-07-23 08:29:50 +03:00
Elian Doran 29cb22c4fd chore(deps): update dependency supertest to v7.1.4 (#6435) 2025-07-23 08:29:32 +03:00
Elian Doran d040bc9e2d chore(deps): update dependency webdriverio to v9.18.3 (#6437) 2025-07-23 08:29:07 +03:00
Elian Doran abb92f23a6 fix(deps): update dependency mind-elixir to v5.0.3 (#6439) 2025-07-23 08:28:47 +03:00
Elian Doran da5c86bb69 chore(deps): update dependency @anthropic-ai/sdk to v0.57.0 (#6440) 2025-07-23 08:28:33 +03:00
Elian Doran a0d428b12c chore(deps): update dependency express-openid-connect to v2.19.2 (#6441) 2025-07-23 08:28:20 +03:00
Elian Doran e22fe20e23 chore(deps): update typescript-eslint monorepo to v8.38.0 (#6442) 2025-07-23 08:27:59 +03:00
renovate[bot] 1e6659aff9 chore(deps): update typescript-eslint monorepo to v8.38.0 2025-07-23 02:37:13 +00:00
renovate[bot] 60b32d5b05 chore(deps): update dependency express-openid-connect to v2.19.2 2025-07-23 02:35:35 +00:00
renovate[bot] e2ee9053a0 chore(deps): update dependency @anthropic-ai/sdk to v0.57.0 2025-07-23 02:34:45 +00:00
renovate[bot] d2f0422ecc fix(deps): update dependency mind-elixir to v5.0.3 2025-07-23 02:33:49 +00:00
renovate[bot] bfd97da626 chore(deps): update dependency webdriverio to v9.18.3 2025-07-23 02:32:01 +00:00
renovate[bot] 1fd163f0bb chore(deps): update dependency supertest to v7.1.4 2025-07-23 02:30:18 +00:00
renovate[bot] d15ce575df chore(deps): update dependency openai to v5.10.2 2025-07-23 02:29:21 +00:00
renovate[bot] 9999ff5a89 chore(deps): update dependency cheerio to v1.1.2 2025-07-23 02:28:26 +00:00
renovate[bot] 4653941082 chore(deps): update dependency @stylistic/eslint-plugin to v5.2.2 2025-07-23 02:27:33 +00:00
Elian Doran fa509661ab Add grid to canvas (#6429) 2025-07-22 23:53:22 +03:00
Elian Doran d9a289bf18 refactor(views/board): unnecessary re-render 2025-07-22 23:40:12 +03:00
Papierkorb2292 98c76b713d Save gridModeEnabled in CanvasContent 2025-07-22 19:12:08 +02:00
Papierkorb2292 05ed917a56 Removed disabling grid mode in ExcalidrawTypeWidget 2025-07-22 19:12:08 +02:00
Elian Doran b833806ec7 feat(share): render inline mermaid (closes #5438) 2025-07-22 20:05:29 +03:00
Elian Doran 7fdef3418a refactor(share): check note type 2025-07-22 19:54:01 +03:00
Elian Doran 49e14ec542 feat(hidden_subtree): remove unexpected branches 2025-07-22 19:19:46 +03:00
Elian Doran efd9244684 fix(help): missing branches if it was relocated 2025-07-22 18:52:39 +03:00
Elian Doran 318f2d1f8c docs(guide): relocate note list documentation 2025-07-22 18:33:46 +03:00
Elian Doran 92fa1cf052 fix(quick_edit): read-only notes not editable (closes #6425) 2025-07-22 17:30:03 +03:00
Elian Doran 17c6eb1680 fix(export/markdown): simple tables rendered as HTML (closes #6366) 2025-07-22 09:09:50 +03:00
Elian Doran 7c6af568d8 fix(share): ck text on dark theme not visible (closes #6427) 2025-07-22 08:44:45 +03:00
Elian Doran 23c9c6826e chore(env): add some instructions 2025-07-21 19:41:29 +03:00
Elian Doran b08fda5e10 Kanban board (#6402) 2025-07-21 18:45:41 +03:00
Elian Doran 5ec3a49377 Merge remote-tracking branch 'origin/main' into feature/kanban_board 2025-07-21 18:24:36 +03:00
Elian Doran 1c728ae432 Merge branch 'release/v0.97.1' 2025-07-21 17:52:57 +03:00
Elian Doran fd25c735c1 chore(release): bump version
Checks / main (push) Failing after 2s
/ Check Docker build (Dockerfile) (push) Failing after 48s
/ Check Docker build (Dockerfile.alpine) (push) Failing after 37s
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm/v7) (push) Has been skipped
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm/v8) (push) Has been skipped
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Has been skipped
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Has been skipped
/ Merge manifest lists (push) Has been skipped
Release / Make Electron (arm64, map[forge_platform:linux image:ubuntu-22.04 name:linux shell:bash]) (push) Failing after 31s
Release / Make Electron (x64, map[forge_platform:linux image:ubuntu-22.04 name:linux shell:bash]) (push) Failing after 36s
Release / Build Linux Server (x64, ubuntu-22.04) (push) Failing after 34s
Release / Make Electron (arm64, map[forge_platform:darwin image:macos-latest name:macos shell:bash]) (push) Has been cancelled
Release / Make Electron (arm64, map[forge_platform:win32 image:win-signing name:windows shell:cmd]) (push) Has been cancelled
Release / Make Electron (x64, map[forge_platform:darwin image:macos-latest name:macos shell:bash]) (push) Has been cancelled
Release / Make Electron (x64, map[forge_platform:win32 image:win-signing name:windows shell:cmd]) (push) Has been cancelled
Release / Build Linux Server (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Release / Publish release (push) Has been cancelled
2025-07-21 17:52:08 +03:00
Elian Doran 7de33907c5 docs(release): add change log for v0.97.1 2025-07-21 17:51:13 +03:00
Elian Doran ec021be16c feat(views/board): display even if no children 2025-07-21 15:02:44 +03:00
Elian Doran 8b6826ffa4 feat(views/board): react to changes in "groupBy" 2025-07-21 15:02:38 +03:00
Elian Doran 00cc1ffe74 feat(views/board): add into view type switcher 2025-07-21 15:02:34 +03:00
Elian Doran 2384fdbaad chore(views/board): fix type errors 2025-07-21 15:02:31 +03:00
Elian Doran 08a93d81d7 feat(views/board): allow changing group by attribute 2025-07-21 15:02:28 +03:00
Elian Doran 86911100df refactor(views/board): use single point for obtaining status attribute 2025-07-21 15:02:22 +03:00
Elian Doran ff01656268 chore(vscode): set up NX LLM integration 2025-07-21 15:02:20 +03:00
Elian Doran d0ea6d9e8d feat(views/board): use same note title editing mechanism for insert above/below 2025-07-21 15:02:15 +03:00
Elian Doran 96ca3d5e38 fix(views/board): creating new notes would render as HTML 2025-07-21 13:14:07 +03:00
Elian Doran 3a569499cb feat(views/board): edit the note title inline on new 2025-07-21 11:28:46 +03:00
Elian Doran 545b19f978 fix(views/board): drop indicator remaining stuck 2025-07-21 11:19:14 +03:00
Elian Doran d98be19c9a feat(views/board): set up differential renderer 2025-07-21 11:13:41 +03:00
Elian Doran 4826898c55 refactor(views/board): move drag logic to separate file 2025-07-21 11:01:49 +03:00
Elian Doran 482b592f77 feat(views/board): add drag preview when using touch 2025-07-21 11:01:49 +03:00
Elian Doran 939ebfe47b chore(deps): update dependency cheerio to v1.1.1 (#6417) 2025-07-21 10:07:37 +03:00
Elian Doran c6dee1339b chore(deps): update dependency svelte to v5.36.12 (#6418) 2025-07-21 10:07:27 +03:00
Elian Doran 23f8c3ad3c chore(deps): update nx monorepo to v21.3.1 (#6419) 2025-07-21 10:06:25 +03:00
renovate[bot] 81c1b88376 chore(deps): update nx monorepo to v21.3.1 2025-07-21 02:58:10 +00:00
renovate[bot] c4a85db698 chore(deps): update dependency svelte to v5.36.12 2025-07-21 02:57:09 +00:00
renovate[bot] e6eda45c04 chore(deps): update dependency cheerio to v1.1.1 2025-07-21 02:56:06 +00:00
Elian Doran a3014434cf chore(release): update version number
Checks / main (push) Failing after 2s
/ Check Docker build (Dockerfile) (push) Failing after 45s
/ Check Docker build (Dockerfile.alpine) (push) Failing after 42s
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm/v7) (push) Has been skipped
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm/v8) (push) Has been skipped
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Has been skipped
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Has been skipped
/ Merge manifest lists (push) Has been skipped
Release / Make Electron (arm64, map[forge_platform:linux image:ubuntu-22.04 name:linux shell:bash]) (push) Failing after 33s
Release / Make Electron (x64, map[forge_platform:linux image:ubuntu-22.04 name:linux shell:bash]) (push) Failing after 38s
Release / Build Linux Server (x64, ubuntu-22.04) (push) Failing after 31s
Release / Make Electron (arm64, map[forge_platform:darwin image:macos-latest name:macos shell:bash]) (push) Has been cancelled
Release / Make Electron (arm64, map[forge_platform:win32 image:win-signing name:windows shell:cmd]) (push) Has been cancelled
Release / Make Electron (x64, map[forge_platform:darwin image:macos-latest name:macos shell:bash]) (push) Has been cancelled
Release / Make Electron (x64, map[forge_platform:win32 image:win-signing name:windows shell:cmd]) (push) Has been cancelled
Release / Build Linux Server (arm64, ubuntu-24.04-arm) (push) Has been cancelled
Release / Publish release (push) Has been cancelled
2025-07-20 23:39:58 +03:00
Elian Doran 3ebab2c126 docs(release): add changelog 2025-07-20 21:30:17 +03:00
Elian Doran 954619bd36 fix(views/table): note ID column being editable 2025-07-20 21:21:01 +03:00
Elian Doran eb76362de4 chore(views/board): improve header 2025-07-20 20:55:41 +03:00
Elian Doran 1cde14859b feat(views/board): touch support 2025-07-20 20:31:07 +03:00
Elian Doran c752b98995 chore(views/board): smaller add new column 2025-07-20 20:22:41 +03:00
Elian Doran 1f792ca418 feat(views/board): add new column 2025-07-20 20:06:54 +03:00
Elian Doran b22e08b1eb refactor(views/board): use bulk API for renaming columns 2025-07-20 19:59:21 +03:00
Elian Doran 2b5029cc38 chore(views/board): delete values when deleting column 2025-07-20 19:52:16 +03:00
Elian Doran 9e936cb57b feat(views/board): delete empty columns 2025-07-20 19:52:10 +03:00
Elian Doran e8fd2c1b3c fix(views/board): old column not removed when changing it 2025-07-20 19:52:06 +03:00
Elian Doran 977fbf54ee refactor(views/board): delegate storage to API 2025-07-20 19:52:01 +03:00
Elian Doran 3e5c91415d feat(views/board): rename columns 2025-07-20 19:51:56 +03:00
Elian Doran d60b855f74 chore(views/board): disable move to for the current column 2025-07-20 19:51:52 +03:00
Elian Doran 4146192b6d chore(views/board): add icon to menu item 2025-07-20 19:51:46 +03:00
Elian Doran 26ee0ff48f feat(views/board): insert above/below 2025-07-20 17:35:52 +03:00
Elian Doran 6995fbfd06 chore(deps): update dependency esbuild to v0.25.8 (#6404) 2025-07-20 16:07:54 +03:00
Elian Doran 1763d80d5f feat(views/board): add move to in context menu 2025-07-20 13:24:22 +03:00
Elian Doran a594e5147c feat(views/board): set up open in context menu 2025-07-20 12:42:19 +03:00
Elian Doran e51ea1a619 feat(views/board): add context menu with delete 2025-07-20 12:40:30 +03:00
Elian Doran 83b72eafa6 Merge branch 'main' into renovate/esbuild-0.x-lockfile 2025-07-20 11:31:50 +03:00
Elian Doran 757a6777be chore(deps): update dependency svelte to v5.36.10 (#6405) 2025-07-20 11:30:49 +03:00
Elian Doran 37c9260dca feat(views/board): keep empty columns 2025-07-20 10:50:26 +03:00
Elian Doran e1a8f4f5db chore(views/board): hide promoted attributes of collection 2025-07-20 10:50:13 +03:00
Elian Doran b7b0b39afc feat(views/board): add preset notes 2025-07-20 10:36:36 +03:00
Elian Doran af797489e8 feat(views/board): set up template 2025-07-20 10:30:48 +03:00
renovate[bot] d003e91b89 chore(deps): update dependency svelte to v5.36.10 2025-07-20 01:09:12 +00:00
renovate[bot] 4a35df745a chore(deps): update dependency esbuild to v0.25.8 2025-07-20 01:08:07 +00:00
Elian Doran b1b756b179 feat(views/board): store new columns into config 2025-07-19 22:21:24 +03:00
Elian Doran 9e3372df72 feat(views/board): react to changes in note title 2025-07-19 21:50:57 +03:00
Elian Doran 657df7a728 feat(views/board): add new item 2025-07-19 21:45:48 +03:00
Elian Doran 944f0b694b feat(views/board): open in popup 2025-07-19 21:09:55 +03:00
Elian Doran efd409da17 fix(views/board): some runtime errors 2025-07-19 21:07:29 +03:00
Elian Doran 08d60c554c feat(views/board): set up reordering for same column 2025-07-19 20:44:54 +03:00
Elian Doran a428ea7beb refactor(views/board): store both branch and note 2025-07-19 20:34:54 +03:00
Elian Doran f69878b082 refactor(views/board): use branches instead of notes 2025-07-19 20:30:05 +03:00
Elian Doran c5ffc2882b feat(views/board): react to changes 2025-07-19 19:57:02 +03:00
Elian Doran 765691751a feat(views/board): bypass horizontal scroll if column needs scrolling 2025-07-19 19:53:48 +03:00
Elian Doran f19e5977c2 feat(views/board): set up dragging 2025-07-19 19:48:03 +03:00
Elian Doran 8f8b9af862 feat(views/board): set up scroll via mouse wheel 2025-07-19 19:31:13 +03:00
Elian Doran 3e7dc71995 feat(views/board): make scrollable 2025-07-19 19:23:42 +03:00
Elian Doran 2a25cd8686 feat(views/board): fixed column size 2025-07-19 19:20:32 +03:00
Elian Doran 7664839135 feat(views/board): display note icon 2025-07-19 19:16:39 +03:00
Elian Doran 47daebc65a feat(views/board): improve display of the notes 2025-07-19 19:03:09 +03:00
Elian Doran 0d18b944b6 feat(views/board): display columns 2025-07-19 18:44:50 +03:00
Elian Doran 951b5384a3 chore(views/board): prepare to group by attribute 2025-07-19 18:39:24 +03:00
Elian Doran 11547ecaa3 chore(views/board): create empty board 2025-07-19 18:29:31 +03:00
Adorian Doran 713a0f5b09 Merge branch 'main' of https://github.com/TriliumNext/Trilium 2025-07-19 16:28:56 +03:00
Adorian Doran 2cf9c98b43 style/table view: tweak table footer 2025-07-19 16:28:52 +03:00
Elian Doran d7af196a0c feat(views/table): hide multiplicity when adding a new column 2025-07-19 16:03:58 +03:00
Adorian Doran c363be57b7 client/table view: tweak icons 2025-07-19 16:02:11 +03:00
Adorian Doran 10645790de Merge branch 'main' of https://github.com/TriliumNext/Trilium 2025-07-19 15:55:06 +03:00
Adorian Doran 8b18cf382c style(next)/dropdown menus: fix rotated icons 2025-07-19 15:54:56 +03:00
Elian Doran 7a131e0bcc feat(views/table): support color class for title 2025-07-19 15:44:43 +03:00
Elian Doran 3d264379cc fix(views/table): color no longer shown for reference links 2025-07-19 15:44:42 +03:00
Elian Doran f405682ec1 Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-19 14:56:21 +03:00
Elian Doran 3debf3ce1c fix(views/calendar): not refreshing on note title change 2025-07-19 14:53:27 +03:00
Adorian Doran 5a76883969 Merge branch 'main' of https://github.com/TriliumNext/Trilium 2025-07-19 14:41:51 +03:00
Adorian Doran 6f51c5e0cc style/attribute detail dialog: fix stretched close button 2025-07-19 14:41:48 +03:00
Elian Doran 2c730d1f0b Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-19 14:24:12 +03:00
Elian Doran d487da0b2f feat(views/table): update new column in context menu to support relations also 2025-07-19 14:23:42 +03:00
Elian Doran cb8a5cbb62 chore(views/table): add icons to add new column/row context menu 2025-07-19 14:06:00 +03:00
Elian Doran ceb08593d8 chore(views/table): use translations for new label/relation 2025-07-19 14:04:25 +03:00
Elian Doran 9dd0eb7b9b fix(views/table): not reacting to external attribute changes 2025-07-19 14:02:19 +03:00
Elian Doran ebff644d24 fix(views/table): changing column inheritability not working 2025-07-19 13:31:46 +03:00
Elian Doran beb1c15fa5 fix(views/table): inheritable checkbox not respected 2025-07-19 13:25:54 +03:00
Elian Doran 40a5eee211 docs(views/table): describe exactly how to remove relation 2025-07-19 13:00:08 +03:00
Elian Doran 8f393d0bae refactor(bulk_action): fix type error 2025-07-19 12:57:58 +03:00
Elian Doran 94dad49e2f refactor(bulk_action): full type safety for client 2025-07-19 12:56:37 +03:00
Elian Doran 409638151c refactor(bulk_action): add basic type safety for client 2025-07-19 12:54:16 +03:00
Elian Doran 0d3de92890 refactor(views/table): move bulk action implementation in service 2025-07-19 12:46:38 +03:00
Elian Doran 5d619131ec fix(views/table): bulk actions sometimes not working 2025-07-19 12:44:55 +03:00
Elian Doran e2c8443778 refactor(bulk_action): remake types & change method signature 2025-07-19 12:32:47 +03:00
Elian Doran daa4743967 refactor(server): add some type safety to bulk actions 2025-07-19 12:15:33 +03:00
Elian Doran 56553078ef docs(views/table): update documentation 2025-07-19 09:47:10 +03:00
Elian Doran 5584a06cb3 chore(deps): update nx monorepo to v21.3.0 (#6398) 2025-07-19 09:28:55 +03:00
renovate[bot] cfeb69ace6 chore(deps): update nx monorepo to v21.3.0 2025-07-19 06:03:54 +00:00
Elian Doran b0c8f110de chore(deps): update dependency @types/node to v22.16.5 (#6392) 2025-07-19 09:00:33 +03:00
Elian Doran aba1266c45 chore(deps): update svelte monorepo (#6395) 2025-07-19 08:59:56 +03:00
Elian Doran c331e0103d chore(deps): update dependency eslint-config-prettier to v10.1.8 (#6394) 2025-07-19 08:56:51 +03:00
Elian Doran 13978574e0 chore(deps): update dependency stylelint to v16.22.0 (#6397) 2025-07-19 08:56:25 +03:00
renovate[bot] be85963558 chore(deps): update dependency stylelint to v16.22.0 2025-07-19 05:55:03 +00:00
Elian Doran 8c19261ced fix(deps): update dependency marked to v16.1.1 (#6396) 2025-07-19 08:53:07 +03:00
Elian Doran 7ca17fa609 chore(deps): update dependency esbuild to v0.25.7 (#6393) 2025-07-19 08:52:36 +03:00
renovate[bot] 3d107572df fix(deps): update dependency marked to v16.1.1 2025-07-19 01:52:34 +00:00
renovate[bot] f7488655a7 chore(deps): update svelte monorepo 2025-07-19 01:51:41 +00:00
renovate[bot] 876e0a29d4 chore(deps): update dependency eslint-config-prettier to v10.1.8 2025-07-19 01:50:50 +00:00
renovate[bot] af74375695 chore(deps): update dependency esbuild to v0.25.7 2025-07-19 01:50:00 +00:00
renovate[bot] 896965fec5 chore(deps): update dependency @types/node to v22.16.5 2025-07-19 01:49:07 +00:00
Elian Doran ba5ef93c1a fix(views/table): wrong type when renaming relations 2025-07-18 21:07:29 +03:00
Elian Doran ef1153d336 fix(views/table): insert direction no longer working 2025-07-18 20:37:16 +03:00
Elian Doran 0d347f8823 feat(views/table): allow creating relations 2025-07-18 16:52:13 +03:00
Elian Doran 897cdc26ae chore(deps): update dependency express-session to v1.18.2 (#6372) 2025-07-18 11:53:17 +03:00
Elian Doran aba621c099 fix(deps): update dependency mermaid to v11.9.0 (#6384) 2025-07-18 11:53:03 +03:00
renovate[bot] 839813ebde fix(deps): update dependency mermaid to v11.9.0 2025-07-18 08:35:57 +00:00
renovate[bot] 545e2ddbfc chore(deps): update dependency express-session to v1.18.2 2025-07-18 08:35:17 +00:00
Elian Doran 1d63a5903a fix(deps): update dependency marked to v16.1.0 (#6383) 2025-07-18 11:31:35 +03:00
Elian Doran 2b34c00a0c chore(deps): update dependency openai to v5.10.1 (#6378) 2025-07-18 11:31:17 +03:00
Elian Doran 123068062a chore(deps): update dependency @stylistic/eslint-plugin to v5.2.0 (#6376) 2025-07-18 11:31:01 +03:00
Elian Doran 9a668e8709 chore(deps): update node.js to v22.17.1 (#6374) 2025-07-18 11:30:51 +03:00
Elian Doran f6f8937d64 chore(deps): update dependency express-rate-limit to v8.0.1 (#6371) 2025-07-18 11:30:24 +03:00
Elian Doran c9f53a2880 chore(deps): update dependency compression to v1.8.1 (#6369) 2025-07-18 11:30:17 +03:00
Elian Doran 2887e712c3 chore(deps): update dependency multer to v2.0.2 [security] (#6368) 2025-07-18 11:29:21 +03:00
renovate[bot] 5d3a0ed1b4 fix(deps): update dependency marked to v16.1.0 2025-07-18 08:06:03 +00:00
renovate[bot] 334b6319de chore(deps): update dependency openai to v5.10.1 2025-07-18 08:05:15 +00:00
renovate[bot] 4c118c0fd4 chore(deps): update dependency @stylistic/eslint-plugin to v5.2.0 2025-07-18 08:04:29 +00:00
renovate[bot] db00d60684 chore(deps): update node.js to v22.17.1 2025-07-18 08:04:25 +00:00
renovate[bot] 25b74af363 chore(deps): update dependency express-rate-limit to v8.0.1 2025-07-18 08:03:31 +00:00
renovate[bot] eb57cf97ad chore(deps): update dependency compression to v1.8.1 2025-07-18 08:02:36 +00:00
renovate[bot] c92e24363f chore(deps): update dependency multer to v2.0.2 [security] 2025-07-18 08:01:44 +00:00
Elian Doran 8d5d00ac0f chore(deps): update nx monorepo to v21.2.4 (#6375) 2025-07-18 10:59:33 +03:00
renovate[bot] 8b457384ba chore(deps): update nx monorepo to v21.2.4 2025-07-18 07:27:59 +00:00
Elian Doran fab2d53ece chore(deps): update dependency vite to v7.0.5 (#6373) 2025-07-18 10:25:50 +03:00
renovate[bot] 774f27d8d2 chore(deps): update dependency vite to v7.0.5 2025-07-18 07:10:21 +00:00
Elian Doran d7f02ef1b3 chore(deps): update dependency webdriverio to v9.18.1 (#6380) 2025-07-18 10:07:52 +03:00
renovate[bot] 97eaa6294c chore(deps): update dependency webdriverio to v9.18.1 2025-07-18 06:44:57 +00:00
Elian Doran dc02bb0850 chore(deps): update svelte monorepo (#6382) 2025-07-18 09:41:49 +03:00
renovate[bot] 2c8c041e1c chore(deps): update svelte monorepo 2025-07-18 06:04:00 +00:00
Elian Doran 874b1c6654 chore(deps): update dependency electron to v37.2.3 (#6370) 2025-07-18 09:02:15 +03:00
Elian Doran fb982c7097 fix(views/table): regression in restoring column width 2025-07-18 09:01:24 +03:00
Elian Doran b7f5ce600e chore(renovate): add more packages to svelte monorepo 2025-07-18 08:58:35 +03:00
renovate[bot] 91604c9e26 chore(deps): update dependency electron to v37.2.3 2025-07-18 01:55:54 +00:00
Elian Doran c874333a37 chore(client): fix type errors 2025-07-17 22:38:00 +03:00
Elian Doran 1298b968f2 fix(views/table): relation display sometimes not showing up 2025-07-17 22:31:54 +03:00
Elian Doran 6fe5a854a7 feat(views/table): allow deleting relations 2025-07-17 21:44:09 +03:00
Elian Doran aba3b5cb19 feat(views/table): hide all buttons in relation editor 2025-07-17 21:07:44 +03:00
Elian Doran 282aed22b5 feat(views/geomap): support recursive notes 2025-07-17 20:51:15 +03:00
Elian Doran 669a3d9dcf feat(views/table): automatic index col width 2025-07-17 20:44:31 +03:00
Elian Doran 9d7455d28a fix(views/table): expander style affecting row number 2025-07-17 20:44:00 +03:00
Elian Doran 4f0c8b081c feat(views): improve style in collections properties 2025-07-17 19:56:14 +03:00
Elian Doran a5db5298a0 feat(views/table): integrate depth limit into collection properties 2025-07-17 19:44:34 +03:00
Elian Doran 876c6e9252 feat(views/table): allow limiting depth 2025-07-17 19:34:29 +03:00
Elian Doran aef824d262 feat(views/table): add a context menu for the header outside columns 2025-07-17 15:36:33 +03:00
Elian Doran a25ce42490 feat(views/table): allow hiding number row & title 2025-07-17 15:00:19 +03:00
Elian Doran 8b0fdaccf4 feat(views/table): improve alignment for first level + increase indentation 2025-07-17 14:45:38 +03:00
Elian Doran bd840a2421 feat(views/table): align items expanders 2025-07-17 14:40:44 +03:00
Elian Doran 27d515f289 refactor(views/table): use builtin way of disabling branch elements 2025-07-17 14:34:40 +03:00
Elian Doran df3b9faf8d fix(client): tree operations no longer working due to loss of focus 2025-07-17 14:05:19 +03:00
Elian Doran 0f129734ae fix(link): popup triggering with bare right click 2025-07-17 11:19:29 +03:00
Elian Doran 275aacfba9 chore(vscode): add search excludes 2025-07-16 21:40:14 +03:00
Elian Doran e7f47a0663 feat(views/table): delete column definition as well 2025-07-16 21:40:01 +03:00
Elian Doran 66486541fe feat(client): batch delete column values 2025-07-16 21:30:16 +03:00
Elian Doran 34f1a84769 fix(views/table): wrong position when renaming column 2025-07-16 09:23:06 +03:00
Elian Doran 2244f0368f fix(views/table): index column ends up in the wrong position 2025-07-16 09:16:47 +03:00
Elian Doran 9d85005255 chore(deps): update dependency express-rate-limit to v8 (#6362) 2025-07-16 08:32:51 +03:00
Elian Doran ad8629dca6 chore(deps): update typescript-eslint monorepo to v8.37.0 (#6361) 2025-07-16 08:31:56 +03:00
Elian Doran cccfe0e05a chore(deps): update svelte monorepo (#6360) 2025-07-16 08:31:36 +03:00
Elian Doran a8874257e8 fix(deps): update dependency @codemirror/view to v6.38.1 (#6359) 2025-07-16 08:29:40 +03:00
Elian Doran f689c55f56 chore(deps): update node.js to v22.17.1 (#6358) 2025-07-16 08:28:48 +03:00
Elian Doran 853c7be8b8 chore(deps): update dependency openai to v5.9.2 (#6357) 2025-07-16 08:28:39 +03:00
Elian Doran 823df1e12d chore(deps): update dependency electron to v37.2.2 (#6356) 2025-07-16 08:14:56 +03:00
renovate[bot] 7570f818e9 chore(deps): update dependency express-rate-limit to v8 2025-07-16 02:39:01 +00:00
renovate[bot] 03aa5aea2c chore(deps): update typescript-eslint monorepo to v8.37.0 2025-07-16 02:38:09 +00:00
renovate[bot] a4e86ac353 chore(deps): update svelte monorepo 2025-07-16 02:36:37 +00:00
renovate[bot] cf6efc050a fix(deps): update dependency @codemirror/view to v6.38.1 2025-07-16 02:35:51 +00:00
renovate[bot] 3e0802176b chore(deps): update node.js to v22.17.1 2025-07-16 02:35:04 +00:00
renovate[bot] 697954d4d9 chore(deps): update dependency openai to v5.9.2 2025-07-16 02:34:06 +00:00
renovate[bot] 741f6c1114 chore(deps): update dependency electron to v37.2.2 2025-07-16 02:33:19 +00:00
Adorian Doran b2237ffa51 style/collections/tables: tweak nested rows 2025-07-16 05:23:13 +03:00
Adorian Doran 7b6d11bffa style/collections/tables: fix frozen cells overlapping with the outline of the left-side cells 2025-07-16 05:04:06 +03:00
Adorian Doran 97565e8f36 style(next)/collection/tables: improve the color scheme 2025-07-16 04:22:43 +03:00
perf3ct c0dfee8439 fix(metrics): don't assign a timestamp to Prometheus metrics, let the scraper assign the timestamp to the time series 2025-07-15 20:39:36 +00:00
Elian Doran fc98240614 chore(client): fix type error 2025-07-15 22:36:30 +03:00
Elian Doran 169d1203c2 fix(views/table): some context menu items active when they shouldn't 2025-07-15 22:30:52 +03:00
Elian Doran f3350bc8f5 refactor(views/table): better cleanup 2025-07-15 22:06:32 +03:00
Elian Doran 504a19275c feat(views/table): basic renaming of fields 2025-07-15 21:48:16 +03:00
Elian Doran 14cdc52670 feat(views/table): support renaming columns 2025-07-15 20:42:47 +03:00
Elian Doran cf8063f311 feat(views/table): format note ID as monospace 2025-07-15 19:32:13 +03:00
Elian Doran aa8902f5b9 fix(client): popup not displayed for existing attributes (closes #5718) 2025-07-15 18:55:29 +03:00
Elian Doran 7cd0e664ac feat(views/table): basic editing of columns (rename not supported) 2025-07-15 18:51:51 +03:00
Elian Doran a04804d3fa fix(views/table): wrong specs when restoring columns 2025-07-15 18:39:20 +03:00
Elian Doran 86f90e6685 fix(api): also rate limit etapi docs endpoint (#6352) 2025-07-15 17:06:52 +03:00
Elian Doran 8131a4b3d2 fix(views/table): events/commands not well sent 2025-07-15 15:49:32 +03:00
Elian Doran b91a3e13b0 refactor(views/table): move row editing to own component 2025-07-15 15:32:30 +03:00
Elian Doran 5a7a0d32d1 refactor(views/table): move col editing to own component 2025-07-15 14:53:18 +03:00
perf3ct 3f5df18d6c fix(api): also rate limit etapi docs endpoint 2025-07-14 21:12:00 +00:00
Elian Doran df2cede075 fix(views/calendar): nested entries in calendar view 2025-07-14 23:12:55 +03:00
Elian Doran 4321c161ac fix(views/calendar): duplicate entries in calendar view 2025-07-14 23:07:26 +03:00
Elian Doran b1f0c64ef2 chore(views/geo): typing issue 2025-07-14 22:52:37 +03:00
Elian Doran c9b37dcc77 refactor(views/table): rename event 2025-07-14 21:06:44 +03:00
Elian Doran ab093ed9a0 chore(views/table): add translations 2025-07-14 20:59:29 +03:00
Elian Doran cf31367acd feat(views/table): insert column to the right 2025-07-14 20:42:37 +03:00
Elian Doran e3d306cac3 fix(views/table): wrong insert position for insert left 2025-07-14 20:34:05 +03:00
Elian Doran 960d321019 fix(views/table): position not restored after new columns (closes #6285) 2025-07-14 20:32:24 +03:00
Elian Doran 2d4ac93221 feat(views/table): basic implementation for inserting columns at position 2025-07-14 19:14:10 +03:00
Elian Doran d4a4f15416 refactor(views/table): move attribute detail widget to view 2025-07-14 17:29:20 +03:00
Elian Doran 504a842d37 feat(views/table): force a refresh if #sorted is changed 2025-07-14 17:02:07 +03:00
Elian Doran ded5b1f5d2 feat(views/table): expand child notes by default 2025-07-14 17:00:01 +03:00
Elian Doran fcbbc21a80 feat(views/table): force a refresh if data tree changes 2025-07-14 16:58:14 +03:00
Elian Doran 38fce25b86 fix(views/table): show/hide columns not always updated properly 2025-07-14 16:51:20 +03:00
Elian Doran 4cc2fa5300 fix(snippets): warning about missing note IDs when deleting 2025-07-14 16:49:42 +03:00
Elian Doran 4a82c3f65a fix(views/table): insert above/below not working in nested trees 2025-07-14 16:49:29 +03:00
Elian Doran b255d70e18 fix(views/table): context menu remains active while clicking on an expand/collapse button 2025-07-14 16:24:54 +03:00
Elian Doran caa842cd55 fix(views/table): unable to update state for newly created rows 2025-07-14 16:16:55 +03:00
Elian Doran cd338085fb refactor(views/table): clean up 2025-07-14 15:52:21 +03:00
Elian Doran e703ce92a8 refactor(views/table): simplify context menu handling 2025-07-14 15:46:22 +03:00
Elian Doran 84479a2c2a feat(views/table): focus if creating child note 2025-07-14 15:38:57 +03:00
Elian Doran c13969217c feat(views/table): insert child note 2025-07-14 13:37:18 +03:00
Elian Doran 402540f483 feat(views/table): support recursive children update 2025-07-14 13:15:41 +03:00
Elian Doran 8c56315313 refactor(views): move full height detection to rendererer 2025-07-14 12:56:17 +03:00
Elian Doran b29c3eff6e refactor(views): prepare for supporting subtrees 2025-07-14 12:53:11 +03:00
Elian Doran ec7dacfc9b feat(views/table): improve expand/collapse button 2025-07-14 12:04:13 +03:00
Elian Doran 5f9a6a9f76 feat(views/table): integrate expander into note title section 2025-07-14 11:39:12 +03:00
Elian Doran 28f4aea3d5 refactor(views/table): use slightly more performant formatter for row number 2025-07-14 11:30:46 +03:00
Elian Doran 8d29c5fe1b feat(views/table): hide draggable rows if not supported 2025-07-14 11:29:14 +03:00
Elian Doran ccd935b562 refactor(views/table): don't configure reordering rows if not available 2025-07-14 11:22:32 +03:00
Elian Doran d77a49857b feat(views/table): basic nested tree support 2025-07-14 11:11:08 +03:00
Elian Doran e30478e5d4 chore(views/table): disable menu module since it's no longer necessary 2025-07-14 10:45:01 +03:00
Elian Doran 71863752cd feat(views/table): display both promoted and non-promoted attributes 2025-07-14 10:38:45 +03:00
Elian Doran e4a2a8e56d fix(text): selection and cursor not maintained properly when switching tabs 2025-07-14 09:58:58 +03:00
Elian Doran 0f1c505823 fix(tab): editor not focused after switching tabs 2025-07-14 09:58:58 +03:00
Elian Doran 1ecce11113 chore(deps): update dependency vite-plugin-static-copy to v3.1.1 (#6345) 2025-07-14 08:10:18 +03:00
renovate[bot] 2287d67fb5 chore(deps): update dependency vite-plugin-static-copy to v3.1.1 2025-07-13 19:16:04 +00:00
Elian Doran 5b4f17ef3d Update README.md (#6344) 2025-07-13 22:14:33 +03:00
Elian Doran 3720ab6df6 fix(views/table): not reacting to title changes 2025-07-13 21:38:23 +03:00
diyoyo 3c893d69e5 Update README.md
updating from `Notes` to `Trilium` in the `Contribute`section.
2025-07-13 20:29:02 +02:00
Elian Doran b93a4a3e42 fix(views/table): booleans not working 2025-07-13 21:06:41 +03:00
Elian Doran 23cef0ab94 chore(views/table): translate row menu 2025-07-13 16:56:03 +03:00
Elian Doran c8ffb8d694 chore(views/table): translate column menu 2025-07-13 16:52:29 +03:00
Elian Doran 08e08d8920 feat(views/table): improve column context menu 2025-07-13 16:45:04 +03:00
Elian Doran 7acd300163 feat(views/table): add option to clear sorting 2025-07-13 16:41:43 +03:00
Elian Doran d8d95db4ec feat(views/table): add sort by 2025-07-13 16:37:45 +03:00
Elian Doran af97d3ef1d feat(views/table): add back show/hide columns 2025-07-13 16:22:57 +03:00
Elian Doran c65ec14943 feat(views/table): hide column in contetx menu 2025-07-13 14:37:13 +03:00
Elian Doran adfdc7edb4 feat(views/table): drag handle to avoid editing issues 2025-07-13 14:24:12 +03:00
Elian Doran 8cced607eb feat(views/table): insert row before 2025-07-13 14:10:37 +03:00
Elian Doran 5dd5af90c2 feat(views/table): insert row below 2025-07-13 13:06:53 +03:00
Elian Doran 7a48333b4f chore(deps): update dependency @sveltejs/vite-plugin-svelte to v6 (#6341) 2025-07-13 08:22:24 +03:00
Elian Doran 7044533398 fix(deps): update dependency mind-elixir to v5.0.2 (#6340) 2025-07-13 08:21:06 +03:00
renovate[bot] 560aad8df6 chore(deps): update dependency @sveltejs/vite-plugin-svelte to v6 2025-07-13 01:34:38 +00:00
renovate[bot] 36c2099b2e fix(deps): update dependency mind-elixir to v5.0.2 2025-07-13 01:33:47 +00:00
Elian Doran 6c157675d7 feat(views/table): open in new tab/quick edit 2025-07-13 00:44:44 +03:00
Elian Doran 458d66cb21 feat(views/table): delete row from context menu (closes #6288) 2025-07-13 00:36:34 +03:00
Elian Doran 201e8911c5 chore: prefer short name 2025-07-12 23:48:42 +03:00
Elian Doran 1b1ed2408f feat(funding): add Buy Me a Coffee 2025-07-12 23:28:07 +03:00
Elian Doran 62487d21d8 feat(funding): add LiberaPay 2025-07-12 23:20:55 +03:00
Elian Doran bc752bdb0b fix(popup_editor): note icon overlapping with classic editor 2025-07-12 22:38:20 +03:00
Elian Doran 9e00d421fb fix(ckeditor): color and font mismatch after update 2025-07-12 22:34:27 +03:00
Elian Doran e7f02fe22b fix(deps): update ckeditor monorepo (major) (#6283) 2025-07-12 22:03:15 +03:00
Elian Doran 6d694f8e53 chore(client): update types 2025-07-12 20:20:41 +03:00
Elian Doran 977befd0a7 chore(ckeditor5): update ckeditor theme variable names 2025-07-12 20:00:01 +03:00
Elian Doran 1566ae4fbd chore(ckeditor5): fix references: DocumentSelection -> ModelDocumentSelection 2025-07-12 19:45:00 +03:00
Elian Doran 4e97490cc6 chore(ckeditor5): fix references: Selectable -> ModelSelectable 2025-07-12 19:44:38 +03:00
Elian Doran 446d5a0fcc chore(ckeditor5): fix references: Item -> ModelItem 2025-07-12 19:44:12 +03:00
Elian Doran 1fd6465012 chore(ckeditor5): fix references: NodeAttributes -> ModelNodeAttributes 2025-07-12 19:43:48 +03:00
Elian Doran 6cea8e3b87 chore(ckeditor5): fix references: Range -> ModelRange 2025-07-12 19:43:18 +03:00
Elian Doran 28a63e0326 chore(ckeditor5): fix references: DocumentFragment -> ModelDocumentFragment 2025-07-12 19:42:54 +03:00
Elian Doran b73da46111 chore(ckeditor5): fix references: Writer -> ModelWriter 2025-07-12 19:42:26 +03:00
Elian Doran abafa8c2d2 chore(ckeditor5): fix references: Position -> ModelPosition 2025-07-12 19:41:30 +03:00
Elian Doran 4ae3272cdf chore(ckeditor5): fix references: Element -> ModelElement 2025-07-12 19:40:24 +03:00
Elian Doran 6aa3b8dbd7 chore(ckeditor5-admonition): fix references 2025-07-12 19:38:36 +03:00
Elian Doran 395e9b2228 chore(ckeditor5-admonition): fix references: DocumentFragment -> ViewDocumentFragment 2025-07-12 19:29:51 +03:00
Elian Doran be33f68c52 chore(ckeditor5-math): fix references: DowncastWriter -> ViewDowncastWriter 2025-07-12 19:28:28 +03:00
Elian Doran 29d96381fa chore(ckeditor5-math): fix references: LiveRange -> ModelLiveRange 2025-07-12 19:27:46 +03:00
Elian Doran da8eecf774 chore(ckeditor5-math): fix references: LivePosition -> ModelLivePosition 2025-07-12 19:27:22 +03:00
Elian Doran de91326c12 chore(ckeditor5-math): fix references: Element -> ModelElement 2025-07-12 19:26:52 +03:00
Elian Doran ee1c3c35d7 chore(ckeditor5-mermaid): fix references: {Item,Node} -> Model{Item,Node} 2025-07-12 19:25:40 +03:00
Elian Doran 70eece1429 chore(ckeditor5-mermaid): fix references: Element -> ModelElement 2025-07-12 19:24:39 +03:00
Elian Doran b4f2be332b chore(ckeditor5-footnotes): fix references: Schema -> ModelSchema 2025-07-12 19:23:44 +03:00
Elian Doran 23fe76989b chore(ckeditor5-footnotes): fix references: Writer -> ModelWriter 2025-07-12 19:23:40 +03:00
Elian Doran 275d07659d chore(ckeditor5-footnotes): fix references: Range -> ModelRange 2025-07-12 19:23:37 +03:00
Elian Doran a901e92573 chore(ckeditor5-footnotes): fix references: Element -> ModelElement 2025-07-12 19:23:33 +03:00
Elian Doran 6ead31b45f chore(ckeditor5-footnotes): fix references: RootElement -> ModelRootElement 2025-07-12 19:23:30 +03:00
Elian Doran d4ce12dca9 chore(ckeditor5-footnotes): fix references: TextProxy -> ModelTextProxy 2025-07-12 19:23:25 +03:00
Elian Doran bb6e22cdb7 chore(ckeditor5-footnotes): fix references: Text -> ModelText 2025-07-12 19:23:13 +03:00
Elian Doran 2c9fc4812e chore(deps): update dependency electron to v37.2.1 (#6303) 2025-07-12 19:05:18 +03:00
Elian Doran 60f4554afa Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-12 19:04:43 +03:00
Elian Doran 3c486bfd1b chore(ci): set personal access token for conflict checker 2025-07-12 19:04:41 +03:00
Elian Doran 26b9a95bb2 chore(deps): update svelte monorepo (#6316) 2025-07-12 19:00:30 +03:00
Elian Doran f7c9217cea Table Collections: restyle (#6298) 2025-07-12 18:59:56 +03:00
Elian Doran e92022b73c Tree Context Menu: relocate the "Duplicate subtree" menu item (#6299) 2025-07-12 18:52:35 +03:00
Elian Doran 61ff2353c8 docs(help): update documentation on duplicate subtree 2025-07-12 18:52:12 +03:00
Elian Doran c8cca26ca4 Merge remote-tracking branch 'origin/main' into feat/tree-context-menu/relocate-duplicate-note-command 2025-07-12 18:41:58 +03:00
Elian Doran aa556ed4d5 Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-12 18:40:28 +03:00
Elian Doran 5d694a7bdf chore(ci): permissions issue with merge checker 2025-07-12 18:40:25 +03:00
renovate[bot] c4787dae23 fix(deps): update ckeditor monorepo 2025-07-12 10:57:11 +00:00
renovate[bot] 9f5f329c53 chore(deps): update dependency electron to v37.2.1 2025-07-12 10:55:36 +00:00
Elian Doran f82b96fcc4 chore(deps): update dependency @types/node to v22.16.3 (#6302) 2025-07-12 13:53:21 +03:00
renovate[bot] d4b24fa427 chore(deps): update svelte monorepo 2025-07-12 10:41:27 +00:00
renovate[bot] c852f67c59 chore(deps): update dependency @types/node to v22.16.3 2025-07-12 10:39:43 +00:00
Elian Doran 92c228a3c9 chore(deps): update nx monorepo to v21.2.3 (#6306) 2025-07-12 13:17:34 +03:00
renovate[bot] 42f948e2b3 chore(deps): update nx monorepo to v21.2.3 2025-07-12 08:59:08 +00:00
Elian Doran 13e8932117 chore(deps): update dependency @playwright/test to v1.54.1 (#6308) 2025-07-12 11:55:42 +03:00
renovate[bot] 910d34bd42 chore(deps): update dependency @playwright/test to v1.54.1 2025-07-12 07:57:58 +00:00
Elian Doran b204ba29e7 fix(deps): update eslint monorepo to v9.31.0 (#6311) 2025-07-12 10:54:00 +03:00
Elian Doran d49244cbc8 Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-12 10:08:35 +03:00
Elian Doran ef2f2f17b4 feat(ci): mark PRs with merge conflicts 2025-07-12 10:08:33 +03:00
renovate[bot] b9f21dcf4c fix(deps): update eslint monorepo to v9.31.0 2025-07-12 07:03:21 +00:00
Elian Doran 808fe690cc chore(deps): update dependency openai to v5.9.0 (#6309) 2025-07-12 10:01:18 +03:00
Elian Doran 901eec04e5 chore(deps): update dependency vite to v7.0.4 (#6305) 2025-07-12 10:01:02 +03:00
Elian Doran 9272394ada fix(deps): update dependency eslint-linter-browserify to v9.31.0 (#6310) 2025-07-12 09:59:19 +03:00
Elian Doran 4457982fae chore(deps): update ckeditor5 config packages to v12 (major) (#6312) 2025-07-12 09:59:05 +03:00
Elian Doran 7f67b2b461 chore(deps): update dependency dotenv to v17.2.0 (#6279) 2025-07-12 09:58:05 +03:00
Elian Doran 7f3934f4c3 chore(renovate): group svelte as monorepo 2025-07-12 09:57:29 +03:00
renovate[bot] a3b80a2cc4 chore(deps): update ckeditor5 config packages to v12 2025-07-12 01:37:21 +00:00
renovate[bot] 6d967e5e51 fix(deps): update dependency eslint-linter-browserify to v9.31.0 2025-07-12 01:34:54 +00:00
renovate[bot] b674ca90d1 chore(deps): update dependency openai to v5.9.0 2025-07-12 01:34:03 +00:00
renovate[bot] 95edb60a84 chore(deps): update dependency vite to v7.0.4 2025-07-12 01:29:46 +00:00
Adorian Doran 40add78ccb client/tree context menu: update translations 2025-07-12 02:22:00 +03:00
Adorian Doran 1029c24c06 client/tree context menu: relocate the "Duplicate subtree" menu item 2025-07-12 02:21:22 +03:00
Adorian Doran 94d94fe8fb Merge branch 'main' of https://github.com/TriliumNext/Trilium into feat/style/collections/table 2025-07-12 01:56:04 +03:00
Adorian Doran 49489c0f45 style/table collections: refactor 2025-07-12 01:55:07 +03:00
Adorian Doran 215833a2c9 style/table collections: tweak table footer 2025-07-12 01:40:18 +03:00
Adorian Doran a7471a3d47 style/table collections: tweak checkbox cells 2025-07-12 01:34:22 +03:00
Adorian Doran 909aaefbd7 style/table collections: restyle context menus 2025-07-12 01:17:09 +03:00
Elian Doran 15c2f56bf2 fix(options): display a less ambiguous/scary message after performing… (#6284) 2025-07-12 00:34:23 +03:00
Elian Doran 84cdfec415 Popup editor (#6292) 2025-07-12 00:30:33 +03:00
Elian Doran 91572ab8b9 fix(popup_editor): use cmd on macos 2025-07-11 22:53:14 +03:00
Adorian Doran ed758f4c92 style/table collections: tweak appearance 2025-07-11 22:39:49 +03:00
Elian Doran f1fc15e115 fix(link): popup menu no longer triggering 2025-07-11 22:34:41 +03:00
Adorian Doran 22300e8151 style/table collections: tweak appearance 2025-07-11 21:52:35 +03:00
Elian Doran 292646e14a fix(popup_editor): styles showing up when classic toolbar is shown 2025-07-11 20:46:48 +03:00
Elian Doran b4921a20d8 fix(client): type errors 2025-07-11 20:08:35 +03:00
Elian Doran 54be79a725 feat(in-app-help): link grid/list book types 2025-07-11 19:43:12 +03:00
Elian Doran 4fc47370fe docs(help): fix some old references to books 2025-07-11 19:42:19 +03:00
Elian Doran 9e30bcf233 docs(help): improve documentation on collections 2025-07-11 19:40:54 +03:00
Elian Doran e5712c54e6 docs(help): add a section on feature highlights 2025-07-11 19:09:42 +03:00
Elian Doran 2a4fe21a39 docs(help): document keyboard shortcuts for note tree 2025-07-11 18:50:56 +03:00
Elian Doran b259558f0f docs(help): document note tooltip 2025-07-11 18:33:16 +03:00
Elian Doran e2f6d9e0d6 docs(help): document quick edit 2025-07-11 18:27:55 +03:00
Elian Doran 4fc2b0fa5e feat(popup_editor): focus on editor automatically for easier editing 2025-07-11 16:52:13 +03:00
Elian Doran 8dca79ecf2 fix(popup_editor): block toolbar from behind modal interfering 2025-07-11 16:52:13 +03:00
Elian Doran c7f49f0e21 chore(popup_editor): switch keyboard combo to Ctrl+right click 2025-07-11 16:52:13 +03:00
Elian Doran bce2094fb2 fix(tree): middle click triggering paste 2025-07-11 16:52:13 +03:00
renovate[bot] 65c33e1aa0 chore(deps): update dependency webdriverio to v9.17.0 2025-07-11 16:52:13 +03:00
renovate[bot] 8e108bc5e2 chore(deps): update dependency svelte to v5.35.5 2025-07-11 16:52:13 +03:00
renovate[bot] 4e75ce7fdb chore(deps): update pnpm to v10.13.1 2025-07-11 16:52:13 +03:00
renovate[bot] 1e42574d28 fix(deps): update dependency i18next to v25.3.2 2025-07-11 16:52:13 +03:00
renovate[bot] 85ebaf6afa chore(deps): update dependency dotenv to v17.2.0 2025-07-11 16:52:13 +03:00
renovate[bot] 661c7e4056 chore(deps): update dependency @sveltejs/kit to v2.22.4 2025-07-11 16:52:13 +03:00
Elian Doran 1e8ea54dbc feat(popup_editor): smoother operation 2025-07-11 12:16:56 +03:00
Elian Doran ddbe7e9936 chore(popup_editor): clean up after closing modal 2025-07-11 12:00:32 +03:00
Elian Doran cab86175ef fix(file): pdf having a 10px margin at the bottom 2025-07-11 11:28:10 +03:00
Elian Doran ec7414b174 fix(popup_editor): collections being displayed under a full empty screen 2025-07-11 10:47:06 +03:00
Elian Doran 8343a5d1dd feat(popup_editor): add mobile support 2025-07-11 09:06:06 +03:00
Adorian Doran 18c55784c7 style/table collections: add a placeholder style for rows and cells 2025-07-11 00:16:15 +03:00
Elian Doran 39eac83d38 fix(popup_editor): mermaid not rendering properly 2025-07-10 23:21:37 +03:00
Elian Doran 55bd6fb57d feat(popup_editor): properly support file note types 2025-07-10 22:55:16 +03:00
Elian Doran 6fdec52332 fix(popup_editor): mind map not rendering properly 2025-07-10 22:48:33 +03:00
Adorian Doran 824a3c5fcc style/table collections: fix an issue with column headers 2025-07-10 21:54:32 +03:00
Adorian Doran 87da644027 style/table collections: add a placeholder style for column headers 2025-07-10 21:52:09 +03:00
Adorian Doran 4f42f543d8 style/table collections: create a stylesheet dedicated to the table view 2025-07-10 21:20:48 +03:00
Elian Doran 97ea3ac3fc fix(popup_editor): block popup not working 2025-07-10 20:54:50 +03:00
Elian Doran f04b75fd36 feat(popup_editor): add shortcut in links 2025-07-10 19:56:13 +03:00
Elian Doran f5bffc38f1 feat(popup_editor): add shortcut in note tree 2025-07-10 19:54:51 +03:00
Elian Doran 27738acefc feat(popup_editor): support collections 2025-07-10 19:39:08 +03:00
Elian Doran 59ce2072c5 feat(popup_editor): display promoted attributes 2025-07-10 19:19:44 +03:00
Elian Doran ed68dda70b feat(popup_editor): integrate with note tooltip 2025-07-10 18:57:13 +03:00
Elian Doran 892ab02f06 feat(popup_editor): integrate with geomap 2025-07-10 18:21:12 +03:00
Elian Doran 7d9196d5e1 feat(popup_editor): integrate with calendar for day notes 2025-07-10 18:14:23 +03:00
Elian Doran dccdb5ceb7 feat(popup_editor): integrate with calendar for existing notes 2025-07-10 17:54:27 +03:00
Elian Doran f961698e44 feat(popup_editor): improve fit for wider notes 2025-07-10 17:40:57 +03:00
Elian Doran 278fe3262e feat(popup_editor): improve fit for full-height note types 2025-07-10 17:33:00 +03:00
Elian Doran 1fc860b052 feat(popup_editor): integrate with tree context menu 2025-07-10 17:26:40 +03:00
Elian Doran 88a8311173 feat(popup_editor): integrate with note link context menu 2025-07-10 17:19:10 +03:00
Elian Doran 63dc5697dd fix(popup_editor): classic editor toolbar displayed when it shouldn't 2025-07-10 16:37:34 +03:00
Elian Doran b595d1fade fix(popup_editor): ckeditor modals not showing 2025-07-10 16:35:44 +03:00
Elian Doran d91c59b7d0 feat(popup_editor): floating classic toolbar 2025-07-10 16:16:09 +03:00
Elian Doran aa2ab0da31 feat(popup_editor): limit max height & reduce padding 2025-07-10 16:12:38 +03:00
Elian Doran 91f94106fb feat(popup_editor): integrate classic editor toolbar 2025-07-10 16:09:34 +03:00
Elian Doran 308f319138 feat(popup_editor): normalize paddings 2025-07-10 15:28:55 +03:00
Elian Doran fa0c01591a feat(popup_editor): integrate note title + icon into modal header 2025-07-10 15:25:07 +03:00
Elian Doran cb5a771490 feat(popup_editor): add editable note title and icon 2025-07-10 15:07:48 +03:00
Elian Doran 0c17a13462 fix(popup_editor): current tab events interfering 2025-07-10 14:57:32 +03:00
Romain DEP. 04593cb2d7 fix(options): display a less ambiguous/scary message after performing a consistency check.
The current message could easily be misinterpreted as an instruction for the user to go fix issues
2025-07-10 13:15:41 +02:00
Elian Doran b6f50b6af0 chore(deps): update dependency webdriverio to v9.17.0 (#6281) 2025-07-10 13:57:25 +03:00
renovate[bot] fc454cba03 chore(deps): update dependency webdriverio to v9.17.0 2025-07-10 09:54:03 +00:00
Elian Doran 6f165df29e chore(deps): update dependency svelte to v5.35.5 (#6277) 2025-07-10 12:49:50 +03:00
Elian Doran d16468071d chore(deps): update pnpm to v10.13.1 (#6282) 2025-07-10 12:49:38 +03:00
renovate[bot] 20a492523f chore(deps): update dependency svelte to v5.35.5 2025-07-10 07:43:08 +00:00
Elian Doran 1216f51c78 fix(deps): update dependency i18next to v25.3.2 (#6278) 2025-07-10 10:42:16 +03:00
Elian Doran ea3ac1041b chore(deps): update dependency dotenv to v17.2.0 (#6280) 2025-07-10 10:41:45 +03:00
Elian Doran d838e8baf0 chore(deps): update dependency @sveltejs/kit to v2.22.4 (#6276) 2025-07-10 10:41:01 +03:00
renovate[bot] 60a7347d7d chore(deps): update pnpm to v10.13.1 2025-07-10 02:40:45 +00:00
renovate[bot] 4e05e79426 chore(deps): update dependency dotenv to v17.2.0 2025-07-10 02:39:34 +00:00
renovate[bot] aa872f47f2 chore(deps): update dependency dotenv to v17.2.0 2025-07-10 02:38:39 +00:00
renovate[bot] fbd833ad86 fix(deps): update dependency i18next to v25.3.2 2025-07-10 02:37:42 +00:00
renovate[bot] bee65ed32c chore(deps): update dependency @sveltejs/kit to v2.22.4 2025-07-10 02:35:48 +00:00
Elian Doran 5adca76a9a refactor(popup_editor): better error handling 2025-07-09 21:56:11 +03:00
Elian Doran e7467f6446 feat(popup_editor): get editor to show up if note is open somewhere else 2025-07-09 21:44:42 +03:00
Elian Doran e49473fbd3 refactor(client): unused import 2025-07-09 21:20:24 +03:00
Elian Doran bfec44aa5a refactor(popup_editor): inject note detail widget 2025-07-09 21:20:16 +03:00
Elian Doran 55b3bf6036 feat(popup_editor): create an empty modal with auto-trigger 2025-07-09 21:12:18 +03:00
Elian Doran c9c07f0cb0 chore(book_properties): add config for all note types 2025-07-09 20:53:35 +03:00
Elian Doran e25727441d chore(book_properties): add translations 2025-07-09 20:40:04 +03:00
Elian Doran 51b7955ccd refactor(book_properties): move rendering to book_properties 2025-07-09 20:37:05 +03:00
Elian Doran 196bba9cda refactor(book_properties): list buttons are now declarative 2025-07-09 20:29:58 +03:00
Elian Doran 430ed78d85 feat(book_properties): improve layout & accessibility 2025-07-09 20:14:42 +03:00
Elian Doran 2d11ed805d feat(book_properties): react to external changes 2025-07-09 20:13:08 +03:00
Elian Doran f55426bdb0 feat(collections): basic properties for calendar 2025-07-09 20:10:25 +03:00
Elian Doran 87b5068fec chore(collections): rename references to book 2025-07-09 19:40:35 +03:00
Elian Doran 9ddd1a4ae2 feat(collections): add i18n 2025-07-09 19:37:10 +03:00
Elian Doran 736bc9c9bd chore(insert_note): improve layout slightly 2025-07-09 19:32:29 +03:00
Elian Doran 5a2da62992 feat(collections): hide book default note type 2025-07-09 19:28:44 +03:00
Elian Doran 1a72eb91ee feat(collections): display grid/view in collections list 2025-07-09 19:22:12 +03:00
Elian Doran 0d3c5b06e2 feat(collections): add calendar as a standalone template 2025-07-09 19:05:05 +03:00
Elian Doran 035b72a08d feat(insert_note): add dedicated section for collections 2025-07-09 18:55:08 +03:00
Elian Doran fc4a595725 feat(insert_note): display on two columns 2025-07-09 18:37:09 +03:00
Elian Doran 444969bcf4 chore(deps): update dependency @types/node to v22.16.2 (#6267) 2025-07-09 08:47:37 +03:00
renovate[bot] 2cb6b14eca chore(deps): update dependency @types/node to v22.16.2 2025-07-09 05:23:39 +00:00
Elian Doran 468b5022a4 chore(deps): update dependency @types/express-serve-static-core to v5.0.7 (#6265) 2025-07-09 08:20:41 +03:00
Elian Doran c1897563ca chore(deps): update dependency @types/leaflet to v1.9.20 (#6266) 2025-07-09 08:20:28 +03:00
Elian Doran 5e533896b9 chore(deps): update dependency openai to v5.8.3 (#6268) 2025-07-09 08:20:11 +03:00
Elian Doran d3ceb7cfc1 chore(deps): update dependency vite to v7.0.3 (#6269) 2025-07-09 08:19:59 +03:00
Elian Doran 731f74f421 chore(deps): update dependency is-svg to v6.1.0 (#6270) 2025-07-09 08:19:40 +03:00
renovate[bot] 46d82651a3 chore(deps): update dependency is-svg to v6.1.0 2025-07-09 02:08:18 +00:00
renovate[bot] b3108c7e2b chore(deps): update dependency vite to v7.0.3 2025-07-09 02:07:23 +00:00
renovate[bot] 0cb988470e chore(deps): update dependency openai to v5.8.3 2025-07-09 02:06:29 +00:00
renovate[bot] 5a030014b0 chore(deps): update dependency @types/leaflet to v1.9.20 2025-07-09 02:04:53 +00:00
renovate[bot] 2a43ef4dae chore(deps): update dependency @types/express-serve-static-core to v5.0.7 2025-07-09 02:04:03 +00:00
Elian Doran 6b5f9fc6ff Merge branch 'main' of https://github.com/TriliumNext/trilium 2025-07-08 22:33:44 +03:00
Elian Doran b3a156c20d Update docker-compose.yml with new container image (#6263) 2025-07-08 21:33:51 +03:00
Elian Doran 24340d3a8e refactor(next): remove use of :has for performance reasons 2025-07-08 21:24:18 +03:00
Elian Doran 2fac2a8c5e chore(next): improve performance of a specific selector 2025-07-08 21:09:13 +03:00
Elian Doran decb0c702d chore(nx): disable cache for rebuild-deps 2025-07-08 20:54:14 +03:00
Elian Doran d45ff6cca5 fix(next): another non-performant CSS selector 2025-07-08 20:53:06 +03:00
DeveloperCrocodiles 83833e668c Update docker-compose.yml with new container image
Update docker-compose.yml to use the new container image triliumnext/trilium rather than triliumnext/notes.
2025-07-08 16:51:10 +01:00
Elian Doran 2cc181d1ac fix(tree): pasting in editor on middle click (closes #5812) 2025-07-08 15:51:19 +03:00
Elian Doran a946ce3534 chore(deps): update dependency dotenv to v17.1.0 (#6255) 2025-07-08 09:05:40 +03:00
renovate[bot] 3e9f476b37 chore(deps): update dependency dotenv to v17.1.0 2025-07-08 05:21:18 +00:00
Elian Doran de65c748a4 chore(deps): update dependency esbuild to v0.25.6 (#6249) 2025-07-08 08:18:57 +03:00
Elian Doran 8a2bfb9d7b chore(deps): update dependency supertest to v7.1.3 (#6250) 2025-07-08 08:18:20 +03:00
Elian Doran a1ced31fea chore(deps): update dependency svelte to v5.35.4 (#6251) 2025-07-08 08:18:08 +03:00
Elian Doran 989a9f506e fix(deps): update dependency @electron/remote to v2.1.3 (#6252) 2025-07-08 08:17:58 +03:00
Elian Doran 59d55e2489 fix(deps): update dependency mermaid to v11.8.1 (#6253) 2025-07-08 08:17:47 +03:00
Elian Doran 2b312a9234 chore(deps): update typescript-eslint monorepo to v8.36.0 (#6256) 2025-07-08 08:17:31 +03:00
Elian Doran 16d9b982c2 chore(deps): update dependency dotenv to v17.1.0 (#6254) 2025-07-08 08:17:19 +03:00
renovate[bot] a5600e75f5 chore(deps): update typescript-eslint monorepo to v8.36.0 2025-07-08 02:01:43 +00:00
renovate[bot] f91dea62b6 chore(deps): update dependency dotenv to v17.1.0 2025-07-08 01:59:19 +00:00
renovate[bot] 4915ffcf2a fix(deps): update dependency mermaid to v11.8.1 2025-07-08 01:58:31 +00:00
renovate[bot] 9dbea2aa18 fix(deps): update dependency @electron/remote to v2.1.3 2025-07-08 01:57:38 +00:00
renovate[bot] 45f6a70fb8 chore(deps): update dependency svelte to v5.35.4 2025-07-08 01:56:42 +00:00
renovate[bot] 96b4c611cc chore(deps): update dependency supertest to v7.1.3 2025-07-08 01:55:48 +00:00
renovate[bot] 4e559d6594 chore(deps): update dependency esbuild to v0.25.6 2025-07-08 01:54:54 +00:00
Elian Doran db1a599f95 fix(deps): update dependency mind-elixir to v5 (#6239) 2025-07-07 22:12:14 +03:00
Elian Doran 040964bbb7 chore(env): add variable for inspector 2025-07-07 22:10:07 +03:00
Elian Doran dc6a303154 fix(mindmap): update deps to reintegrate features 2025-07-07 20:59:31 +03:00
Elian Doran f88f14c983 Merge remote-tracking branch 'origin/main' into renovate/mind-elixir-5.x 2025-07-07 20:50:23 +03:00
Elian Doran f870649256 Geomap improvements & conversion to book type (#6241) 2025-07-07 20:27:07 +03:00
Elian Doran ed4dc30a6e refator(client): type error 2025-07-07 20:07:04 +03:00
Elian Doran ce9010ff13 chore(client): add some documentation 2025-07-07 20:04:17 +03:00
Elian Doran 994e9fa852 docs(views/geomap): update user manual to reflect newly added features 2025-07-07 19:54:06 +03:00
Elian Doran 9df7d6227e Merge branch 'feature/geomap_collection' of https://github.com/TriliumNext/trilium into feature/geomap_collection 2025-07-07 19:20:24 +03:00
Elian Doran 242a576548 refactor(views/geomap): solve type errors 2025-07-07 19:15:38 +03:00
Elian Doran c1a5808f37 feat(views/geomap): allow disabling editing 2025-07-07 19:04:47 +03:00
Elian Doran 5c6bb99d78 refactor(views/geomap): integrate drag into editing 2025-07-07 18:04:17 +03:00
Elian Doran 63c408c45b feat(views/geomap): dragging notes that are not children 2025-07-07 18:02:32 +03:00
Elian Doran 2a665dffbc feat(views/geomap): dragging notes that are children 2025-07-07 17:55:16 +03:00
Elian Doran 6509acd6ee feat(views/geomap): add open location to blank item as well 2025-07-07 17:24:42 +03:00
Elian Doran 4853d45609 chore(nix): switch to master for electron 37 (closes #6217) 2025-07-07 14:28:50 +03:00
Jon Fuller fe78c1fee3 fix(api): implement better rate limiting controls for pre-auth endpoints (#6243) 2025-07-06 14:15:24 -07:00
Elian Doran 8102172557 feat(views/geomap): display geolocation in both context menus 2025-07-06 23:48:51 +03:00
Elian Doran a1341e6036 feat(views/geomap): display geolocation in empty menu 2025-07-06 23:48:29 +03:00
Elian Doran d31af2ddc2 feat(views/geomap): add a context menu for empty area 2025-07-06 23:34:07 +03:00
Elian Doran a563330136 feat(import/zip): improve geomap compatibility 2025-07-06 23:05:21 +03:00
Elian Doran a58e5789bc feat(import/zip): backward compatibility 2025-07-06 22:33:19 +03:00
Elian Doran 68e258f23b fix(views/geomap): unable to change note type to geomap 2025-07-06 22:32:24 +03:00
Elian Doran dd18866156 refactor(server): convert to switch 2025-07-06 22:22:19 +03:00
Elian Doran 1b1f1957c3 chore(views/help): reintroduce help button 2025-07-06 22:11:03 +03:00
Elian Doran ff6b4effbd fix(views/geomap): tooltip not showing 2025-07-06 21:29:42 +03:00
Elian Doran 06fa59239c chore(views/geomap): remove old files 2025-07-06 21:12:05 +03:00
Elian Doran 557bfbd1d6 feat(views/geomap): remove old geo map type 2025-07-06 20:23:50 +03:00
Elian Doran f5a6dfa629 feat(views/geomap): migrate db to new collection type 2025-07-06 20:17:15 +03:00
Elian Doran ce33dfb003 feat(views): delete duplicate attachments 2025-07-06 20:16:47 +03:00
Elian Doran 7b1c058d29 feat(views/geomap): add template 2025-07-06 19:21:53 +03:00
perf3ct 04c8f8a123 fix(api): implement better rate limiting controls for pre-auth endpoints 2025-07-06 16:19:08 +00:00
Elian Doran d15fccb1d8 chore(views/geomap): integrate context menu options 2025-07-06 18:36:36 +03:00
Elian Doran 229dd9cd18 chore(views/geomap): integrate touchbar 2025-07-06 18:25:53 +03:00
Elian Doran a4faaa406b fix(views/geomap): proper refresh 2025-07-06 17:54:13 +03:00
Elian Doran b6d2de54b2 chore(views/geomap): reintroduce adding notes 2025-07-06 17:51:57 +03:00
Elian Doran d5e81d77a2 refactor(views/geomap): move context menu 2025-07-06 12:28:24 +03:00
Elian Doran 939e99637f chore(views/geomap): reintroduce moving markers 2025-07-06 12:22:51 +03:00
Elian Doran 579a261612 chore(views/geomap): reintroduce display of tracks 2025-07-06 12:19:09 +03:00
Elian Doran 6d03304cbb chore(views/geomap): reintroduce display of markers 2025-07-06 12:12:59 +03:00
Elian Doran b8d41b3421 chore(views/geomap): reintroduce viewport storage 2025-07-06 11:47:37 +03:00
Elian Doran 6a5bb1f5c8 chore(views/geomap): reintroduce map init 2025-07-06 11:36:59 +03:00
Elian Doran cd742a4617 chore(views/geomap): basic initialization 2025-07-06 11:30:24 +03:00
Elian Doran 54063b97ad feat(views/geomap): display even if empty 2025-07-06 11:22:49 +03:00
Elian Doran 7abb67e737 feat(views/geomap): display in view type 2025-07-06 11:21:25 +03:00
Elian Doran 00fd1ba137 feat(views/geomap): make full-height 2025-07-06 11:15:28 +03:00
Elian Doran 7ea37b9eb9 feat(views/geomap): create empty view type 2025-07-06 11:13:17 +03:00
Elian Doran b749de8fe1 fix(mind_map): styles no longer loading after major update 2025-07-06 10:08:45 +03:00
Elian Doran 8efef6842d Note Type Badges (#6229) 2025-07-06 09:54:27 +03:00
renovate[bot] dc206f38d5 fix(deps): update dependency mind-elixir to v5 2025-07-06 01:15:10 +00:00
Elian Doran 29a00a6c0e Merge branch 'main' of github.com:TriliumNext/trilium 2025-07-05 22:31:08 +03:00
Elian Doran fe678230a8 feat(webview): allow popups (closes #5698) 2025-07-05 22:31:04 +03:00
Adorian Doran 9cdbeb061f Merge branch 'main' of https://github.com/TriliumNext/Trilium into feat/context-menu/menu-items-badge-support 2025-07-05 22:06:39 +03:00
Adorian Doran 6c308f35c1 style(next)/badges: tweak appearance 2025-07-05 21:35:00 +03:00
Adorian Doran 34b89cf2e8 style/badges: tweak appearance 2025-07-05 20:46:45 +03:00
Elian Doran b566a188dc chore(client): crash during serialization for CKEditor errors 2025-07-05 20:31:48 +03:00
Elian Doran 998432e236 chore(deps): update dependency @ckeditor/ckeditor5-package-tools to v4.0.1 (#6178) 2025-07-05 20:20:02 +03:00
Adorian Doran 1af8edfe4d client/note type chooser: add support for badges 2025-07-05 19:31:59 +03:00
Adorian Doran 5bf01106c5 client/context menus/insert note: refactor 2025-07-05 19:13:34 +03:00
Adorian Doran a45289e385 client/context menus/insert note: fix a typo again 2025-07-05 18:54:22 +03:00
Adorian Doran 4ffd005b09 client/context menus/insert note: fix a typo 2025-07-05 18:53:40 +03:00
Adorian Doran e6ca89fea8 client/context menus/insert note: fix a comment 2025-07-05 18:53:03 +03:00
Adorian Doran 2225aea756 client/context menus/insert note: fix a console log 2025-07-05 18:49:22 +03:00
Adorian Doran bfc4a84020 client/context menus/insert note: avoid highlighting predefined templates as "new" 2025-07-05 18:46:52 +03:00
Elian Doran 5390bfdcab fix(help): ligatures in code causing confusion (closes #6224) 2025-07-05 18:32:55 +03:00
Elian Doran 301211ff41 chore(client): remove comment 2025-07-05 18:25:52 +03:00
renovate[bot] 64139e4e08 chore(deps): update dependency @ckeditor/ckeditor5-package-tools to v4.0.1 2025-07-05 15:22:56 +00:00
Elian Doran e6485cde92 chore(deps): update dependency webdriverio to v9.16.2 (#6193) 2025-07-05 18:20:30 +03:00
Adorian Doran 891f6ba66f client/context menus/insert note: highlight new templates based on their creation date 2025-07-05 18:10:36 +03:00
Elian Doran 5d3c1e3fec chore(client,website): bypass issue with plugins 2025-07-05 17:58:36 +03:00
Elian Doran 087e755390 Revert "chore(client): bypass issue with plugins"
This reverts commit 025dc1ce75.
2025-07-05 17:48:53 +03:00
Elian Doran 025dc1ce75 chore(client): bypass issue with plugins 2025-07-05 17:44:54 +03:00
Elian Doran 703200338d Merge remote-tracking branch 'origin/main' into renovate/webdriverio-monorepo 2025-07-05 17:15:39 +03:00
Elian Doran 377c93ca0b chore(deps): update dependency @anthropic-ai/sdk to v0.56.0 (#6211) 2025-07-05 14:50:20 +03:00
renovate[bot] 69394ffe29 chore(deps): update dependency webdriverio to v9.16.2 2025-07-05 11:38:36 +00:00
renovate[bot] f85231d74a chore(deps): update dependency @anthropic-ai/sdk to v0.56.0 2025-07-05 11:37:47 +00:00
Elian Doran b93d8b0159 chore(deps): update dependency @types/tabulator-tables to v6.2.7 (#6220) 2025-07-05 10:16:45 +03:00
Elian Doran 67b9329903 chore(deps): update dependency vite to v7.0.2 (#6221) 2025-07-05 10:16:10 +03:00
Elian Doran c0edd4ea4f fix(deps): update dependency i18next to v25.3.1 (#6222) 2025-07-05 10:15:41 +03:00
renovate[bot] 8eaf2786e8 fix(deps): update dependency i18next to v25.3.1 2025-07-05 02:42:27 +00:00
renovate[bot] 25622df464 chore(deps): update dependency vite to v7.0.2 2025-07-05 02:41:34 +00:00
renovate[bot] a48900e178 chore(deps): update dependency @types/tabulator-tables to v6.2.7 2025-07-05 02:40:46 +00:00
Elian Doran ac8b0535d2 Introduce the table view (#6097) 2025-07-04 23:34:34 +03:00
Elian Doran 6ce25a825b feat(help): link table note type 2025-07-04 23:19:33 +03:00
Elian Doran b3f56851b8 docs(guide): document table functionality 2025-07-04 23:03:50 +03:00
Elian Doran 4b86fedce1 Merge remote-tracking branch 'origin/main' into feature/table_view
; Conflicts:
;	pnpm-lock.yaml
2025-07-04 21:47:26 +03:00
Elian Doran 1ebb70c4d2 fix(views/table): refresh when relation changes 2025-07-04 21:43:56 +03:00
Elian Doran 3de7b81be8 refactor(views/table): fix some type errors 2025-07-04 21:31:43 +03:00
Elian Doran d08225339c style(views/table): reintroduce horizontal padding 2025-07-04 21:27:45 +03:00
Elian Doran ba22d0706f chore(views/table): unnecessary code 2025-07-04 21:25:27 +03:00
Elian Doran ef80f104c0 fix(views/table): scroll lost when updating data 2025-07-04 21:22:13 +03:00
Elian Doran af296a1e4e refactor(views/table): split column & rows into separate file 2025-07-04 21:18:52 +03:00
Elian Doran 28a755306a feat(views/table): deduplicate columns 2025-07-04 21:15:10 +03:00
Elian Doran 461e085eff fix(views/table): column width or visibility lost after adding new column 2025-07-04 21:11:50 +03:00
Elian Doran fbda049c32 fix(views/table): column context menu disappearing after update 2025-07-04 20:57:39 +03:00
Elian Doran 4ded5e2b98 feat(views/table): hide footer in search 2025-07-04 20:56:10 +03:00
Elian Doran 63537aff20 feat(views/table): disable reordering in search 2025-07-04 20:43:16 +03:00
Elian Doran 0f7a2adf15 feat(views/table): improve layout 2025-07-04 20:38:48 +03:00
Elian Doran 60963abe2c refactor(views/table): reduce duplication 2025-07-04 20:33:48 +03:00
Elian Doran 08cf95aa38 feat(views/table): merge open note and icon into title 2025-07-04 20:22:55 +03:00
Elian Doran e5b10ab16a feat(views/table): set up relations not as a link 2025-07-04 20:08:41 +03:00
Elian Doran 7f5a1ee45a feat(ribbon): stop focusing book tab by default 2025-07-04 19:47:52 +03:00
Elian Doran 15c593f68e feat(views/table): automatically focus on title when creating new row 2025-07-04 19:46:37 +03:00
Elian Doran 5f8ef0395b feat(views/table): improve default layout 2025-07-04 19:37:05 +03:00
Elian Doran 513636e1e0 feat(views/table): hide column titles for small ones 2025-07-04 19:32:22 +03:00
Elian Doran ae9b2c08a9 feat(views/table): hide context menu for small columns 2025-07-04 19:29:33 +03:00
Elian Doran d5327b3b4a feat(views/table): get rid of note position column 2025-07-04 19:26:06 +03:00
Elian Doran 323e3d3cac feat(views/table): hide note ID by default 2025-07-04 19:25:08 +03:00
Elian Doran 01b2257063 feat(views/table): relocate new row/column buttons 2025-07-04 19:23:26 +03:00
Elian Doran c69ef611a0 feat(views/table): basic reordering mechanism 2025-07-04 18:53:31 +03:00
Elian Doran dcad23316d style(views/table): improve autocomplete styling 2025-07-04 18:26:24 +03:00
Elian Doran e411f9932f feat(views/table): display note title when editing relation 2025-07-04 18:13:07 +03:00
Elian Doran 854969e1b8 feat(views/table): react to external attribute changes 2025-07-04 18:05:24 +03:00
Elian Doran 4ac7b6e9e8 feat(views/table): allow creation of new notes 2025-07-04 17:17:39 +03:00
Elian Doran ac70908c5a feat(views/table): integrate reference-like for relations 2025-07-04 16:14:14 +03:00
Elian Doran 45ac70b78f feat(views/table): proper storage of relations 2025-07-04 15:07:40 +03:00
Elian Doran a4664576fe feat(views/table): separate data model for relations 2025-07-04 15:05:00 +03:00
Elian Doran b293643398 feat(views/table): basic formatter for relations 2025-07-04 15:02:10 +03:00
Elian Doran a2e197facd feat(views/table): set up relation editor 2025-07-04 14:50:07 +03:00
Elian Doran 8614d39ef4 chore(views/table): remove unnecessary log 2025-07-04 14:42:04 +03:00
Elian Doran 6456bb34ae chore(views/table): start implementing a relation editor 2025-07-04 14:29:40 +03:00
Elian Doran f5dc4de1c1 feat(views/table): parse relations 2025-07-04 14:12:36 +03:00
Adorian Doran d869056910 client/note types mapping: add a way to highlight newly introduced types 2025-07-04 13:47:50 +03:00
Adorian Doran 821e4b17cb client/refactor: de-duplicate the note type mapping 2025-07-04 13:38:02 +03:00
Elian Doran d8cb5efd2d Merge remote-tracking branch 'origin/main' into feature/table_view 2025-07-04 12:59:03 +03:00
Adorian Doran f90e2fb484 client/tree context menu/insert note: mark the beta note types with a badge 2025-07-04 11:46:10 +03:00
Adorian Doran 2c9a7144da Merge branch 'main' of https://github.com/TriliumNext/Trilium into feat/context-menu/menu-items-badge-support 2025-07-04 11:36:43 +03:00
Adorian Doran 88d1af7210 style/refactor: make the current badge style to be global 2025-07-04 11:33:29 +03:00
Elian Doran 300e5a5528 chore(deps): update dependency svelte to v5.35.2 (#6209) 2025-07-04 09:36:34 +03:00
renovate[bot] 4418fefe4b chore(deps): update dependency svelte to v5.35.2 2025-07-04 06:15:18 +00:00
Elian Doran fe5d1cac9a Feature/note map filter (#6104) 2025-07-04 09:12:20 +03:00
Elian Doran 49d17fff9b chore(deps): update dependency globals to v16.3.0 (#6190) 2025-07-04 09:11:53 +03:00
Elian Doran 557c6d2d8b chore(deps): update dependency stylelint to v16.21.1 (#6208) 2025-07-04 09:11:40 +03:00
Elian Doran 45fc62357d chore(deps): update dependency vite to v7.0.1 (#6210) 2025-07-04 09:11:26 +03:00
Elian Doran 840e3cc22f fix(deps): update dependency force-graph to v1.50.1 (#6212) 2025-07-04 09:11:16 +03:00
Elian Doran c158c7fc88 fix(deps): update dependency mermaid to v11.8.0 (#6213) 2025-07-04 09:09:37 +03:00
renovate[bot] bc6f8fc2dd fix(deps): update dependency mermaid to v11.8.0 2025-07-04 01:40:34 +00:00
renovate[bot] 117730acb2 fix(deps): update dependency force-graph to v1.50.1 2025-07-04 01:39:40 +00:00
renovate[bot] 595a7dac83 chore(deps): update dependency vite to v7.0.1 2025-07-04 01:38:08 +00:00
renovate[bot] 64a4d70df4 chore(deps): update dependency stylelint to v16.21.1 2025-07-04 01:36:34 +00:00
renovate[bot] be36199fe1 chore(deps): update dependency globals to v16.3.0 2025-07-03 21:26:15 +00:00
Elian Doran e46ad25677 Merge branch 'main' into feat/note-map-filter 2025-07-04 00:20:44 +03:00
Elian Doran d5ee663922 Tree context menu: reorder the note types of "Insert (child) note..." (#6206) 2025-07-04 00:16:52 +03:00
Elian Doran a7ab4be055 chore(deps): update vitest monorepo to v3.2.4 (#6186) 2025-07-03 23:39:37 +03:00
Elian Doran 6bbf29e75a Merge branch 'main' into renovate/vitest-monorepo 2025-07-03 23:39:08 +03:00
Adorian Doran 0a06c60cb7 client/context menus: add support to display badges for menu items 2025-07-03 23:27:02 +03:00
Adorian Doran 03658575eb client/context menu/insert note: reorder note types 2025-07-03 22:45:49 +03:00
Adorian Doran 38114bddb9 style(next)/ribbon/file & image properties: tweak the vertical alignment of properties 2025-07-03 21:36:37 +03:00
Adorian Doran 0711a197db style(next): tweak the "Delete notes preview" dialog width 2025-07-03 20:52:38 +03:00
Adorian Doran f8f818b211 style(next): fix a performance-heavy selector 2025-07-03 20:00:46 +03:00
Elian Doran 988932209c chore(deps): update dependency svelte to v5.35.1 (#6192) 2025-07-03 11:53:59 +03:00
Elian Doran 2aa56cec30 chore(deps): update typescript-eslint monorepo to v8.35.1 (#6194) 2025-07-03 11:53:45 +03:00
renovate[bot] 93d493650c chore(deps): update typescript-eslint monorepo to v8.35.1 2025-07-03 08:25:21 +00:00
renovate[bot] c6162ddcb4 chore(deps): update dependency svelte to v5.35.1 2025-07-03 08:24:09 +00:00
renovate[bot] 038517eda4 chore(deps): update vitest monorepo to v3.2.4 2025-07-03 08:23:31 +00:00
Elian Doran 30a9db73ab chore(deps): update dependency stylelint to v16.21.0 (#6191) 2025-07-03 11:21:09 +03:00
Elian Doran a50aa41bdb fix(deps): update eslint monorepo to v9.30.1 (#6198) 2025-07-03 11:20:43 +03:00
renovate[bot] cbb322fdb8 fix(deps): update eslint monorepo to v9.30.1 2025-07-03 06:23:39 +00:00
renovate[bot] 026e2a020d chore(deps): update dependency stylelint to v16.21.0 2025-07-03 06:20:49 +00:00
Elian Doran 07aab1d005 chore(deps): update ckeditor5 config packages to v11 (major) (#6199) 2025-07-03 09:14:38 +03:00
Elian Doran 26f0f7b188 fix(deps): update dependency tsx to v4.20.3 (#6197) 2025-07-03 09:13:57 +03:00
Elian Doran 1efde3b86b fix(deps): update dependency better-sqlite3 to v12.2.0 (#6196) 2025-07-03 09:13:23 +03:00
Elian Doran 8c1318f379 fix(deps): update dependency @inlang/paraglide-js to v2.2.0 (#6195) 2025-07-03 09:13:10 +03:00
Elian Doran 40e67e8e17 chore(deps): update dependency eslint-plugin-svelte to v3.10.1 (#6189) 2025-07-03 09:12:33 +03:00
Elian Doran 04466f52fd chore(deps): update dependency electron to v37.2.0 (#6188) 2025-07-03 09:11:59 +03:00
Elian Doran 06baa5fb57 chore(deps): update dependency @sveltejs/kit to v2.22.2 (#6187) 2025-07-03 09:11:23 +03:00
Elian Doran 04e1657628 chore(deps): update tailwindcss monorepo to v4.1.11 (#6185) 2025-07-03 09:10:31 +03:00
Elian Doran 7816c8cab0 chore(deps): update nx monorepo to v21.2.2 (#6184) 2025-07-03 09:10:18 +03:00
Elian Doran 6636e658a4 chore(deps): update dependency svelte-check to v4.2.2 (#6183) 2025-07-03 09:09:59 +03:00
Elian Doran 2a06f0daef chore(deps): update dependency lint-staged to v16.1.2 (#6182) 2025-07-03 09:09:43 +03:00
Elian Doran 883cfa588c chore(deps): update dependency dotenv to v17.0.1 (#6181) 2025-07-03 09:09:23 +03:00
Elian Doran 68011a0b5a Merge pull request #6180 from TriliumNext/renovate/mime-types-3.x-lockfile
chore(deps): update dependency @types/mime-types to v3.0.1
2025-07-03 09:08:59 +03:00
Elian Doran 5247d1a371 Merge pull request #6179 from TriliumNext/renovate/eslint-compat-1.x-lockfile
chore(deps): update dependency @eslint/compat to v1.3.1
2025-07-03 09:08:20 +03:00
renovate[bot] cabdd528d4 chore(deps): update ckeditor5 config packages to v11 2025-07-03 00:19:09 +00:00
renovate[bot] 2bacbb796b fix(deps): update dependency tsx to v4.20.3 2025-07-03 00:17:08 +00:00
renovate[bot] aa0ed6434a fix(deps): update dependency better-sqlite3 to v12.2.0 2025-07-03 00:16:25 +00:00
renovate[bot] 5b2215d646 fix(deps): update dependency @inlang/paraglide-js to v2.2.0 2025-07-03 00:15:46 +00:00
renovate[bot] 0e760e25f2 chore(deps): update dependency eslint-plugin-svelte to v3.10.1 2025-07-03 00:10:18 +00:00
renovate[bot] acbb85b409 chore(deps): update dependency electron to v37.2.0 2025-07-03 00:09:29 +00:00
renovate[bot] ea1d4b97ad chore(deps): update dependency @sveltejs/kit to v2.22.2 2025-07-03 00:08:38 +00:00
renovate[bot] a81839c13f chore(deps): update tailwindcss monorepo to v4.1.11 2025-07-03 00:06:48 +00:00
renovate[bot] b9d4668d4d chore(deps): update nx monorepo to v21.2.2 2025-07-03 00:05:55 +00:00
renovate[bot] 42b27f5965 chore(deps): update dependency svelte-check to v4.2.2 2025-07-03 00:05:03 +00:00
renovate[bot] 9cc8222b1c chore(deps): update dependency lint-staged to v16.1.2 2025-07-03 00:04:08 +00:00
renovate[bot] e8479338df chore(deps): update dependency dotenv to v17.0.1 2025-07-03 00:03:19 +00:00
renovate[bot] fa9e6c9fc0 chore(deps): update dependency @types/mime-types to v3.0.1 2025-07-03 00:02:32 +00:00
renovate[bot] 5366173b52 chore(deps): update dependency @eslint/compat to v1.3.1 2025-07-03 00:01:45 +00:00
Elian Doran 63520c55b3 Merge branch 'main' into feat/note-map-filter 2025-07-02 23:37:56 +03:00
Elian Doran 86f6d9b14a feat(export/zip): handle empty title for archive 2025-07-02 22:28:19 +03:00
Elian Doran 5270cf6284 feat(export/zip): handle empty notes for subtree 2025-07-02 22:28:03 +03:00
Elian Doran 4f46d81e1b fix(export/zip): missing note meta for empty file (closes #6146) 2025-07-02 21:59:58 +03:00
Elian Doran 294a2e6fdb feat(export/zip): improve error handling 2025-07-02 21:56:22 +03:00
Elian Doran b20a8bc90b chore(backend_api): document week note behaviour 2025-07-02 21:41:12 +03:00
Elian Doran 68bdd1336f Merge pull request #6168 from TriliumNext/renovate/node-22.x
chore(deps): update dependency @types/node to v22.16.0
2025-07-02 20:35:02 +03:00
renovate[bot] e62ccd932d chore(deps): update dependency @types/node to v22.16.0 2025-07-02 13:54:50 +00:00
Elian Doran d6c188df6e Merge pull request #6170 from TriliumNext/renovate/i18next-25.x 2025-07-02 16:53:00 +03:00
Elian Doran 004000b5d2 Merge pull request #6171 from TriliumNext/renovate/eslint-monorepo 2025-07-02 16:52:25 +03:00
renovate[bot] 633c8a3444 fix(deps): update eslint monorepo to v9.30.1 2025-07-02 08:28:18 +00:00
renovate[bot] 2f59a20b6b fix(deps): update dependency i18next to v25.3.0 2025-07-02 08:27:21 +00:00
Elian Doran 593c435f75 Merge pull request #6172 from TriliumNext/renovate/multer-2.x
chore(deps): update dependency @types/multer to v2
2025-07-02 11:24:18 +03:00
Elian Doran 20ec45be57 Merge pull request #6169 from TriliumNext/renovate/globals-16.x
fix(deps): update dependency globals to v16.3.0
2025-07-02 11:23:17 +03:00
Elian Doran d2a0e12409 Merge pull request #6167 from TriliumNext/renovate/fullcalendar-monorepo
fix(deps): update fullcalendar monorepo to v6.1.18
2025-07-02 11:22:51 +03:00
Elian Doran 33eebe117b Merge pull request #6166 from TriliumNext/renovate/eslint-linter-browserify-9.x
fix(deps): update dependency eslint-linter-browserify to v9.30.1
2025-07-02 11:22:29 +03:00
Elian Doran ef0cfc2e7c Merge pull request #6165 from TriliumNext/renovate/dotenv-17.x
chore(deps): update dependency dotenv to v17.0.1
2025-07-02 11:22:07 +03:00
Elian Doran b6e17ae543 Merge pull request #6164 from TriliumNext/renovate/playwright-monorepo
chore(deps): update dependency @playwright/test to v1.53.2
2025-07-02 11:21:51 +03:00
Elian Doran 8a33e2be89 Merge pull request #6163 from TriliumNext/renovate/anthropic-ai-sdk-0.x
chore(deps): update dependency @anthropic-ai/sdk to v0.55.1
2025-07-02 11:21:36 +03:00
renovate[bot] 5f91097987 chore(deps): update dependency @types/multer to v2 2025-07-02 02:56:29 +00:00
renovate[bot] 0fd4f02951 fix(deps): update dependency globals to v16.3.0 2025-07-02 02:52:17 +00:00
renovate[bot] 106e78ed62 fix(deps): update fullcalendar monorepo to v6.1.18 2025-07-02 02:47:34 +00:00
renovate[bot] 8855868b27 fix(deps): update dependency eslint-linter-browserify to v9.30.1 2025-07-02 02:46:36 +00:00
renovate[bot] bfc3e8a907 chore(deps): update dependency dotenv to v17.0.1 2025-07-02 02:44:43 +00:00
renovate[bot] 154371e052 chore(deps): update dependency @playwright/test to v1.53.2 2025-07-02 02:43:47 +00:00
renovate[bot] ab4a4d3d72 chore(deps): update dependency @anthropic-ai/sdk to v0.55.1 2025-07-02 02:42:29 +00:00
Elian Doran 5a4de02db7 Merge pull request #6105 from TriliumNext/renovate/ckeditor-monorepo
chore(deps): update dependency @ckeditor/ckeditor5-package-tools to v4.0.1
2025-07-01 22:22:05 +03:00
renovate[bot] 43cbc8c6e8 chore(deps): update dependency @ckeditor/ckeditor5-package-tools to v4.0.1 2025-07-01 19:09:31 +00:00
Elian Doran 5938aa7b50 Merge pull request #6110 from TriliumNext/renovate/openai-5.x
chore(deps): update dependency openai to v5.8.2
2025-07-01 22:06:06 +03:00
Elian Doran a49252b2f5 Merge pull request #6121 from TriliumNext/renovate/codemirror
fix(deps): update dependency @codemirror/view to v6.38.0
2025-07-01 22:05:55 +03:00
Elian Doran 0be885d9bf Merge pull request #6123 from TriliumNext/renovate/eslint-linter-browserify-9.x
fix(deps): update dependency eslint-linter-browserify to v9.30.0
2025-07-01 22:05:14 +03:00
renovate[bot] ae1e8353f2 fix(deps): update dependency eslint-linter-browserify to v9.30.0 2025-07-01 18:50:46 +00:00
renovate[bot] 98fe88581f fix(deps): update dependency @codemirror/view to v6.38.0 2025-07-01 18:49:50 +00:00
renovate[bot] d66475576f chore(deps): update dependency openai to v5.8.2 2025-07-01 18:48:54 +00:00
Elian Doran 65ff7be776 Merge pull request #6093 from TriliumNext/renovate/vite-7.x
chore(deps): update dependency vite to v7
2025-07-01 21:31:57 +03:00
Elian Doran 190b079494 fix(website): typecheck dependency not well set 2025-07-01 21:30:59 +03:00
renovate[bot] b020a30bd4 chore(deps): update dependency vite to v7 2025-07-01 18:08:46 +00:00
Elian Doran 81f8453c38 fix(client): type error due to use of intersection 2025-07-01 21:01:54 +03:00
Elian Doran 533e3cf42d Merge pull request #6125 from TriliumNext/renovate/eslint-monorepo
fix(deps): update eslint monorepo to v9.30.0
2025-07-01 20:55:26 +03:00
Elian Doran 69ee73492d Merge pull request #6142 from TriliumNext/renovate/rollup-plugin-webpack-stats-2.x
chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.0
2025-07-01 20:55:16 +03:00
renovate[bot] 4a902d04b2 fix(deps): update eslint monorepo to v9.30.0 2025-07-01 17:18:00 +00:00
renovate[bot] 2e48e316c2 chore(deps): update dependency rollup-plugin-webpack-stats to v2.1.0 2025-07-01 17:16:31 +00:00
Elian Doran bbe5dddb83 Merge pull request #6131 from TriliumNext/renovate/node-22.x
chore(deps): update dependency @types/node to v22.15.34
2025-07-01 20:14:23 +03:00
Elian Doran 7c943fe4ac chore(view/table): leftover files 2025-07-01 12:10:01 +03:00
Elian Doran 2cbb49681a fix(view/table): most type errors 2025-07-01 12:09:13 +03:00
Elian Doran 84db4ed57c docs(release): fix link 2025-07-01 11:56:05 +03:00
Elian Doran e155642ce4 Merge remote-tracking branch 'origin/main' into feature/table_view 2025-07-01 11:55:45 +03:00
Elian Doran 87c4df60d3 Merge pull request #6126 from TriliumNext/renovate/major-ckeditor5-config-packages
chore(deps): update ckeditor5 config packages to v11 (major)
2025-07-01 11:45:01 +03:00
Elian Doran ff412835e4 Merge pull request #6119 from TriliumNext/renovate/webdriverio-monorepo
chore(deps): update dependency webdriverio to v9.16.2
2025-07-01 11:44:39 +03:00
Elian Doran ad15828157 Merge pull request #6111 from TriliumNext/renovate/svelte-5.x-lockfile
chore(deps): update dependency svelte to v5.34.9
2025-07-01 11:43:34 +03:00
renovate[bot] b2fc7f934e chore(deps): update dependency @types/node to v22.15.34 2025-07-01 08:43:30 +00:00
Elian Doran 2fac4d91d6 Merge pull request #6112 from TriliumNext/renovate/svelte-check-4.x-lockfile
chore(deps): update dependency svelte-check to v4.2.2
2025-07-01 11:43:22 +03:00
Elian Doran 125cd96354 Merge pull request #6113 from TriliumNext/renovate/typedoc-0.x
chore(deps): update dependency typedoc to v0.28.7
2025-07-01 11:43:03 +03:00
Elian Doran af02e6b714 Merge pull request #6116 from TriliumNext/renovate/dotenv-16.x-lockfile
chore(deps): update dependency dotenv to v16.6.1
2025-07-01 11:42:55 +03:00
Elian Doran 0c87b25244 Merge pull request #6117 from TriliumNext/renovate/eslint-plugin-svelte-3.x-lockfile
chore(deps): update dependency eslint-plugin-svelte to v3.10.1
2025-07-01 11:42:41 +03:00
Elian Doran e87ada6e79 Merge pull request #6118 from TriliumNext/renovate/stylelint-16.x-lockfile
chore(deps): update dependency stylelint to v16.21.0
2025-07-01 11:42:33 +03:00
Elian Doran 282c8e58bd Merge pull request #6120 from TriliumNext/renovate/typescript-eslint-monorepo
chore(deps): update typescript-eslint monorepo to v8.35.1
2025-07-01 11:42:17 +03:00
Elian Doran 475b66b115 Merge pull request #6122 from TriliumNext/renovate/better-sqlite3-12.x-lockfile
fix(deps): update dependency better-sqlite3 to v12.2.0
2025-07-01 11:42:02 +03:00
Elian Doran 5bb971e61a Merge pull request #6124 from TriliumNext/renovate/tsx-4.x-lockfile
fix(deps): update dependency tsx to v4.20.3
2025-07-01 11:41:36 +03:00
Elian Doran ebad9ba723 Merge pull request #6127 from TriliumNext/renovate/dotenv-17.x
chore(deps): update dependency dotenv to v17
2025-07-01 11:41:20 +03:00
Elian Doran 6ece2a839e Merge pull request #6128 from TriliumNext/renovate/marked-16.x
fix(deps): update dependency marked to v16
2025-07-01 11:40:55 +03:00
Elian Doran 8d6527fb75 Merge pull request #6132 from TriliumNext/renovate/ws-8.x
chore(deps): update dependency ws to v8.18.3
2025-07-01 11:40:27 +03:00
Elian Doran 6bfff38182 Merge pull request #6115 from TriliumNext/renovate/sveltejs-kit-2.x-lockfile
chore(deps): update dependency @sveltejs/kit to v2.22.2
2025-07-01 11:39:54 +03:00
Elian Doran 9e446717fa Merge pull request #6114 from TriliumNext/renovate/tailwindcss-monorepo
chore(deps): update tailwindcss monorepo to v4.1.11
2025-07-01 11:39:42 +03:00
Elian Doran 408b48f606 Merge pull request #6133 from TriliumNext/renovate/better-sqlite3-12.x
fix(deps): update dependency better-sqlite3 to v12.2.0
2025-07-01 11:39:32 +03:00
renovate[bot] 8d077ad46d chore(deps): update dependency dotenv to v17 2025-07-01 08:39:18 +00:00
Elian Doran db72465e0b Merge pull request #6140 from TriliumNext/renovate/mind-elixir-4.x
fix(deps): update dependency mind-elixir to v4.6.2
2025-07-01 11:38:22 +03:00
Elian Doran ba9f5e1688 Merge pull request #6141 from TriliumNext/renovate/eslint-stylistic-monorepo
chore(deps): update dependency @stylistic/eslint-plugin to v5.1.0
2025-07-01 11:37:57 +03:00
Elian Doran caf40cd272 Merge pull request #6109 from TriliumNext/renovate/lint-staged-16.x-lockfile
chore(deps): update dependency lint-staged to v16.1.2
2025-07-01 11:37:02 +03:00
Elian Doran 3edccd224a Merge pull request #6107 from TriliumNext/renovate/mime-types-3.x-lockfile
chore(deps): update dependency @types/mime-types to v3.0.1
2025-07-01 11:36:28 +03:00
Elian Doran f48931a969 Merge pull request #6106 from TriliumNext/renovate/eslint-compat-1.x-lockfile
chore(deps): update dependency @eslint/compat to v1.3.1
2025-07-01 11:36:12 +03:00
Elian Doran 84f23aa997 Merge pull request #6092 from TriliumNext/renovate/electron-37.x
chore(deps): update dependency electron to v37
2025-07-01 11:35:46 +03:00
Elian Doran 1965da6a85 docs(release): mention docker image changing 2025-07-01 10:15:10 +03:00
renovate[bot] 441ae3e25b chore(deps): update typescript-eslint monorepo to v8.35.1 2025-06-30 18:35:19 +00:00
renovate[bot] 7f612711a0 chore(deps): update dependency typedoc to v0.28.7 2025-06-30 04:54:36 +00:00
renovate[bot] 92eb4aa822 chore(deps): update dependency @stylistic/eslint-plugin to v5.1.0 2025-06-30 01:36:17 +00:00
renovate[bot] 08ec522ae7 fix(deps): update dependency mind-elixir to v4.6.2 2025-06-30 01:36:11 +00:00
Elian Doran c5cc1fcc1e feat(views/table): introduce hiding of columns 2025-06-29 22:26:25 +03:00
Elian Doran cedf91ea1a chore(views/table): reintroduce column reordering 2025-06-29 16:56:34 +03:00
Elian Doran 51b462f043 chore(views/table): bring back restore state 2025-06-29 16:16:15 +03:00
Elian Doran 727eeb6c74 chore(views/table): bring back persistence 2025-06-29 16:08:27 +03:00
Elian Doran a114fba062 chore(views/table): set up frozen columns 2025-06-29 15:11:09 +03:00
Elian Doran cf322b5c2a chore(views/table): back to bootstrap5 theme 2025-06-29 10:09:39 +03:00
renovate[bot] 92116f1671 fix(deps): update dependency better-sqlite3 to v12.2.0 2025-06-29 00:37:40 +00:00
renovate[bot] bc479248d7 chore(deps): update dependency ws to v8.18.3 2025-06-29 00:35:22 +00:00
Elian Doran 8ee12f2950 chore(views/table): bring back resizing columns 2025-06-28 23:50:54 +03:00
Elian Doran dcea4c30ef chore(views/table): improve editing for date types 2025-06-28 23:33:52 +03:00
Elian Doran e7ca56e061 chore(views/table): support more data types 2025-06-28 23:29:31 +03:00
Elian Doran 09b800b9ad chore(views/table): bring back editing attributes 2025-06-28 23:23:29 +03:00
Elian Doran 9a6a8580de chore(views/table): bring back editing title 2025-06-28 23:19:54 +03:00
Elian Doran a31ac17792 chore(views/table): set row ID as index 2025-06-28 22:49:40 +03:00
Elian Doran 0e27cd0801 feat(views/table): add row number 2025-06-28 22:47:49 +03:00
Elian Doran bc36676fa1 chore(views/table): disable sorting for note action button 2025-06-28 17:45:11 +03:00
Elian Doran 3d2db23f33 fix(views/table): use a more stable loading mechanism 2025-06-28 17:24:18 +03:00
Elian Doran 56d366a286 feat(views/table): add column to open note 2025-06-28 17:23:42 +03:00
Elian Doran 4a26f30d65 feat(views/table): render note icon 2025-06-28 17:07:11 +03:00
Elian Doran 8e51469de5 chore(views/table): re-enable sorting 2025-06-28 16:56:36 +03:00
Elian Doran 50ebcd552c fix(views/table): error when adding a new column 2025-06-28 16:51:24 +03:00
Elian Doran ada39cd3c7 fix(views/table): error when adding a new row 2025-06-28 16:48:01 +03:00
Elian Doran b2d20af51a fix(views/table): refreshing of columns 2025-06-28 16:39:24 +03:00
Elian Doran f528fa25d1 feat(views/table): switch to bootstrap theme 2025-06-28 12:51:19 +03:00
Elian Doran e09a7fb6e0 chore(views/table): reintroduce rows 2025-06-28 12:24:40 +03:00
Elian Doran 30f7939616 chore(views/table): reintroduce column definitions 2025-06-28 12:18:24 +03:00
Elian Doran 16b9375b9d chore(views/table): add types 2025-06-28 12:18:17 +03:00
Elian Doran 4ef93569a1 refactor(views/table): start switching to tabulator 2025-06-28 12:00:50 +03:00
renovate[bot] 1ce2aaeaf1 fix(deps): update dependency marked to v16 2025-06-28 02:47:26 +00:00
renovate[bot] 6bfe8dfcf0 chore(deps): update ckeditor5 config packages to v11 2025-06-28 02:45:32 +00:00
renovate[bot] 8d8f4795e2 fix(deps): update dependency tsx to v4.20.3 2025-06-28 02:42:36 +00:00
renovate[bot] 6f6d06377b fix(deps): update dependency better-sqlite3 to v12.1.1 2025-06-28 02:40:39 +00:00
renovate[bot] f22823fcf6 chore(deps): update dependency webdriverio to v9.16.2 2025-06-28 02:37:40 +00:00
renovate[bot] 93ce57ee1a chore(deps): update dependency stylelint to v16.21.0 2025-06-28 02:36:39 +00:00
renovate[bot] 97dd747252 chore(deps): update dependency eslint-plugin-svelte to v3.10.1 2025-06-28 02:35:40 +00:00
renovate[bot] bc8c136458 chore(deps): update dependency dotenv to v16.6.1 2025-06-28 02:34:42 +00:00
renovate[bot] 0774252dc1 chore(deps): update dependency @sveltejs/kit to v2.22.2 2025-06-28 02:33:38 +00:00
renovate[bot] ae30ae4be6 chore(deps): update tailwindcss monorepo to v4.1.11 2025-06-28 02:32:27 +00:00
renovate[bot] a2b8935763 chore(deps): update dependency svelte-check to v4.2.2 2025-06-28 02:31:19 +00:00
renovate[bot] 703efb74d3 chore(deps): update dependency svelte to v5.34.8 2025-06-28 02:30:18 +00:00
renovate[bot] b2c6062e9a chore(deps): update dependency lint-staged to v16.1.2 2025-06-28 02:28:22 +00:00
renovate[bot] c9e7e461b1 chore(deps): update dependency @types/mime-types to v3.0.1 2025-06-28 02:26:47 +00:00
renovate[bot] 6aaddfc5a4 chore(deps): update dependency @eslint/compat to v1.3.1 2025-06-28 02:25:51 +00:00
Elian Doran 7f2c41940d feat(views/table): add basic row creation mechanism 2025-06-28 00:07:14 +03:00
Elian Doran d31ba39a91 feat(views/table): basic dark mode support 2025-06-27 23:40:00 +03:00
Elian Doran c058673e33 feat(views/table): smooth column update 2025-06-27 23:01:15 +03:00
Elian Doran 44ce6a5169 feat(views/table): refresh on attribute change 2025-06-27 22:50:27 +03:00
Elian Doran 0fb0be4ffc feat(views/table): actually add attributes 2025-06-27 22:43:29 +03:00
Kieran e70ba00929 docs(map): document relation filtering 2025-06-27 20:25:09 +01:00
Elian Doran fe1dbb4cbf feat(views/table): display a dialog to add a new column 2025-06-27 22:19:09 +03:00
Kieran 31df2341c3 feat(map): add mapIncludeRelation and mapExcludeRelation to builtin_attributes 2025-06-27 20:18:28 +01:00
Kieran 9d99da14e1 feat(map): add mapIncludeRelation and mapExcludeRelation to include only or exclude specific relation types 2025-06-27 20:15:59 +01:00
Elian Doran f8e10f36db refactor(note_list): use object for constructor arg 2025-06-27 21:51:38 +03:00
Elian Doran bb0f384a39 feat(views/table): disable drag if sorted 2025-06-27 20:30:36 +03:00
Elian Doran 6a0b24f032 chore(views/table): remove logs 2025-06-27 20:08:41 +03:00
Elian Doran 80d5536503 feat(views/table): basic drag support 2025-06-27 19:53:40 +03:00
Elian Doran 9dcd79bd94 feat(views/table): add debouncing 2025-06-27 17:58:25 +03:00
Elian Doran c5020b8884 refactor(views/table): move table view into its own folder 2025-06-27 17:44:29 +03:00
Elian Doran 0b74de275c refactor(views/table): integrate parser into data 2025-06-27 17:43:19 +03:00
Elian Doran e66aef17df refactor(views/table): merge storage into table view 2025-06-27 17:40:56 +03:00
Elian Doran 19eff5e6d6 refactor(views/table): merge renderer into table view 2025-06-27 17:39:57 +03:00
Elian Doran 88b4fc73de chore(views/table): remove placeholder text 2025-06-27 17:22:47 +03:00
Elian Doran 70694542eb feat(views/table): allow in search 2025-06-27 17:18:52 +03:00
Elian Doran 360e5e3102 Merge remote-tracking branch 'origin/main' into feature/table_view 2025-06-27 17:08:23 +03:00
renovate[bot] 6e89a232e6 chore(deps): update dependency electron to v37 2025-06-27 06:48:15 +00:00
Elian Doran ecd3b7039f feat(book/table): add template 2025-06-25 19:31:25 +03:00
Elian Doran 4a22e3d2d4 feat(book/table): hide promoted attributes 2025-06-25 19:25:01 +03:00
Elian Doran dcb4ebe5d9 feat(book/table): display even if empty 2025-06-25 18:31:45 +03:00
Elian Doran dd379bf18d refactor(book/table): fix some lack of generics 2025-06-25 18:30:44 +03:00
Elian Doran c9b556160f feat(book/table): support changing note title 2025-06-25 17:56:47 +03:00
Elian Doran 168e224d3e refactor(book/table): make clear what kind of attribute is being changed 2025-06-25 17:54:00 +03:00
Elian Doran 9e57c14130 feat(attachments): add pretty formatting to JSON 2025-06-25 17:45:11 +03:00
Elian Doran 9c137a1c48 feat(book/table): display attachment JSON 2025-06-25 17:43:58 +03:00
Elian Doran ccb9b7e5fb feat(book/table): store hidden columns 2025-06-25 16:18:34 +03:00
Elian Doran c7b16cd043 feat(book/table): allow show/hide columns 2025-06-25 13:52:53 +03:00
Elian Doran 7e20e41521 feat(book/table): allow editing cell values 2025-06-25 13:06:38 +03:00
Elian Doran 66761a69d3 refactor(book/table): clean up 2025-06-25 12:10:08 +03:00
Elian Doran fb32d26479 feat(book/table): support boolean type 2025-06-25 12:05:10 +03:00
Elian Doran b6398fdb5d refactor(book/table): extract gathering definitions 2025-06-25 12:03:17 +03:00
Elian Doran d9443527ee feat(book/table): support date type 2025-06-25 11:56:30 +03:00
Elian Doran 7c175da9f1 chore(book/table): ignore multi attributes 2025-06-25 11:45:46 +03:00
Elian Doran 05aa087851 feat(book/table): support basic text columns 2025-06-25 11:23:34 +03:00
Elian Doran 592e968f9f feat(book/table): display note titles 2025-06-25 11:06:49 +03:00
Elian Doran 894a26cc67 feat(book/table): set up sample grid 2025-06-25 10:49:33 +03:00
Elian Doran 1b5dd4638d chore(book/table): install ag-grid 2025-06-25 10:40:11 +03:00
Elian Doran a19186c508 feat(book/table): set full height 2025-06-25 10:40:04 +03:00
Elian Doran 5450bdeae9 feat(book/table): hide no children warning 2025-06-25 10:34:03 +03:00
Elian Doran fcd71957ff feat(book/table): create new view type 2025-06-25 10:31:41 +03:00
490 changed files with 119834 additions and 21221 deletions
+2
View File
@@ -2,3 +2,5 @@
github: [eliandoran]
custom: ["https://paypal.me/eliandoran"]
liberapay: ElianDoran
buy_me_a_coffee: eliandoran
+40
View File
@@ -0,0 +1,40 @@
---
applyTo: '**'
---
// This file is automatically generated by Nx Console
You are in an nx workspace using Nx 21.3.9 and pnpm as the package manager.
You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user:
# General Guidelines
- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture
- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration
- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors
- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool
# Generation Guidelines
If the user wants to generate something, use the following flow:
- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable
- get the available generators using the 'nx_generators' tool
- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them
- get generator details using the 'nx_generator_schema' tool
- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure
- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic
- open the generator UI using the 'nx_open_generate_ui' tool
- wait for the user to finish the generator
- read the generator log file using the 'nx_read_generator_log' tool
- use the information provided in the log file to answer the user's question or continue with what they were doing
# Running Tasks Guidelines
If the user wants help with tasks or commands (which include keywords like "test", "build", "lint", or other similar actions), use the following flow:
- Use the 'nx_current_running_tasks_details' tool to get the list of tasks (this can include tasks that were completed, stopped or failed).
- If there are any tasks, ask the user if they would like help with a specific task then use the 'nx_current_running_task_output' tool to get the terminal output for that task/command
- Use the terminal output from 'nx_current_running_task_output' to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
- If the user would like to rerun the task or command, always use `nx run <taskId>` to rerun in the terminal. This will ensure that the task will run in the nx context and will be run the same way it originally executed
- If the task was marked as "continuous" do not offer to rerun the task. This task is already running and the user can see the output in the terminal. You can use 'nx_current_running_task_output' to get the output of the task to verify the output.
+17
View File
@@ -0,0 +1,17 @@
name: Checks
on:
push:
pull_request_target:
types: [synchronize]
jobs:
main:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Check if PRs have conflicts
uses: eps1lon/actions-label-merge-conflict@v3
with:
dirtyLabel: "merge-conflicts"
repoToken: "${{ secrets.MERGE_CONFLICT_LABEL_PAT }}"
+2 -2
View File
@@ -1,2 +1,2 @@
Adam Zivner <adam.zivner@gmail.com>
Adam Zivner <zadam.apps@gmail.com>
zadam <adam.zivner@gmail.com>
zadam <zadam.apps@gmail.com>
+1 -1
View File
@@ -1 +1 @@
22.17.0
22.18.0
+8
View File
@@ -0,0 +1,8 @@
{
"servers": {
"nx-mcp": {
"type": "http",
"url": "http://localhost:9461/mcp"
}
}
}
+9 -1
View File
@@ -28,5 +28,13 @@
"typescript.validate.enable": true,
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
"typescript.enablePromptUseWorkspaceTsdk": true,
"search.exclude": {
"**/node_modules": true,
"docs/**/*.html": true,
"docs/**/*.png": true,
"apps/server/src/assets/doc_notes/**": true,
"apps/edit-docs/demo/**": true
},
"nxConsole.generateAiAgentRules": true
}
+161
View File
@@ -0,0 +1,161 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Overview
Trilium Notes is a hierarchical note-taking application with advanced features like synchronization, scripting, and rich text editing. It's built as a TypeScript monorepo using NX, with multiple applications and shared packages.
## Development Commands
### Setup
- `pnpm install` - Install all dependencies
- `corepack enable` - Enable pnpm if not available
### Running Applications
- `pnpm run server:start` - Start development server (http://localhost:8080)
- `pnpm nx run server:serve` - Alternative server start command
- `pnpm nx run desktop:serve` - Run desktop Electron app
- `pnpm run server:start-prod` - Run server in production mode
### Building
- `pnpm nx build <project>` - Build specific project (server, client, desktop, etc.)
- `pnpm run client:build` - Build client application
- `pnpm run server:build` - Build server application
- `pnpm run electron:build` - Build desktop application
### Testing
- `pnpm test:all` - Run all tests (parallel + sequential)
- `pnpm test:parallel` - Run tests that can run in parallel
- `pnpm test:sequential` - Run tests that must run sequentially (server, ckeditor5-mermaid, ckeditor5-math)
- `pnpm nx test <project>` - Run tests for specific project
- `pnpm coverage` - Generate coverage reports
### Linting & Type Checking
- `pnpm nx run <project>:lint` - Lint specific project
- `pnpm nx run <project>:typecheck` - Type check specific project
## Architecture Overview
### Monorepo Structure
- **apps/**: Runnable applications
- `client/` - Frontend application (shared by server and desktop)
- `server/` - Node.js server with web interface
- `desktop/` - Electron desktop application
- `web-clipper/` - Browser extension for saving web content
- Additional tools: `db-compare`, `dump-db`, `edit-docs`
- **packages/**: Shared libraries
- `commons/` - Shared interfaces and utilities
- `ckeditor5/` - Custom rich text editor with Trilium-specific plugins
- `codemirror/` - Code editor customizations
- `highlightjs/` - Syntax highlighting
- Custom CKEditor plugins: `ckeditor5-admonition`, `ckeditor5-footnotes`, `ckeditor5-math`, `ckeditor5-mermaid`
### Core Architecture Patterns
#### Three-Layer Cache System
- **Becca** (Backend Cache): Server-side entity cache (`apps/server/src/becca/`)
- **Froca** (Frontend Cache): Client-side mirror of backend data (`apps/client/src/services/froca.ts`)
- **Shaca** (Share Cache): Optimized cache for shared/published notes (`apps/server/src/share/`)
#### Entity System
Core entities are defined in `apps/server/src/becca/entities/`:
- `BNote` - Notes with content and metadata
- `BBranch` - Hierarchical relationships between notes (allows multiple parents)
- `BAttribute` - Key-value metadata attached to notes
- `BRevision` - Note version history
- `BOption` - Application configuration
#### Widget-Based UI
Frontend uses a widget system (`apps/client/src/widgets/`):
- `BasicWidget` - Base class for all UI components
- `NoteContextAwareWidget` - Widgets that respond to note changes
- `RightPanelWidget` - Widgets displayed in the right panel
- Type-specific widgets in `type_widgets/` directory
#### API Architecture
- **Internal API**: REST endpoints in `apps/server/src/routes/api/`
- **ETAPI**: External API for third-party integrations (`apps/server/src/etapi/`)
- **WebSocket**: Real-time synchronization (`apps/server/src/services/ws.ts`)
### Key Files for Understanding Architecture
1. **Application Entry Points**:
- `apps/server/src/main.ts` - Server startup
- `apps/client/src/desktop.ts` - Client initialization
2. **Core Services**:
- `apps/server/src/becca/becca.ts` - Backend data management
- `apps/client/src/services/froca.ts` - Frontend data synchronization
- `apps/server/src/services/backend_script_api.ts` - Scripting API
3. **Database Schema**:
- `apps/server/src/assets/db/schema.sql` - Core database structure
4. **Configuration**:
- `nx.json` - NX workspace configuration
- `package.json` - Project dependencies and scripts
## Note Types and Features
Trilium supports multiple note types, each with specialized widgets:
- **Text**: Rich text with CKEditor5 (markdown import/export)
- **Code**: Syntax-highlighted code editing with CodeMirror
- **File**: Binary file attachments
- **Image**: Image display with editing capabilities
- **Canvas**: Drawing/diagramming with Excalidraw
- **Mermaid**: Diagram generation
- **Relation Map**: Visual note relationship mapping
- **Web View**: Embedded web pages
- **Doc/Book**: Hierarchical documentation structure
## Development Guidelines
### Testing Strategy
- Server tests run sequentially due to shared database
- Client tests can run in parallel
- E2E tests use Playwright for both server and desktop apps
- Build validation tests check artifact integrity
### Scripting System
Trilium provides powerful user scripting capabilities:
- Frontend scripts run in browser context
- Backend scripts run in Node.js context with full API access
- Script API documentation available in `docs/Script API/`
### Internationalization
- Translation files in `apps/client/src/translations/`
- Supported languages: English, German, Spanish, French, Romanian, Chinese
### Security Considerations
- Per-note encryption with granular protected sessions
- CSRF protection for API endpoints
- OpenID and TOTP authentication support
- Sanitization of user-generated content
## Common Development Tasks
### Adding New Note Types
1. Create widget in `apps/client/src/widgets/type_widgets/`
2. Register in `apps/client/src/services/note_types.ts`
3. Add backend handling in `apps/server/src/services/notes.ts`
### Extending Search
- Search expressions handled in `apps/server/src/services/search/`
- Add new search operators in search context files
### Custom CKEditor Plugins
- Create new package in `packages/` following existing plugin structure
- Register in `packages/ckeditor5/src/plugins.ts`
### Database Migrations
- Add migration scripts in `apps/server/src/migrations/`
- Update schema in `apps/server/src/assets/db/schema.sql`
## Build System Notes
- Uses NX for monorepo management with build caching
- Vite for fast development builds
- ESBuild for production optimization
- pnpm workspaces for dependency management
- Docker support with multi-stage builds
+18 -10
View File
@@ -1,9 +1,9 @@
# Trilium Notes
![GitHub Sponsors](https://img.shields.io/github/sponsors/eliandoran?style=flat-square)
![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes?style=flat-square)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total?style=flat-square)
[![RelativeCI](https://badges.relative-ci.com/badges/Di5q7dz9daNDZ9UXi0Bp?branch=develop&style=flat-square)](https://app.relative-ci.com/projects/Di5q7dz9daNDZ9UXi0Bp)
![GitHub Sponsors](https://img.shields.io/github/sponsors/eliandoran) ![LiberaPay patrons](https://img.shields.io/liberapay/patrons/ElianDoran)
![Docker Pulls](https://img.shields.io/docker/pulls/triliumnext/notes)
![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/triliumnext/notes/total)
[![RelativeCI](https://badges.relative-ci.com/badges/Di5q7dz9daNDZ9UXi0Bp?branch=develop)](https://app.relative-ci.com/projects/Di5q7dz9daNDZ9UXi0Bp) [![Translation status](https://hosted.weblate.org/widget/trilium/svg-badge.svg)](https://hosted.weblate.org/engage/trilium/)
[English](./README.md) | [Chinese](./docs/README-ZH_CN.md) | [Russian](./docs/README.ru.md) | [Japanese](./docs/README.ja.md) | [Italian](./docs/README.it.md) | [Spanish](./docs/README.es.md)
@@ -115,12 +115,20 @@ To install TriliumNext on your own server (including via Docker from [Dockerhub]
## 💻 Contribute
### Translations
If you are a native speaker, help us translate Trilium by heading over to our [Weblate page](https://hosted.weblate.org/engage/trilium/).
Here's the language coverage we have so far:
[![Translation status](https://hosted.weblate.org/widget/trilium/multi-auto.svg)](https://hosted.weblate.org/engage/trilium/)
### 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
git clone https://github.com/TriliumNext/Trilium.git
cd Trilium
pnpm install
pnpm run server:start
```
@@ -129,8 +137,8 @@ pnpm run server:start
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
git clone https://github.com/TriliumNext/Trilium.git
cd Trilium
pnpm install
pnpm nx run edit-docs:edit-docs
```
@@ -138,8 +146,8 @@ 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
git clone https://github.com/TriliumNext/Trilium.git
cd Trilium
pnpm install
pnpm nx --project=desktop electron-forge:make -- --arch=x64 --platform=win32
```
+5 -5
View File
@@ -35,13 +35,13 @@
"chore:generate-openapi": "tsx bin/generate-openapi.js"
},
"devDependencies": {
"@playwright/test": "1.53.1",
"@stylistic/eslint-plugin": "5.0.0",
"@playwright/test": "1.54.2",
"@stylistic/eslint-plugin": "5.2.2",
"@types/express": "5.0.3",
"@types/node": "22.15.33",
"@types/node": "22.17.0",
"@types/yargs": "17.0.33",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.29.0",
"eslint": "9.32.0",
"eslint-plugin-simple-import-sort": "12.1.1",
"esm": "3.2.25",
"jsdoc": "4.0.4",
@@ -49,7 +49,7 @@
"rcedit": "4.0.1",
"rimraf": "6.0.1",
"tslib": "2.8.1",
"typedoc": "0.28.5",
"typedoc": "0.28.9",
"typedoc-plugin-missing-exports": "4.0.0"
},
"optionalDependencies": {
+2 -1
View File
@@ -1,4 +1,5 @@
# 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
VITE_CKEDITOR_KEY=eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE3NTc3MjE1OTksImp0aSI6ImFiN2E0NjZmLWJlZGMtNDNiYy1iMzU4LTk0NGQ0YWJhY2I3ZiIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsic2giLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwiZmVhdHVyZXMiOlsiRFJVUCIsIkNNVCIsIkRPIiwiRlAiLCJTQyIsIlRPQyIsIlRQTCIsIlBPRSIsIkNDIiwiTUYiLCJTRUUiLCJFQ0giLCJFSVMiXSwidmMiOiI1MzlkOWY5YyJ9.2rvKPql4hmukyXhEtWPZ8MLxKvzPIwzCdykO653g7IxRRZy2QJpeRszElZx9DakKYZKXekVRAwQKgHxwkgbE_w
VITE_CKEDITOR_ENABLE_INSPECTOR=false
+22 -20
View File
@@ -1,6 +1,6 @@
{
"name": "@triliumnext/client",
"version": "0.96.0",
"version": "0.97.2",
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
"private": true,
"license": "AGPL-3.0-only",
@@ -10,16 +10,17 @@
"url": "https://github.com/TriliumNext/Notes"
},
"dependencies": {
"@eslint/js": "9.29.0",
"@eslint/js": "9.32.0",
"@excalidraw/excalidraw": "0.18.0",
"@fullcalendar/core": "6.1.17",
"@fullcalendar/daygrid": "6.1.17",
"@fullcalendar/interaction": "6.1.17",
"@fullcalendar/list": "6.1.17",
"@fullcalendar/multimonth": "6.1.17",
"@fullcalendar/timegrid": "6.1.17",
"@fullcalendar/core": "6.1.18",
"@fullcalendar/daygrid": "6.1.18",
"@fullcalendar/interaction": "6.1.18",
"@fullcalendar/list": "6.1.18",
"@fullcalendar/multimonth": "6.1.18",
"@fullcalendar/timegrid": "6.1.18",
"@maplibre/maplibre-gl-leaflet": "0.1.3",
"@mermaid-js/layout-elk": "0.1.8",
"@mind-elixir/node-menu": "1.0.5",
"@mind-elixir/node-menu": "5.0.0",
"@popperjs/core": "2.11.8",
"@triliumnext/ckeditor5": "workspace:*",
"@triliumnext/codemirror": "workspace:*",
@@ -33,12 +34,11 @@
"dayjs-plugin-utc": "0.1.2",
"debounce": "2.2.0",
"draggabilly": "3.0.0",
"force-graph": "1.49.6",
"globals": "16.2.0",
"i18next": "25.2.1",
"force-graph": "1.50.1",
"globals": "16.3.0",
"i18next": "25.3.2",
"i18next-http-backend": "3.0.2",
"jquery": "3.7.1",
"jquery-hotkeys": "0.2.2",
"jquery.fancytree": "2.38.5",
"jsplumb": "2.15.6",
"katex": "0.16.22",
@@ -46,27 +46,29 @@
"leaflet": "1.9.4",
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
"marked": "15.0.12",
"mermaid": "11.7.0",
"mind-elixir": "4.6.1",
"marked": "16.1.1",
"mermaid": "11.9.0",
"mind-elixir": "5.0.4",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.26.9",
"preact": "10.27.0",
"split.js": "1.6.5",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",
"vanilla-js-wheel-zoom": "9.0.4"
},
"devDependencies": {
"@ckeditor/ckeditor5-inspector": "4.1.0",
"@ckeditor/ckeditor5-inspector": "5.0.0",
"@types/bootstrap": "5.2.10",
"@types/jquery": "3.5.32",
"@types/leaflet": "1.9.19",
"@types/leaflet": "1.9.20",
"@types/leaflet-gpx": "1.3.7",
"@types/mark.js": "8.11.12",
"@types/tabulator-tables": "6.2.9",
"copy-webpack-plugin": "13.0.0",
"happy-dom": "18.0.1",
"script-loader": "0.7.2",
"vite-plugin-static-copy": "3.1.0"
"vite-plugin-static-copy": "3.1.1"
},
"nx": {
"name": "client",
+89 -2
View File
@@ -28,6 +28,8 @@ import TouchBarComponent from "./touch_bar.js";
import type { CKTextEditor } from "@triliumnext/ckeditor5";
import type CodeMirror from "@triliumnext/codemirror";
import { StartupChecks } from "./startup_checks.js";
import type { CreateNoteOpts } from "../services/note_create.js";
import { ColumnComponent } from "tabulator-tables";
interface Layout {
getRootWidget: (appContext: AppContext) => RootWidget;
@@ -122,6 +124,7 @@ export type CommandMappings = {
showImportDialog: CommandData & { noteId: string };
openNewNoteSplit: NoteCommandData;
openInWindow: NoteCommandData;
openInPopup: CommandData & { noteIdOrPath: string; };
openNoteInNewTab: CommandData;
openNoteInNewSplit: CommandData;
openNoteInNewWindow: CommandData;
@@ -130,6 +133,8 @@ export type CommandMappings = {
hideLeftPane: CommandData;
showCpuArchWarning: CommandData;
showLeftPane: CommandData;
showAttachments: CommandData;
showSearchHistory: CommandData;
hoistNote: CommandData & { noteId: string };
leaveProtectedSession: CommandData;
enterProtectedSession: CommandData;
@@ -140,6 +145,7 @@ export type CommandMappings = {
};
openInTab: ContextMenuCommandData;
openNoteInSplit: ContextMenuCommandData;
openNoteInPopup: ContextMenuCommandData;
toggleNoteHoisting: ContextMenuCommandData;
insertNoteAfter: ContextMenuCommandData;
insertChildNote: ContextMenuCommandData;
@@ -169,7 +175,7 @@ export type CommandMappings = {
deleteNotes: ContextMenuCommandData;
importIntoNote: ContextMenuCommandData;
exportNote: ContextMenuCommandData;
searchInSubtree: ContextMenuCommandData;
searchInSubtree: CommandData & { notePath: string; };
moveNoteUp: ContextMenuCommandData;
moveNoteDown: ContextMenuCommandData;
moveNoteUpInHierarchy: ContextMenuCommandData;
@@ -258,10 +264,76 @@ export type CommandMappings = {
closeThisNoteSplit: CommandData;
moveThisNoteSplit: CommandData & { isMovingLeft: boolean };
jumpToNote: CommandData;
commandPalette: CommandData;
// Keyboard shortcuts
backInNoteHistory: CommandData;
forwardInNoteHistory: CommandData;
forceSaveRevision: CommandData;
scrollToActiveNote: CommandData;
quickSearch: CommandData;
collapseTree: CommandData;
createNoteAfter: CommandData;
createNoteInto: CommandData;
addNoteAboveToSelection: CommandData;
addNoteBelowToSelection: CommandData;
openNewTab: CommandData;
activateNextTab: CommandData;
activatePreviousTab: CommandData;
openNewWindow: CommandData;
toggleTray: CommandData;
firstTab: CommandData;
secondTab: CommandData;
thirdTab: CommandData;
fourthTab: CommandData;
fifthTab: CommandData;
sixthTab: CommandData;
seventhTab: CommandData;
eigthTab: CommandData;
ninthTab: CommandData;
lastTab: CommandData;
showNoteSource: CommandData;
showSQLConsole: CommandData;
showBackendLog: CommandData;
showCheatsheet: CommandData;
showHelp: CommandData;
addLinkToText: CommandData;
followLinkUnderCursor: CommandData;
insertDateTimeToText: CommandData;
pasteMarkdownIntoText: CommandData;
cutIntoNote: CommandData;
addIncludeNoteToText: CommandData;
editReadOnlyNote: CommandData;
toggleRibbonTabClassicEditor: CommandData;
toggleRibbonTabBasicProperties: CommandData;
toggleRibbonTabBookProperties: CommandData;
toggleRibbonTabFileProperties: CommandData;
toggleRibbonTabImageProperties: CommandData;
toggleRibbonTabOwnedAttributes: CommandData;
toggleRibbonTabInheritedAttributes: CommandData;
toggleRibbonTabPromotedAttributes: CommandData;
toggleRibbonTabNoteMap: CommandData;
toggleRibbonTabNoteInfo: CommandData;
toggleRibbonTabNotePaths: CommandData;
toggleRibbonTabSimilarNotes: CommandData;
toggleRightPane: CommandData;
printActiveNote: CommandData;
exportAsPdf: CommandData;
openNoteExternally: CommandData;
renderActiveNote: CommandData;
unhoist: CommandData;
reloadFrontendApp: CommandData;
openDevTools: CommandData;
findInText: CommandData;
toggleLeftPane: CommandData;
toggleFullscreen: CommandData;
zoomOut: CommandData;
zoomIn: CommandData;
zoomReset: CommandData;
copyWithoutFormatting: CommandData;
// Geomap
deleteFromMap: { noteId: string };
openGeoLocation: { noteId: string; event: JQuery.MouseDownEvent };
toggleZenMode: CommandData;
@@ -275,6 +347,21 @@ export type CommandMappings = {
geoMapCreateChildNote: CommandData;
// Table view
addNewRow: CommandData & {
customOpts: CreateNoteOpts;
parentNotePath?: string;
};
addNewTableColumn: CommandData & {
columnToEdit?: ColumnComponent;
referenceColumn?: ColumnComponent;
direction?: "before" | "after";
type?: "label" | "relation";
};
deleteTableColumn: CommandData & {
columnToDelete?: ColumnComponent;
};
buildTouchBar: CommandData & {
TouchBar: typeof TouchBar;
buildIcon(name: string): NativeImage;
+1 -5
View File
@@ -93,11 +93,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
if (fun) {
return this.callMethod(fun, data);
} else {
if (!this.parent) {
throw new Error(`Component "${this.componentId}" does not have a parent attached to propagate a command.`);
}
} else if (this.parent) {
return this.parent.triggerCommand(name, data);
}
}
+3 -8
View File
@@ -30,13 +30,6 @@ interface CreateChildrenResponse {
export default class Entrypoints extends Component {
constructor() {
super();
if (jQuery.hotkeys) {
// hot keys are active also inside inputs and content editables
jQuery.hotkeys.options.filterInputAcceptingElements = false;
jQuery.hotkeys.options.filterContentEditable = false;
jQuery.hotkeys.options.filterTextInputs = false;
}
}
openDevToolsCommand() {
@@ -113,7 +106,9 @@ export default class Entrypoints extends Component {
if (win.isFullScreenable()) {
win.setFullScreen(!win.isFullScreen());
}
} // outside of electron this is handled by the browser
} else {
document.documentElement.requestFullscreen();
}
}
reloadFrontendAppCommand() {
+33 -8
View File
@@ -315,14 +315,39 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
}
hasNoteList() {
return (
this.note &&
["default", "contextual-help"].includes(this.viewScope?.viewMode ?? "") &&
(this.note.hasChildren() || this.note.getLabelValue("viewType") === "calendar") &&
["book", "text", "code"].includes(this.note.type) &&
this.note.mime !== "text/x-sqlite;schema=trilium" &&
!this.note.isLabelTruthy("hideChildrenOverview")
);
const note = this.note;
if (!note) {
return false;
}
if (!["default", "contextual-help"].includes(this.viewScope?.viewMode ?? "")) {
return false;
}
// Collections must always display a note list, even if no children.
const viewType = note.getLabelValue("viewType") ?? "grid";
if (!["list", "grid"].includes(viewType)) {
return true;
}
if (!note.hasChildren()) {
return false;
}
if (!["book", "text", "code"].includes(note.type)) {
return false;
}
if (note.mime === "text/x-sqlite;schema=trilium") {
return false;
}
if (note.isLabelTruthy("hideChildrenOverview")) {
return false;
}
return true;
}
async getTextEditor(callback?: GetTextEditorCallback) {
-1
View File
@@ -13,7 +13,6 @@ import type ElectronRemote from "@electron/remote";
import type Electron from "electron";
import "./stylesheets/bootstrap.scss";
import "boxicons/css/boxicons.min.css";
import "jquery-hotkeys";
import "autocomplete.js/index_jquery.js";
await appContext.earlyInit();
+15 -2
View File
@@ -27,7 +27,6 @@ const NOTE_TYPE_ICONS = {
doc: "bx bxs-file-doc",
contentWidget: "bx bxs-widget",
mindMap: "bx bx-sitemap",
geoMap: "bx bx-map-alt",
aiChat: "bx bx-bot"
};
@@ -36,7 +35,7 @@ const NOTE_TYPE_ICONS = {
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "geoMap" | "aiChat";
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "aiChat";
export interface NotePathRecord {
isArchived: boolean;
@@ -257,6 +256,20 @@ class FNote {
return this.children;
}
async getSubtreeNoteIds() {
let noteIds: (string | string[])[] = [];
for (const child of await this.getChildNotes()) {
noteIds.push(child.noteId);
noteIds.push(await child.getSubtreeNoteIds());
}
return noteIds.flat();
}
async getSubtreeNotes() {
const noteIds = await this.getSubtreeNoteIds();
return this.froca.getNotes(noteIds);
}
async getChildNotes() {
return await this.froca.getNotes(this.children);
}
+2 -23
View File
@@ -46,28 +46,7 @@ import SharedInfoWidget from "../widgets/shared_info.js";
import FindWidget from "../widgets/find.js";
import TocWidget from "../widgets/toc.js";
import HighlightsListWidget from "../widgets/highlights_list.js";
import BulkActionsDialog from "../widgets/dialogs/bulk_actions.js";
import AboutDialog from "../widgets/dialogs/about.js";
import HelpDialog from "../widgets/dialogs/help.js";
import RecentChangesDialog from "../widgets/dialogs/recent_changes.js";
import BranchPrefixDialog from "../widgets/dialogs/branch_prefix.js";
import SortChildNotesDialog from "../widgets/dialogs/sort_child_notes.js";
import PasswordNoteSetDialog from "../widgets/dialogs/password_not_set.js";
import IncludeNoteDialog from "../widgets/dialogs/include_note.js";
import NoteTypeChooserDialog from "../widgets/dialogs/note_type_chooser.js";
import JumpToNoteDialog from "../widgets/dialogs/jump_to_note.js";
import AddLinkDialog from "../widgets/dialogs/add_link.js";
import CloneToDialog from "../widgets/dialogs/clone_to.js";
import MoveToDialog from "../widgets/dialogs/move_to.js";
import ImportDialog from "../widgets/dialogs/import.js";
import ExportDialog from "../widgets/dialogs/export.js";
import MarkdownImportDialog from "../widgets/dialogs/markdown_import.js";
import ProtectedSessionPasswordDialog from "../widgets/dialogs/protected_session_password.js";
import RevisionsDialog from "../widgets/dialogs/revisions.js";
import DeleteNotesDialog from "../widgets/dialogs/delete_notes.js";
import InfoDialog from "../widgets/dialogs/info.js";
import ConfirmDialog from "../widgets/dialogs/confirm.js";
import PromptDialog from "../widgets/dialogs/prompt.js";
import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import SvgExportButton from "../widgets/floating_buttons/svg_export_button.js";
@@ -83,7 +62,7 @@ import CopyImageReferenceButton from "../widgets/floating_buttons/copy_image_ref
import ScrollPaddingWidget from "../widgets/scroll_padding.js";
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
import options from "../services/options.js";
import utils, { hasTouchBar } from "../services/utils.js";
import utils from "../services/utils.js";
import GeoMapButtons from "../widgets/floating_buttons/geo_map_button.js";
import ContextualHelpButton from "../widgets/floating_buttons/help_button.js";
import CloseZenButton from "../widgets/close_zen_button.js";
@@ -229,7 +208,7 @@ export default class DesktopLayout {
.child(new PromotedAttributesWidget())
.child(new SqlTableSchemasWidget())
.child(new NoteDetailWidget())
.child(new NoteListWidget())
.child(new NoteListWidget(false))
.child(new SearchResultWidget())
.child(new SqlResultWidget())
.child(new ScrollPaddingWidget())
+19
View File
@@ -22,6 +22,14 @@ 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";
import PopupEditorDialog from "../widgets/dialogs/popup_editor.js";
import FlexContainer from "../widgets/containers/flex_container.js";
import NoteIconWidget from "../widgets/note_icon.js";
import NoteTitleWidget from "../widgets/note_title.js";
import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js";
import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js";
import NoteDetailWidget from "../widgets/note_detail.js";
import NoteListWidget from "../widgets/note_list.js";
export function applyModals(rootContainer: RootContainer) {
rootContainer
@@ -47,4 +55,15 @@ export function applyModals(rootContainer: RootContainer) {
.child(new ConfirmDialog())
.child(new PromptDialog())
.child(new IncorrectCpuArchDialog())
.child(new PopupEditorDialog()
.child(new FlexContainer("row")
.class("title-row")
.css("align-items", "center")
.cssBlock(".title-row > * { margin: 5px; }")
.child(new NoteIconWidget())
.child(new NoteTitleWidget()))
.child(new ClassicEditorToolbar())
.child(new PromotedAttributesWidget())
.child(new NoteDetailWidget())
.child(new NoteListWidget(true)))
}
+1 -1
View File
@@ -162,7 +162,7 @@ export default class MobileLayout {
.filling()
.contentSized()
.child(new NoteDetailWidget())
.child(new NoteListWidget())
.child(new NoteListWidget(false))
.child(new FilePropertiesWidget().css("font-size", "smaller"))
)
.child(new MobileEditorToolbar())
+36 -5
View File
@@ -2,7 +2,7 @@ import keyboardActionService from "../services/keyboard_actions.js";
import note_tooltip from "../services/note_tooltip.js";
import utils from "../services/utils.js";
interface ContextMenuOptions<T> {
export interface ContextMenuOptions<T> {
x: number;
y: number;
orientation?: "left";
@@ -17,17 +17,30 @@ interface MenuSeparatorItem {
title: "----";
}
export interface MenuItemBadge {
title: string;
className?: string;
}
export interface MenuCommandItem<T> {
title: string;
command?: T;
type?: string;
/**
* The icon to display in the menu item.
*
* If not set, no icon is displayed and the item will appear shifted slightly to the left if there are other items with icons. To avoid this, use `bx bx-empty`.
*/
uiIcon?: string;
badges?: MenuItemBadge[];
templateNoteId?: string;
enabled?: boolean;
handler?: MenuHandler<T>;
items?: MenuItem<T>[] | null;
shortcut?: string;
spellingSuggestion?: string;
checked?: boolean;
columns?: number;
}
export type MenuItem<T> = MenuCommandItem<T> | MenuSeparatorItem;
@@ -146,10 +159,13 @@ class ContextMenu {
} else {
const $icon = $("<span>");
if ("uiIcon" in item && item.uiIcon) {
$icon.addClass(item.uiIcon);
} else {
$icon.append("&nbsp;");
if ("uiIcon" in item || "checked" in item) {
const icon = (item.checked ? "bx bx-check" : item.uiIcon);
if (icon) {
$icon.addClass(icon);
} else {
$icon.append("&nbsp;");
}
}
const $link = $("<span>")
@@ -157,6 +173,18 @@ class ContextMenu {
.append(" &nbsp; ") // some space between icon and text
.append(item.title);
if ("badges" in item && item.badges) {
for (let badge of item.badges) {
const badgeElement = $(`<span class="badge">`).text(badge.title);
if (badge.className) {
badgeElement.addClass(badge.className);
}
$link.append(badgeElement);
}
}
if ("shortcut" in item && item.shortcut) {
$link.append($("<kbd>").text(item.shortcut));
}
@@ -213,6 +241,9 @@ class ContextMenu {
$link.addClass("dropdown-toggle");
const $subMenu = $("<ul>").addClass("dropdown-menu");
if (!this.isMobile && item.columns) {
$subMenu.css("column-count", item.columns);
}
this.addItems($subMenu, item.items);
+4 -1
View File
@@ -16,7 +16,8 @@ function getItems(): MenuItem<CommandNames>[] {
return [
{ title: t("link_context_menu.open_note_in_new_tab"), command: "openNoteInNewTab", uiIcon: "bx bx-link-external" },
{ title: t("link_context_menu.open_note_in_new_split"), command: "openNoteInNewSplit", uiIcon: "bx bx-dock-right" },
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" }
{ title: t("link_context_menu.open_note_in_new_window"), command: "openNoteInNewWindow", uiIcon: "bx bx-window-open" },
{ title: t("link_context_menu.open_note_in_popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit" }
];
}
@@ -40,6 +41,8 @@ function handleLinkContextMenuItem(command: string | undefined, notePath: string
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
} else if (command === "openNoteInNewWindow") {
appContext.triggerCommand("openInWindow", { notePath, hoistedNoteId, viewScope });
} else if (command === "openNoteInPopup") {
appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath })
}
}
+15 -10
View File
@@ -23,7 +23,7 @@ let lastTargetNode: HTMLElement | null = null;
// This will include all commands that implement ContextMenuCommandData, but it will not work if it additional options are added via the `|` operator,
// so they need to be added manually.
export type TreeCommandNames = FilteredCommandNames<ContextMenuCommandData> | "openBulkActionsDialog";
export type TreeCommandNames = FilteredCommandNames<ContextMenuCommandData> | "openBulkActionsDialog" | "searchInSubtree";
export default class TreeContextMenu implements SelectMenuItemEventListener<TreeCommandNames> {
private treeWidget: NoteTreeWidget;
@@ -70,8 +70,8 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
const items: (MenuItem<TreeCommandNames> | null)[] = [
{ title: `${t("tree-context-menu.open-in-a-new-tab")}`, command: "openInTab", uiIcon: "bx bx-link-external", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit", enabled: noSelectedNotes },
isHoisted
? null
@@ -92,7 +92,8 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
command: "insertNoteAfter",
uiIcon: "bx bx-plus",
items: insertNoteAfterEnabled ? await noteTypesService.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes && notOptionsOrHelp
enabled: insertNoteAfterEnabled && noSelectedNotes && notOptionsOrHelp,
columns: 2
},
{
@@ -100,7 +101,8 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
command: "insertChildNote",
uiIcon: "bx bx-plus",
items: notSearch ? await noteTypesService.getNoteTypeItems("insertChildNote") : null,
enabled: notSearch && noSelectedNotes && notOptionsOrHelp
enabled: notSearch && noSelectedNotes && notOptionsOrHelp,
columns: 2
},
{ title: "----" },
@@ -127,12 +129,6 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
enabled: isNotRoot && parentNotSearch && noSelectedNotes && notOptionsOrHelp
},
{ title: t("tree-context-menu.convert-to-attachment"), command: "convertNoteToAttachment", uiIcon: "bx bx-paperclip", enabled: isNotRoot && !isHoisted && notOptionsOrHelp },
{
title: `${t("tree-context-menu.duplicate-subtree")} <kbd data-command="duplicateSubtree">`,
command: "duplicateSubtree",
uiIcon: "bx bx-outline",
enabled: parentNotSearch && isNotRoot && !isHoisted && notOptionsOrHelp
},
{ title: "----" },
@@ -186,6 +182,13 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
{ title: `${t("tree-context-menu.clone-to")} <kbd data-command="cloneNotesTo"></kbd>`, command: "cloneNotesTo", uiIcon: "bx bx-duplicate", enabled: isNotRoot && !isHoisted },
{
title: `${t("tree-context-menu.duplicate")} <kbd data-command="duplicateSubtree">`,
command: "duplicateSubtree",
uiIcon: "bx bx-outline",
enabled: parentNotSearch && isNotRoot && !isHoisted && notOptionsOrHelp
},
{
title: `${t("tree-context-menu.delete")} <kbd data-command="deleteNotes"></kbd>`,
command: "deleteNotes",
@@ -244,6 +247,8 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
const { ntxId } = subContexts?.[subContexts.length - 1] ?? {};
this.treeWidget.triggerCommand("openNewNoteSplit", { ntxId, notePath });
} else if (command === "openNoteInPopup") {
appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath })
} else if (command === "convertNoteToAttachment") {
if (!(await dialogService.confirm(t("tree-context-menu.convert-to-attachment-confirm")))) {
return;
+13 -1
View File
@@ -79,7 +79,19 @@ async function renderAttributes(attributes: FAttribute[], renderIsInheritable: b
return $container;
}
const HIDDEN_ATTRIBUTES = ["originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation", "docName"];
const HIDDEN_ATTRIBUTES = [
"originalFileName",
"fileSize",
"template",
"inherit",
"cssClass",
"iconClass",
"pageSize",
"viewType",
"geolocation",
"docName",
"webViewSrc"
];
async function renderNormalAttributes(note: FNote) {
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();
+7 -5
View File
@@ -3,19 +3,21 @@ import froca from "./froca.js";
import type FNote from "../entities/fnote.js";
import type { AttributeRow } from "./load_results.js";
async function addLabel(noteId: string, name: string, value: string = "") {
async function addLabel(noteId: string, name: string, value: string = "", isInheritable = false) {
await server.put(`notes/${noteId}/attribute`, {
type: "label",
name: name,
value: value
value: value,
isInheritable
});
}
async function setLabel(noteId: string, name: string, value: string = "") {
export async function setLabel(noteId: string, name: string, value: string = "", isInheritable = false) {
await server.put(`notes/${noteId}/set-attribute`, {
type: "label",
name: name,
value: value
value: value,
isInheritable
});
}
@@ -49,7 +51,7 @@ function removeOwnedLabelByName(note: FNote, labelName: string) {
* @param name the name of the attribute to set.
* @param value the value of the attribute to set.
*/
async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) {
export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) {
if (value) {
// Create or update the attribute.
await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value });
+15 -5
View File
@@ -95,7 +95,15 @@ async function moveToParentNote(branchIdsToMove: string[], newParentBranchId: st
}
}
async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = false) {
/**
* Shows the delete confirmation screen
*
* @param branchIdsToDelete the list of branch IDs to delete.
* @param forceDeleteAllClones whether to check by default the "Delete also all clones" checkbox.
* @param moveToParent whether to automatically go to the parent note path after a succesful delete. Usually makes sense if deleting the active note(s).
* @returns promise that returns false if the operation was cancelled or there was nothing to delete, true if the operation succeeded.
*/
async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = false, moveToParent = true) {
branchIdsToDelete = filterRootNote(branchIdsToDelete);
if (branchIdsToDelete.length === 0) {
@@ -110,10 +118,12 @@ async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = f
return false;
}
try {
await activateParentNotePath();
} catch (e) {
console.error(e);
if (moveToParent) {
try {
await activateParentNotePath();
} catch (e) {
console.error(e);
}
}
const taskId = utils.randomString(10);
+13
View File
@@ -15,6 +15,8 @@ import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation
import RenameNoteBulkAction from "../widgets/bulk_actions/note/rename_note.js";
import { t } from "./i18n.js";
import type FNote from "../entities/fnote.js";
import toast from "./toast.js";
import { BulkAction } from "@triliumnext/commons";
const ACTION_GROUPS = [
{
@@ -89,6 +91,17 @@ function parseActions(note: FNote) {
.filter((action) => !!action);
}
export async function executeBulkActions(targetNoteIds: string[], actions: BulkAction[], includeDescendants = false) {
await server.post("bulk-action/execute", {
noteIds: targetNoteIds,
includeDescendants,
actions
});
await ws.waitForMaxKnownEntityChangeId();
toast.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
}
export default {
addAction,
parseActions,
@@ -0,0 +1,295 @@
import { ActionKeyboardShortcut } from "@triliumnext/commons";
import appContext, { type CommandNames } from "../components/app_context.js";
import type NoteTreeWidget from "../widgets/note_tree.js";
import { t, translationsInitializedPromise } from "./i18n.js";
import keyboardActions from "./keyboard_actions.js";
import utils from "./utils.js";
export interface CommandDefinition {
id: string;
name: string;
description?: string;
icon?: string;
shortcut?: string;
commandName?: CommandNames;
handler?: () => Promise<unknown> | null | undefined | void;
aliases?: string[];
source?: "manual" | "keyboard-action";
/** Reference to the original keyboard action for scope checking. */
keyboardAction?: ActionKeyboardShortcut;
}
class CommandRegistry {
private commands: Map<string, CommandDefinition> = new Map();
private aliases: Map<string, string> = new Map();
constructor() {
this.loadCommands();
}
private async loadCommands() {
await translationsInitializedPromise;
this.registerDefaultCommands();
await this.loadKeyboardActionsAsync();
}
private registerDefaultCommands() {
this.register({
id: "export-note",
name: t("command_palette.export_note_title"),
description: t("command_palette.export_note_description"),
icon: "bx bx-export",
handler: () => {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (notePath) {
appContext.triggerCommand("showExportDialog", {
notePath,
defaultType: "single"
});
}
}
});
this.register({
id: "show-attachments",
name: t("command_palette.show_attachments_title"),
description: t("command_palette.show_attachments_description"),
icon: "bx bx-paperclip",
handler: () => appContext.triggerCommand("showAttachments")
});
// Special search commands with custom logic
this.register({
id: "search-notes",
name: t("command_palette.search_notes_title"),
description: t("command_palette.search_notes_description"),
icon: "bx bx-search",
handler: () => appContext.triggerCommand("searchNotes", {})
});
this.register({
id: "search-in-subtree",
name: t("command_palette.search_subtree_title"),
description: t("command_palette.search_subtree_description"),
icon: "bx bx-search-alt",
handler: () => {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (notePath) {
appContext.triggerCommand("searchInSubtree", { notePath });
}
}
});
this.register({
id: "show-search-history",
name: t("command_palette.search_history_title"),
description: t("command_palette.search_history_description"),
icon: "bx bx-history",
handler: () => appContext.triggerCommand("showSearchHistory")
});
this.register({
id: "show-launch-bar",
name: t("command_palette.configure_launch_bar_title"),
description: t("command_palette.configure_launch_bar_description"),
icon: "bx bx-sidebar",
handler: () => appContext.triggerCommand("showLaunchBarSubtree")
});
}
private async loadKeyboardActionsAsync() {
try {
const actions = await keyboardActions.getActions();
this.registerKeyboardActions(actions);
} catch (error) {
console.error("Failed to load keyboard actions:", error);
}
}
private registerKeyboardActions(actions: ActionKeyboardShortcut[]) {
for (const action of actions) {
// Skip actions that we've already manually registered
if (this.commands.has(action.actionName)) {
continue;
}
// Skip actions that don't have a description (likely separators)
if (!action.description) {
continue;
}
// Skip Electron-only actions if not in Electron environment
if (action.isElectronOnly && !utils.isElectron()) {
continue;
}
// Skip actions that should not appear in the command palette
if (action.ignoreFromCommandPalette) {
continue;
}
// Get the primary shortcut (first one in the list)
const primaryShortcut = action.effectiveShortcuts?.[0];
let name = action.friendlyName;
if (action.scope === "note-tree") {
name = t("command_palette.tree-action-name", { name: action.friendlyName });
}
// Create a command definition from the keyboard action
const commandDef: CommandDefinition = {
id: action.actionName,
name,
description: action.description,
icon: action.iconClass,
shortcut: primaryShortcut ? this.formatShortcut(primaryShortcut) : undefined,
commandName: action.actionName as CommandNames,
source: "keyboard-action",
keyboardAction: action
};
this.register(commandDef);
}
}
private formatShortcut(shortcut: string): string {
// Convert electron accelerator format to display format
return shortcut
.replace(/CommandOrControl/g, 'Ctrl')
.replace(/\+/g, ' + ');
}
register(command: CommandDefinition) {
this.commands.set(command.id, command);
// Register aliases
if (command.aliases) {
for (const alias of command.aliases) {
this.aliases.set(alias.toLowerCase(), command.id);
}
}
}
getCommand(id: string): CommandDefinition | undefined {
return this.commands.get(id);
}
getAllCommands(): CommandDefinition[] {
const commands = Array.from(this.commands.values());
// Sort commands by name
commands.sort((a, b) => a.name.localeCompare(b.name));
return commands;
}
searchCommands(query: string): CommandDefinition[] {
const normalizedQuery = query.toLowerCase();
const results: { command: CommandDefinition; score: number }[] = [];
for (const command of this.commands.values()) {
let score = 0;
// Exact match on name
if (command.name.toLowerCase() === normalizedQuery) {
score = 100;
}
// Name starts with query
else if (command.name.toLowerCase().startsWith(normalizedQuery)) {
score = 80;
}
// Name contains query
else if (command.name.toLowerCase().includes(normalizedQuery)) {
score = 60;
}
// Description contains query
else if (command.description?.toLowerCase().includes(normalizedQuery)) {
score = 40;
}
// Check aliases
else if (command.aliases?.some(alias => alias.toLowerCase().includes(normalizedQuery))) {
score = 50;
}
if (score > 0) {
results.push({ command, score });
}
}
// Sort by score (highest first) and then by name
results.sort((a, b) => {
if (a.score !== b.score) {
return b.score - a.score;
}
return a.command.name.localeCompare(b.command.name);
});
return results.map(r => r.command);
}
async executeCommand(commandId: string) {
const command = this.getCommand(commandId);
if (!command) {
console.error(`Command not found: ${commandId}`);
return;
}
// Execute custom handler if provided
if (command.handler) {
await command.handler();
return;
}
// Handle keyboard action with scope-aware execution
if (command.keyboardAction && command.commandName) {
if (command.keyboardAction.scope === "note-tree") {
this.executeWithNoteTreeFocus(command.commandName);
} else if (command.keyboardAction.scope === "text-detail") {
this.executeWithTextDetail(command.commandName);
} else {
appContext.triggerCommand(command.commandName, {
ntxId: appContext.tabManager.activeNtxId
});
}
return;
}
// Fallback for commands without keyboard action reference
if (command.commandName) {
appContext.triggerCommand(command.commandName, {
ntxId: appContext.tabManager.activeNtxId
});
return;
}
console.error(`Command ${commandId} has no handler or commandName`);
}
private executeWithNoteTreeFocus(actionName: CommandNames) {
const tree = document.querySelector(".tree-wrapper") as HTMLElement;
if (!tree) {
return;
}
const treeComponent = appContext.getComponentByEl(tree) as NoteTreeWidget;
const activeNode = treeComponent.getActiveNode();
treeComponent.triggerCommand(actionName, {
ntxId: appContext.tabManager.activeNtxId,
node: activeNode
});
}
private async executeWithTextDetail(actionName: CommandNames) {
const typeWidget = await appContext.tabManager.getActiveContext()?.getTypeWidget();
if (!typeWidget) {
return;
}
typeWidget.triggerCommand(actionName, {
ntxId: appContext.tabManager.activeNtxId
});
}
}
const commandRegistry = new CommandRegistry();
export default commandRegistry;
+39 -2
View File
@@ -65,6 +65,9 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FA
$renderedContent.append($("<div>").append("<div>This note is protected and to access it you need to enter password.</div>").append("<br/>").append($button));
} else if (entity instanceof FNote) {
$renderedContent
.css("display", "flex")
.css("flex-direction", "column");
$renderedContent.append(
$("<div>")
.css("display", "flex")
@@ -72,8 +75,33 @@ async function getRenderedContent(this: {} | { ctx: string }, entity: FNote | FA
.css("align-items", "center")
.css("height", "100%")
.css("font-size", "500%")
.css("flex-grow", "1")
.append($("<span>").addClass(entity.getIcon()))
);
if (entity.type === "webView" && entity.hasLabel("webViewSrc")) {
const $footer = $("<footer>")
.addClass("webview-footer");
const $openButton = $(`
<button class="file-open btn btn-primary" type="button">
<span class="bx bx-link-external"></span>
${t("content_renderer.open_externally")}
</button>
`)
.appendTo($footer)
.on("click", () => {
const webViewSrc = entity.getLabelValue("webViewSrc");
if (webViewSrc) {
if (utils.isElectron()) {
const electron = utils.dynamicRequire("electron");
electron.shell.openExternal(webViewSrc);
} else {
window.open(webViewSrc, '_blank', 'noopener,noreferrer');
}
}
});
$footer.appendTo($renderedContent);
}
}
if (entity instanceof FNote) {
@@ -118,8 +146,17 @@ async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HT
async function renderCode(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>) {
const blob = await note.getBlob();
let content = blob?.content || "";
if (note.mime === "application/json") {
try {
content = JSON.stringify(JSON.parse(content), null, 4);
} catch (e) {
// Ignore JSON parsing errors.
}
}
const $codeBlock = $("<code>");
$codeBlock.text(blob?.content || "");
$codeBlock.text(content);
$renderedContent.append($("<pre>").append($codeBlock));
await applySingleBlockSyntaxHighlight($codeBlock, normalizeMimeTypeForCKEditor(note.mime));
}
@@ -301,7 +338,7 @@ function getRenderingType(entity: FNote | FAttachment) {
if (type === "file" && mime === "application/pdf") {
type = "pdf";
} else if (type === "file" && mime && CODE_MIME_TYPES.has(mime)) {
} else if ((type === "file" || type === "viewConfig") && mime && CODE_MIME_TYPES.has(mime)) {
type = "code";
} else if (type === "file" && mime && mime.startsWith("audio/")) {
type = "audio";
+9 -3
View File
@@ -4,14 +4,14 @@ import type { ConfirmDialogOptions, ConfirmDialogResult, ConfirmWithMessageOptio
import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js";
import { focusSavedElement, saveFocusedElement } from "./focus.js";
export async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
export async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true, config?: Partial<Modal.Options>) {
if (closeActDialog) {
closeActiveDialog();
glob.activeDialog = $dialog;
}
saveFocusedElement();
Modal.getOrCreateInstance($dialog[0]).show();
Modal.getOrCreateInstance($dialog[0], config).show();
$dialog.on("hidden.bs.modal", () => {
const $autocompleteEl = $(".aa-input");
@@ -41,8 +41,14 @@ async function info(message: string) {
return new Promise((res) => appContext.triggerCommand("showInfoDialog", { message, callback: res }));
}
/**
* Displays a confirmation dialog with the given message.
*
* @param message the message to display in the dialog.
* @returns A promise that resolves to true if the user confirmed, false otherwise.
*/
async function confirm(message: string) {
return new Promise((res) =>
return new Promise<boolean>((res) =>
appContext.triggerCommand("showConfirmDialog", <ConfirmWithMessageOptions>{
message,
callback: (x: false | ConfirmDialogOptions) => res(x && x.confirmed)
+8 -1
View File
@@ -49,6 +49,13 @@ function setupGlobs() {
const string = e?.reason?.message?.toLowerCase();
let message = "Uncaught error: ";
let errorObjectString;
try {
errorObjectString = JSON.stringify(e.reason)
} catch (error: any) {
errorObjectString = error.toString();
}
if (string?.includes("script error")) {
message += "No details available";
@@ -57,7 +64,7 @@ function setupGlobs() {
`Message: ${e.reason.message}`,
`Line: ${e.reason.lineNumber}`,
`Column: ${e.reason.columnNumber}`,
`Error object: ${JSON.stringify(e.reason)}`,
`Error object: ${errorObjectString}`,
`Stack: ${e.reason && e.reason.stack}`
].join(", ");
}
+7
View File
@@ -6,6 +6,11 @@ import type { Locale } from "@triliumnext/commons";
let locales: Locale[] | null;
/**
* A deferred promise that resolves when translations are initialized.
*/
export let translationsInitializedPromise = $.Deferred();
export async function initLocale() {
const locale = (options.get("locale") as string) || "en";
@@ -19,6 +24,8 @@ export async function initLocale() {
},
returnEmptyString: false
});
translationsInitializedPromise.resolve();
}
export function getAvailableLocales() {
+8 -14
View File
@@ -2,21 +2,15 @@ import server from "./server.js";
import appContext, { type CommandNames } from "../components/app_context.js";
import shortcutService from "./shortcuts.js";
import type Component from "../components/component.js";
import type { ActionKeyboardShortcut } from "@triliumnext/commons";
const keyboardActionRepo: Record<string, Action> = {};
const keyboardActionRepo: Record<string, ActionKeyboardShortcut> = {};
// TODO: Deduplicate with server.
export interface Action {
actionName: CommandNames;
effectiveShortcuts: string[];
scope: string;
}
const keyboardActionsLoaded = server.get<Action[]>("keyboard-actions").then((actions) => {
const keyboardActionsLoaded = server.get<ActionKeyboardShortcut[]>("keyboard-actions").then((actions) => {
actions = actions.filter((a) => !!a.actionName); // filter out separators
for (const action of actions) {
action.effectiveShortcuts = action.effectiveShortcuts.filter((shortcut) => !shortcut.startsWith("global:"));
action.effectiveShortcuts = (action.effectiveShortcuts ?? []).filter((shortcut) => !shortcut.startsWith("global:"));
keyboardActionRepo[action.actionName] = action;
}
@@ -38,7 +32,7 @@ async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, c
const actions = await getActionsForScope(scope);
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
for (const shortcut of action.effectiveShortcuts ?? []) {
shortcutService.bindElShortcut($el, shortcut, () => component.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
}
}
@@ -46,7 +40,7 @@ async function setupActionsForElement(scope: string, $el: JQuery<HTMLElement>, c
getActionsForScope("window").then((actions) => {
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
for (const shortcut of action.effectiveShortcuts ?? []) {
shortcutService.bindGlobalShortcut(shortcut, () => appContext.triggerCommand(action.actionName, { ntxId: appContext.tabManager.activeNtxId }));
}
}
@@ -80,7 +74,7 @@ function updateDisplayedShortcuts($container: JQuery<HTMLElement>) {
const action = await getAction(actionName, true);
if (action) {
const keyboardActions = action.effectiveShortcuts.join(", ");
const keyboardActions = (action.effectiveShortcuts ?? []).join(", ");
if (keyboardActions || $(el).text() !== "not set") {
$(el).text(keyboardActions);
@@ -99,7 +93,7 @@ function updateDisplayedShortcuts($container: JQuery<HTMLElement>) {
if (action) {
const title = $(el).attr("title");
const shortcuts = action.effectiveShortcuts.join(", ");
const shortcuts = (action.effectiveShortcuts ?? []).join(", ");
if (title?.includes(shortcuts)) {
return;
+33 -12
View File
@@ -231,6 +231,7 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
let ntxId: string | null = null;
let hoistedNoteId: string | null = null;
let searchString: string | null = null;
let openInPopup = false;
if (paramString) {
for (const pair of paramString.split("&")) {
@@ -246,6 +247,8 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
searchString = value; // supports triggering search from URL, e.g. #?searchString=blabla
} else if (["viewMode", "attachmentId"].includes(name)) {
(viewScope as any)[name] = value;
} else if (name === "popup") {
openInPopup = true;
} else {
console.warn(`Unrecognized hash parameter '${name}'.`);
}
@@ -266,7 +269,8 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
ntxId,
hoistedNoteId,
viewScope,
searchString
searchString,
openInPopup
};
}
@@ -277,13 +281,21 @@ function goToLink(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent) {
return goToLinkExt(evt, hrefLink, $link);
}
function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent<HTMLCanvasElement>, hrefLink: string | undefined, $link?: JQuery<HTMLElement> | null) {
/**
* Handles navigation to a link, which can be an internal note path (e.g., `#root/1234`) or an external URL (e.g., `https://example.com`).
*
* @param evt the event that triggered the link navigation, or `null` if the link was clicked programmatically. Used to determine if the link should be opened in a new tab/window, based on the button presses.
* @param hrefLink the link to navigate to, which can be a note path (e.g., `#root/1234`) or an external URL with any supported protocol (e.g., `https://example.com`).
* @param $link the jQuery element of the link that was clicked, used to determine if the link is an anchor link (e.g., `#fn1` or `#fnref1`) and to handle it accordingly.
* @returns `true` if the link was handled (i.e., the element was found and scrolled to), or a falsy value otherwise.
*/
function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent<HTMLCanvasElement> | null, hrefLink: string | undefined, $link?: JQuery<HTMLElement> | null) {
if (hrefLink?.startsWith("data:")) {
return true;
}
evt.preventDefault();
evt.stopPropagation();
evt?.preventDefault();
evt?.stopPropagation();
if (hrefLink && hrefLink.startsWith("#") && !hrefLink.startsWith("#root/") && $link) {
if (handleAnchor(hrefLink, $link)) {
@@ -291,19 +303,22 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
}
}
const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink);
const { notePath, viewScope, openInPopup } = parseNavigationStateFromUrl(hrefLink);
const ctrlKey = utils.isCtrlKey(evt);
const shiftKey = evt.shiftKey;
const isLeftClick = "which" in evt && evt.which === 1;
const isMiddleClick = "which" in evt && evt.which === 2;
const ctrlKey = evt && utils.isCtrlKey(evt);
const shiftKey = evt?.shiftKey;
const isLeftClick = !evt || ("which" in evt && evt.which === 1);
// Right click is handled separately.
const isMiddleClick = evt && "which" in evt && evt.which === 2;
const targetIsBlank = ($link?.attr("target") === "_blank");
const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick || targetIsBlank;
const activate = (isLeftClick && ctrlKey && shiftKey) || (isMiddleClick && shiftKey);
const openInNewWindow = isLeftClick && evt.shiftKey && !ctrlKey;
const openInNewWindow = isLeftClick && evt?.shiftKey && !ctrlKey;
if (notePath) {
if (openInNewWindow) {
if (isLeftClick && openInPopup) {
appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath });
} else if (openInNewWindow) {
appContext.triggerCommand("openInWindow", { notePath, viewScope });
} else if (openInNewTab) {
appContext.tabManager.openTabWithNoteWithHoisting(notePath, {
@@ -311,7 +326,7 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
viewScope
});
} else if (isLeftClick) {
const ntxId = $(evt.target as any)
const ntxId = $(evt?.target as any)
.closest("[data-ntx-id]")
.attr("data-ntx-id");
@@ -379,6 +394,12 @@ function linkContextMenu(e: PointerEvent) {
return;
}
if (utils.isCtrlKey(e) && e.button === 2) {
appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath });
e.preventDefault();
return;
}
e.preventDefault();
linkContextMenuService.openContextMenu(notePath, e, viewScope, null);
+70 -4
View File
@@ -3,6 +3,7 @@ import appContext from "../components/app_context.js";
import noteCreateService from "./note_create.js";
import froca from "./froca.js";
import { t } from "./i18n.js";
import commandRegistry from "./command_registry.js";
import type { MentionFeedObjectItem } from "@triliumnext/ckeditor5";
// this key needs to have this value, so it's hit by the tooltip
@@ -29,9 +30,12 @@ export interface Suggestion {
notePathTitle?: string;
notePath?: string;
highlightedNotePathTitle?: string;
action?: string | "create-note" | "search-notes" | "external-link";
action?: string | "create-note" | "search-notes" | "external-link" | "command";
parentNoteId?: string;
icon?: string;
commandId?: string;
commandDescription?: string;
commandShortcut?: string;
}
interface Options {
@@ -40,7 +44,12 @@ interface Options {
allowCreatingNotes?: boolean;
allowJumpToSearchNotes?: boolean;
allowExternalLinks?: boolean;
/** If set, hides the right-side button corresponding to go to selected note. */
hideGoToSelectedNoteButton?: boolean;
/** If set, hides all right-side buttons in the autocomplete dropdown */
hideAllButtons?: boolean;
/** If set, enables command palette mode */
isCommandPalette?: boolean;
}
async function autocompleteSourceForCKEditor(queryText: string) {
@@ -70,6 +79,31 @@ async function autocompleteSourceForCKEditor(queryText: string) {
}
async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void, options: Options = {}) {
// Check if we're in command mode
if (options.isCommandPalette && term.startsWith(">")) {
const commandQuery = term.substring(1).trim();
// Get commands (all if no query, filtered if query provided)
const commands = commandQuery.length === 0
? commandRegistry.getAllCommands()
: commandRegistry.searchCommands(commandQuery);
// Convert commands to suggestions
const commandSuggestions: Suggestion[] = commands.map(cmd => ({
action: "command",
commandId: cmd.id,
noteTitle: cmd.name,
notePathTitle: `>${cmd.name}`,
highlightedNotePathTitle: cmd.name,
commandDescription: cmd.description,
commandShortcut: cmd.shortcut,
icon: cmd.icon
}));
cb(commandSuggestions);
return;
}
const fastSearch = options.fastSearch === false ? false : true;
if (fastSearch === false) {
if (term.trim().length === 0) {
@@ -143,6 +177,12 @@ function showRecentNotes($el: JQuery<HTMLElement>) {
$el.trigger("focus");
}
function showAllCommands($el: JQuery<HTMLElement>) {
searchDelay = 0;
$el.setSelectedNotePath("");
$el.autocomplete("val", ">").autocomplete("open");
}
function fullTextSearch($el: JQuery<HTMLElement>, options: Options) {
const searchString = $el.autocomplete("val") as unknown as string;
if (options.fastSearch === false || searchString?.trim().length === 0) {
@@ -190,9 +230,11 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
const $goToSelectedNoteButton = $("<a>").addClass("input-group-text go-to-selected-note-button bx bx-arrow-to-right");
$el.after($clearTextButton).after($showRecentNotesButton).after($fullTextSearchButton);
if (!options.hideAllButtons) {
$el.after($clearTextButton).after($showRecentNotesButton).after($fullTextSearchButton);
}
if (!options.hideGoToSelectedNoteButton) {
if (!options.hideGoToSelectedNoteButton && !options.hideAllButtons) {
$el.after($goToSelectedNoteButton);
}
@@ -265,7 +307,24 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
},
displayKey: "notePathTitle",
templates: {
suggestion: (suggestion) => `<span class="${suggestion.icon ?? "bx bx-note"}"></span> ${suggestion.highlightedNotePathTitle}`
suggestion: (suggestion) => {
if (suggestion.action === "command") {
let html = `<div class="command-suggestion">`;
html += `<span class="command-icon ${suggestion.icon || "bx bx-terminal"}"></span>`;
html += `<div class="command-content">`;
html += `<div class="command-name">${suggestion.highlightedNotePathTitle}</div>`;
if (suggestion.commandDescription) {
html += `<div class="command-description">${suggestion.commandDescription}</div>`;
}
html += `</div>`;
if (suggestion.commandShortcut) {
html += `<kbd class="command-shortcut">${suggestion.commandShortcut}</kbd>`;
}
html += '</div>';
return html;
}
return `<span class="${suggestion.icon ?? "bx bx-note"}"></span> ${suggestion.highlightedNotePathTitle}`;
}
},
// we can't cache identical searches because notes can be created / renamed, new recent notes can be added
cache: false
@@ -275,6 +334,12 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
// TODO: Types fail due to "autocomplete:selected" not being registered in type definitions.
($el as any).on("autocomplete:selected", async (event: Event, suggestion: Suggestion) => {
if (suggestion.action === "command") {
$el.autocomplete("close");
$el.trigger("autocomplete:commandselected", [suggestion]);
return;
}
if (suggestion.action === "external-link") {
$el.setSelectedNotePath(null);
$el.setSelectedExternalLink(suggestion.externalLink);
@@ -391,6 +456,7 @@ export default {
autocompleteSourceForCKEditor,
initNoteAutocomplete,
showRecentNotes,
showAllCommands,
setText,
init
};
+1 -1
View File
@@ -11,7 +11,7 @@ import type FBranch from "../entities/fbranch.js";
import type { ChooseNoteTypeResponse } from "../widgets/dialogs/note_type_chooser.js";
import type { CKTextEditor } from "@triliumnext/ckeditor5";
interface CreateNoteOpts {
export interface CreateNoteOpts {
isProtected?: boolean;
saveSelection?: boolean;
title?: string | null;
+40 -24
View File
@@ -1,38 +1,31 @@
import type FNote from "../entities/fnote.js";
import BoardView from "../widgets/view_widgets/board_view/index.js";
import CalendarView from "../widgets/view_widgets/calendar_view.js";
import GeoView from "../widgets/view_widgets/geo_view/index.js";
import ListOrGridView from "../widgets/view_widgets/list_or_grid_view.js";
import TableView from "../widgets/view_widgets/table_view/index.js";
import type { ViewModeArgs } from "../widgets/view_widgets/view_mode.js";
import type ViewMode from "../widgets/view_widgets/view_mode.js";
export type ViewTypeOptions = "list" | "grid" | "calendar";
const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board"] as const;
export type ArgsWithoutNoteId = Omit<ViewModeArgs, "noteIds">;
export type ViewTypeOptions = typeof allViewTypes[number];
export default class NoteListRenderer {
private viewType: ViewTypeOptions;
public viewMode: ViewMode | null;
private args: ArgsWithoutNoteId;
public viewMode?: ViewMode<any>;
constructor($parent: JQuery<HTMLElement>, parentNote: FNote, noteIds: string[], showNotePath: boolean = false) {
this.viewType = this.#getViewType(parentNote);
const args: ViewModeArgs = {
$parent,
parentNote,
noteIds,
showNotePath
};
if (this.viewType === "list" || this.viewType === "grid") {
this.viewMode = new ListOrGridView(this.viewType, args);
} else if (this.viewType === "calendar") {
this.viewMode = new CalendarView(args);
} else {
this.viewMode = null;
}
constructor(args: ArgsWithoutNoteId) {
this.args = args;
this.viewType = this.#getViewType(args.parentNote);
}
#getViewType(parentNote: FNote): ViewTypeOptions {
const viewType = parentNote.getLabelValue("viewType");
if (!["list", "grid", "calendar"].includes(viewType || "")) {
if (!(allViewTypes as readonly string[]).includes(viewType || "")) {
// when not explicitly set, decide based on the note type
return parentNote.type === "search" ? "list" : "grid";
} else {
@@ -41,15 +34,38 @@ export default class NoteListRenderer {
}
get isFullHeight() {
return this.viewMode?.isFullHeight;
switch (this.viewType) {
case "list":
case "grid":
return false;
default:
return true;
}
}
async renderList() {
if (!this.viewMode) {
return null;
}
const args = this.args;
const viewMode = this.#buildViewMode(args);
this.viewMode = viewMode;
await viewMode.beforeRender();
return await viewMode.renderList();
}
return await this.viewMode.renderList();
#buildViewMode(args: ViewModeArgs) {
switch (this.viewType) {
case "calendar":
return new CalendarView(args);
case "table":
return new TableView(args);
case "geoMap":
return new GeoView(args);
case "board":
return new BoardView(args);
case "list":
case "grid":
default:
return new ListOrGridView(this.viewType, args);
}
}
}
+7 -2
View File
@@ -13,7 +13,8 @@ let openTooltipElements: JQuery<HTMLElement>[] = [];
let dismissTimer: ReturnType<typeof setTimeout>;
function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
$(document).on("mouseenter", "a:not(.no-tooltip-preview)", mouseEnterHandler);
$(document).on("mouseenter", "[data-href]:not(.no-tooltip-preview)", mouseEnterHandler);
// close any note tooltip after click, this fixes the problem that sometimes tooltips remained on the screen
$(document).on("click", (e) => {
@@ -167,7 +168,10 @@ async function renderTooltip(note: FNote | null) {
if (isContentEmpty) {
classes.push("note-no-content");
}
content = `<h5 class="${classes.join(" ")}"><a href="#${note.noteId}" data-no-context-menu="true">${noteTitleWithPathAsSuffix.prop("outerHTML")}</a></h5>`;
content = `\
<h5 class="${classes.join(" ")}">
<a href="#${note.noteId}" data-no-context-menu="true">${noteTitleWithPathAsSuffix.prop("outerHTML")}</a>
</h5>`;
}
content = `${content}<div class="note-tooltip-attributes">${$renderedAttributes[0].outerHTML}</div>`;
@@ -175,6 +179,7 @@ async function renderTooltip(note: FNote | null) {
content += $renderedContent[0].outerHTML;
}
content += `<a class="open-popup-button" title="${t("note_tooltip.quick-edit")}" href="#${note.noteId}?popup"><span class="bx bx-edit" /></a>`;
return content;
}
+181 -24
View File
@@ -1,32 +1,118 @@
import server from "./server.js";
import froca from "./froca.js";
import { t } from "./i18n.js";
import type { MenuItem } from "../menus/context_menu.js";
import froca from "./froca.js";
import server from "./server.js";
import type { MenuCommandItem, MenuItem, MenuItemBadge } from "../menus/context_menu.js";
import type { NoteType } from "../entities/fnote.js";
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
export interface NoteTypeMapping {
type: NoteType;
mime?: string;
title: string;
icon?: string;
/** Indicates whether this type should be marked as a newly introduced feature. */
isNew?: boolean;
/** Indicates that this note type is part of a beta feature. */
isBeta?: boolean;
/** Indicates that this note type cannot be created by the user. */
reserved?: boolean;
/** Indicates that once a note of this type is created, its type can no longer be changed. */
static?: boolean;
}
export const NOTE_TYPES: NoteTypeMapping[] = [
// The suggested note type ordering method: insert the item into the corresponding group,
// then ensure the items within the group are ordered alphabetically.
// The default note type (always the first item)
{ type: "text", mime: "text/html", title: t("note_types.text"), icon: "bx-note" },
// Text notes group
{ type: "book", mime: "", title: t("note_types.book"), icon: "bx-book" },
// Graphic notes
{ type: "canvas", mime: "application/json", title: t("note_types.canvas"), icon: "bx-pen" },
{ type: "mermaid", mime: "text/mermaid", title: t("note_types.mermaid-diagram"), icon: "bx-selection" },
// Map notes
{ type: "mindMap", mime: "application/json", title: t("note_types.mind-map"), icon: "bx-sitemap" },
{ type: "noteMap", mime: "", title: t("note_types.note-map"), icon: "bxs-network-chart", static: true },
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), icon: "bxs-network-chart" },
// Misc note types
{ type: "render", mime: "", title: t("note_types.render-note"), icon: "bx-extension" },
{ type: "search", title: t("note_types.saved-search"), icon: "bx-file-find", static: true },
{ type: "webView", mime: "", title: t("note_types.web-view"), icon: "bx-globe-alt" },
// Code notes
{ type: "code", mime: "text/plain", title: t("note_types.code"), icon: "bx-code" },
// Reserved types (cannot be created by the user)
{ type: "contentWidget", mime: "", title: t("note_types.widget"), reserved: true },
{ type: "doc", mime: "", title: t("note_types.doc"), reserved: true },
{ type: "file", title: t("note_types.file"), reserved: true },
{ type: "image", title: t("note_types.image"), reserved: true },
{ type: "launcher", mime: "", title: t("note_types.launcher"), reserved: true },
{ type: "aiChat", mime: "application/json", title: t("note_types.ai-chat"), reserved: true }
];
/** The maximum age in days for a template to be marked with the "New" badge */
const NEW_TEMPLATE_MAX_AGE = 3;
/** The length of a day in milliseconds. */
const DAY_LENGTH = 1000 * 60 * 60 * 24;
/** The menu item badge used to mark new note types and templates */
const NEW_BADGE: MenuItemBadge = {
title: t("note_types.new-feature"),
className: "new-note-type-badge"
};
/** The menu item badge used to mark note types that are part of a beta feature */
const BETA_BADGE = {
title: t("note_types.beta-feature")
};
const SEPARATOR = { title: "----" };
const creationDateCache = new Map<string, Date>();
let rootCreationDate: Date | undefined;
async function getNoteTypeItems(command?: TreeCommandNames) {
const items: MenuItem<TreeCommandNames>[] = [
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
{ title: t("note_types.code"), command, type: "code", uiIcon: "bx bx-code" },
{ title: t("note_types.saved-search"), command, type: "search", uiIcon: "bx bx-file-find" },
{ title: t("note_types.relation-map"), command, type: "relationMap", uiIcon: "bx bxs-network-chart" },
{ title: t("note_types.note-map"), command, type: "noteMap", uiIcon: "bx bxs-network-chart" },
{ title: t("note_types.render-note"), command, type: "render", uiIcon: "bx bx-extension" },
{ title: t("note_types.book"), command, type: "book", uiIcon: "bx bx-book" },
{ title: t("note_types.mermaid-diagram"), command, type: "mermaid", uiIcon: "bx bx-selection" },
{ title: t("note_types.canvas"), command, type: "canvas", uiIcon: "bx bx-pen" },
{ 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),
...getBlankNoteTypes(command),
...await getBuiltInTemplates(t("note_types.collections"), command, true),
...await getBuiltInTemplates(null, command, false),
...await getUserTemplates(command)
];
return items;
}
function getBlankNoteTypes(command?: TreeCommandNames): MenuItem<TreeCommandNames>[] {
return NOTE_TYPES
.filter((nt) => !nt.reserved && nt.type !== "book")
.map((nt) => {
const menuItem: MenuCommandItem<TreeCommandNames> = {
title: nt.title,
command,
type: nt.type,
uiIcon: "bx " + nt.icon,
badges: []
}
if (nt.isNew) {
menuItem.badges?.push(NEW_BADGE);
}
if (nt.isBeta) {
menuItem.badges?.push(BETA_BADGE);
}
return menuItem;
});
}
async function getUserTemplates(command?: TreeCommandNames) {
const templateNoteIds = await server.get<string[]>("search-templates");
const templateNotes = await froca.getNotes(templateNoteIds);
@@ -37,19 +123,26 @@ async function getUserTemplates(command?: TreeCommandNames) {
const items: MenuItem<TreeCommandNames>[] = [
SEPARATOR
];
for (const templateNote of templateNotes) {
items.push({
const item: MenuItem<TreeCommandNames> = {
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
});
};
if (await isNewTemplate(templateNote.noteId)) {
item.badges = [NEW_BADGE];
}
items.push(item);
}
return items;
}
async function getBuiltInTemplates(command?: TreeCommandNames) {
async function getBuiltInTemplates(title: string | null, command: TreeCommandNames | undefined, filterCollections: boolean) {
const templatesRoot = await froca.getNote("_templates");
if (!templatesRoot) {
console.warn("Unable to find template root.");
@@ -61,21 +154,85 @@ async function getBuiltInTemplates(command?: TreeCommandNames) {
return [];
}
const items: MenuItem<TreeCommandNames>[] = [
SEPARATOR
];
for (const templateNote of childNotes) {
const items: MenuItem<TreeCommandNames>[] = [];
if (title) {
items.push({
title: title,
enabled: false,
uiIcon: "bx bx-empty"
});
} else {
items.push(SEPARATOR);
}
for (const templateNote of childNotes) {
if (templateNote.hasLabel("collection") !== filterCollections) {
continue;
}
const item: MenuItem<TreeCommandNames> = {
title: templateNote.title,
uiIcon: templateNote.getIcon(),
command: command,
type: templateNote.type,
templateNoteId: templateNote.noteId
});
};
if (await isNewTemplate(templateNote.noteId)) {
item.badges = [NEW_BADGE];
}
items.push(item);
}
return items;
}
async function isNewTemplate(templateNoteId) {
if (rootCreationDate === undefined) {
// Retrieve the root note creation date
try {
let rootNoteInfo: any = await server.get("notes/root");
if ("dateCreated" in rootNoteInfo) {
rootCreationDate = new Date(rootNoteInfo.dateCreated);
}
} catch (ex) {
console.error(ex);
}
}
// Try to retrieve the template's creation date from the cache
let creationDate: Date | undefined = creationDateCache.get(templateNoteId);
if (creationDate === undefined) {
// The creation date isn't available in the cache, try to retrieve it from the server
try {
const noteInfo: any = await server.get("notes/" + templateNoteId);
if ("dateCreated" in noteInfo) {
creationDate = new Date(noteInfo.dateCreated);
creationDateCache.set(templateNoteId, creationDate);
}
} catch (ex) {
console.error(ex);
}
}
if (creationDate) {
if (rootCreationDate && creationDate.getTime() - rootCreationDate.getTime() < 30000) {
// Ignore templates created within 30 seconds after the root note is created.
// This is useful to prevent predefined templates from being marked
// as 'New' after setting up a new database.
return false;
}
// Determine the difference in days between now and the template's creation date
const age = (new Date().getTime() - creationDate.getTime()) / DAY_LENGTH;
// Return true if the template is at most NEW_TEMPLATE_MAX_AGE days old
return (age <= NEW_TEMPLATE_MAX_AGE);
} else {
return false;
}
}
export default {
getNoteTypeItems
};
@@ -1,4 +1,4 @@
type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url";
export type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url" | "color";
type Multiplicity = "single" | "multi";
export interface DefinitionObject {
@@ -17,7 +17,7 @@ function parse(value: string) {
for (const token of tokens) {
if (token === "promoted") {
defObj.isPromoted = true;
} else if (["text", "number", "boolean", "date", "datetime", "time", "url"].includes(token)) {
} else if (["text", "number", "boolean", "date", "datetime", "time", "url", "color"].includes(token)) {
defObj.labelType = token as LabelType;
} else if (["single", "multi"].includes(token)) {
defObj.multiplicity = token as Multiplicity;
+323
View File
@@ -0,0 +1,323 @@
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import shortcuts, { keyMatches, matchesShortcut } from "./shortcuts.js";
// Mock utils module
vi.mock("./utils.js", () => ({
default: {
isDesktop: () => true
}
}));
// Mock jQuery globally since it's used in the shortcuts module
const mockElement = {
addEventListener: vi.fn(),
removeEventListener: vi.fn()
};
const mockJQuery = vi.fn(() => [mockElement]);
(mockJQuery as any).length = 1;
mockJQuery[0] = mockElement;
(global as any).$ = mockJQuery as any;
global.document = mockElement as any;
describe("shortcuts", () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
// Clean up any active bindings after each test
shortcuts.removeGlobalShortcut("test-namespace");
});
describe("normalizeShortcut", () => {
it("should normalize shortcut to lowercase and remove whitespace", () => {
expect(shortcuts.normalizeShortcut("Ctrl + A")).toBe("ctrl+a");
expect(shortcuts.normalizeShortcut(" SHIFT + F1 ")).toBe("shift+f1");
expect(shortcuts.normalizeShortcut("Alt+Space")).toBe("alt+space");
});
it("should handle empty or null shortcuts", () => {
expect(shortcuts.normalizeShortcut("")).toBe("");
expect(shortcuts.normalizeShortcut(null as any)).toBe(null);
expect(shortcuts.normalizeShortcut(undefined as any)).toBe(undefined);
});
it("should handle shortcuts with multiple spaces", () => {
expect(shortcuts.normalizeShortcut("Ctrl + Shift + A")).toBe("ctrl+shift+a");
});
it("should warn about malformed shortcuts", () => {
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
shortcuts.normalizeShortcut("ctrl+");
shortcuts.normalizeShortcut("+a");
shortcuts.normalizeShortcut("ctrl++a");
expect(consoleSpy).toHaveBeenCalledTimes(3);
consoleSpy.mockRestore();
});
});
describe("keyMatches", () => {
const createKeyboardEvent = (key: string, code?: string) => ({
key,
code: code || `Key${key.toUpperCase()}`
} as KeyboardEvent);
it("should match regular letter keys using key code", () => {
const event = createKeyboardEvent("a", "KeyA");
expect(keyMatches(event, "a")).toBe(true);
expect(keyMatches(event, "A")).toBe(true);
});
it("should match number keys using digit codes", () => {
const event = createKeyboardEvent("1", "Digit1");
expect(keyMatches(event, "1")).toBe(true);
});
it("should match special keys using key mapping", () => {
expect(keyMatches({ key: "Enter" } as KeyboardEvent, "return")).toBe(true);
expect(keyMatches({ key: "Enter" } as KeyboardEvent, "enter")).toBe(true);
expect(keyMatches({ key: "Delete" } as KeyboardEvent, "del")).toBe(true);
expect(keyMatches({ key: "Escape" } as KeyboardEvent, "esc")).toBe(true);
expect(keyMatches({ key: " " } as KeyboardEvent, "space")).toBe(true);
expect(keyMatches({ key: "ArrowUp" } as KeyboardEvent, "up")).toBe(true);
});
it("should match function keys", () => {
expect(keyMatches({ key: "F1" } as KeyboardEvent, "f1")).toBe(true);
expect(keyMatches({ key: "F12" } as KeyboardEvent, "f12")).toBe(true);
});
it("should handle undefined or null keys", () => {
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(keyMatches({} as KeyboardEvent, null as any)).toBe(false);
expect(keyMatches({} as KeyboardEvent, undefined as any)).toBe(false);
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
});
describe("matchesShortcut", () => {
const createKeyboardEvent = (options: {
key: string;
code?: string;
ctrlKey?: boolean;
altKey?: boolean;
shiftKey?: boolean;
metaKey?: boolean;
}) => ({
key: options.key,
code: options.code || `Key${options.key.toUpperCase()}`,
ctrlKey: options.ctrlKey || false,
altKey: options.altKey || false,
shiftKey: options.shiftKey || false,
metaKey: options.metaKey || false
} as KeyboardEvent);
it("should match simple key shortcuts", () => {
const event = createKeyboardEvent({ key: "a", code: "KeyA" });
expect(matchesShortcut(event, "a")).toBe(true);
});
it("should match shortcuts with modifiers", () => {
const event = createKeyboardEvent({ key: "a", code: "KeyA", ctrlKey: true });
expect(matchesShortcut(event, "ctrl+a")).toBe(true);
const shiftEvent = createKeyboardEvent({ key: "a", code: "KeyA", shiftKey: true });
expect(matchesShortcut(shiftEvent, "shift+a")).toBe(true);
});
it("should match complex modifier combinations", () => {
const event = createKeyboardEvent({
key: "a",
code: "KeyA",
ctrlKey: true,
shiftKey: true
});
expect(matchesShortcut(event, "ctrl+shift+a")).toBe(true);
});
it("should not match when modifiers don't match", () => {
const event = createKeyboardEvent({ key: "a", code: "KeyA", ctrlKey: true });
expect(matchesShortcut(event, "alt+a")).toBe(false);
expect(matchesShortcut(event, "a")).toBe(false);
});
it("should handle alternative modifier names", () => {
const ctrlEvent = createKeyboardEvent({ key: "a", code: "KeyA", ctrlKey: true });
expect(matchesShortcut(ctrlEvent, "control+a")).toBe(true);
const metaEvent = createKeyboardEvent({ key: "a", code: "KeyA", metaKey: true });
expect(matchesShortcut(metaEvent, "cmd+a")).toBe(true);
expect(matchesShortcut(metaEvent, "command+a")).toBe(true);
});
it("should handle empty or invalid shortcuts", () => {
const event = createKeyboardEvent({ key: "a", code: "KeyA" });
expect(matchesShortcut(event, "")).toBe(false);
expect(matchesShortcut(event, null as any)).toBe(false);
});
it("should handle invalid events", () => {
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(matchesShortcut(null as any, "a")).toBe(false);
expect(matchesShortcut({} as KeyboardEvent, "a")).toBe(false);
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
it("should warn about invalid shortcut formats", () => {
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const event = createKeyboardEvent({ key: "a", code: "KeyA" });
matchesShortcut(event, "ctrl+");
matchesShortcut(event, "+");
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
});
describe("bindGlobalShortcut", () => {
it("should bind a global shortcut", () => {
const handler = vi.fn();
shortcuts.bindGlobalShortcut("ctrl+a", handler, "test-namespace");
expect(mockElement.addEventListener).toHaveBeenCalledWith("keydown", expect.any(Function));
});
it("should not bind shortcuts when handler is null", () => {
shortcuts.bindGlobalShortcut("ctrl+a", null, "test-namespace");
expect(mockElement.addEventListener).not.toHaveBeenCalled();
});
it("should remove previous bindings when namespace is reused", () => {
const handler1 = vi.fn();
const handler2 = vi.fn();
shortcuts.bindGlobalShortcut("ctrl+a", handler1, "test-namespace");
expect(mockElement.addEventListener).toHaveBeenCalledTimes(1);
shortcuts.bindGlobalShortcut("ctrl+b", handler2, "test-namespace");
expect(mockElement.removeEventListener).toHaveBeenCalledTimes(1);
expect(mockElement.addEventListener).toHaveBeenCalledTimes(2);
});
});
describe("bindElShortcut", () => {
it("should bind shortcut to specific element", () => {
const mockEl = { addEventListener: vi.fn(), removeEventListener: vi.fn() };
const mockJQueryEl = [mockEl] as any;
mockJQueryEl.length = 1;
const handler = vi.fn();
shortcuts.bindElShortcut(mockJQueryEl, "ctrl+a", handler, "test-namespace");
expect(mockEl.addEventListener).toHaveBeenCalledWith("keydown", expect.any(Function));
});
it("should fall back to document when element is empty", () => {
const emptyJQuery = [] as any;
emptyJQuery.length = 0;
const handler = vi.fn();
shortcuts.bindElShortcut(emptyJQuery, "ctrl+a", handler, "test-namespace");
expect(mockElement.addEventListener).toHaveBeenCalledWith("keydown", expect.any(Function));
});
});
describe("removeGlobalShortcut", () => {
it("should remove shortcuts for a specific namespace", () => {
const handler = vi.fn();
shortcuts.bindGlobalShortcut("ctrl+a", handler, "test-namespace");
shortcuts.removeGlobalShortcut("test-namespace");
expect(mockElement.removeEventListener).toHaveBeenCalledWith("keydown", expect.any(Function));
});
});
describe("event handling", () => {
it.skip("should call handler when shortcut matches", () => {
const handler = vi.fn();
shortcuts.bindGlobalShortcut("ctrl+a", handler, "test-namespace");
// Get the listener that was registered
expect(mockElement.addEventListener.mock.calls).toHaveLength(1);
const [, listener] = mockElement.addEventListener.mock.calls[0];
// First verify that matchesShortcut works directly
const testEvent = {
type: "keydown",
key: "a",
code: "KeyA",
ctrlKey: true,
altKey: false,
shiftKey: false,
metaKey: false,
preventDefault: vi.fn(),
stopPropagation: vi.fn()
} as any;
// Test matchesShortcut directly first
expect(matchesShortcut(testEvent, "ctrl+a")).toBe(true);
// Now test the actual listener
listener(testEvent);
expect(handler).toHaveBeenCalled();
expect(testEvent.preventDefault).toHaveBeenCalled();
expect(testEvent.stopPropagation).toHaveBeenCalled();
});
it("should not call handler for non-keyboard events", () => {
const handler = vi.fn();
shortcuts.bindGlobalShortcut("ctrl+a", handler, "test-namespace");
const [, listener] = mockElement.addEventListener.mock.calls[0];
// Simulate a non-keyboard event
const event = {
type: "click"
} as any;
listener(event);
expect(handler).not.toHaveBeenCalled();
});
it("should not call handler when shortcut doesn't match", () => {
const handler = vi.fn();
shortcuts.bindGlobalShortcut("ctrl+a", handler, "test-namespace");
const [, listener] = mockElement.addEventListener.mock.calls[0];
// Simulate a non-matching keydown event
const event = {
type: "keydown",
key: "b",
code: "KeyB",
ctrlKey: true,
altKey: false,
shiftKey: false,
metaKey: false,
preventDefault: vi.fn(),
stopPropagation: vi.fn()
} as any;
listener(event);
expect(handler).not.toHaveBeenCalled();
expect(event.preventDefault).not.toHaveBeenCalled();
});
});
});
+157 -17
View File
@@ -1,7 +1,18 @@
import utils from "./utils.js";
type ElementType = HTMLElement | Document;
type Handler = (e: JQuery.TriggeredEvent<ElementType | Element, string, ElementType | Element, ElementType | Element>) => void;
type Handler = (e: KeyboardEvent) => void;
interface ShortcutBinding {
element: HTMLElement | Document;
shortcut: string;
handler: Handler;
namespace: string | null;
listener: (evt: Event) => void;
}
// Store all active shortcut bindings for management
const activeBindings: Map<string, ShortcutBinding[]> = new Map();
function removeGlobalShortcut(namespace: string) {
bindGlobalShortcut("", null, namespace);
@@ -15,38 +26,167 @@ function bindElShortcut($el: JQuery<ElementType | Element>, keyboardShortcut: st
if (utils.isDesktop()) {
keyboardShortcut = normalizeShortcut(keyboardShortcut);
let eventName = "keydown";
// If namespace is provided, remove all previous bindings for this namespace
if (namespace) {
eventName += `.${namespace}`;
// if there's a namespace, then we replace the existing event handler with the new one
$el.off(eventName);
removeNamespaceBindings(namespace);
}
// method can be called to remove the shortcut (e.g. when keyboardShortcut label is deleted)
if (keyboardShortcut) {
$el.bind(eventName, keyboardShortcut, (e) => {
if (handler) {
handler(e);
// Method can be called to remove the shortcut (e.g. when keyboardShortcut label is deleted)
if (keyboardShortcut && handler) {
const element = $el.length > 0 ? $el[0] as (HTMLElement | Document) : document;
const listener = (evt: Event) => {
// Only handle keyboard events
if (evt.type !== 'keydown' || !(evt instanceof KeyboardEvent)) {
return;
}
e.preventDefault();
e.stopPropagation();
});
const e = evt as KeyboardEvent;
if (matchesShortcut(e, keyboardShortcut)) {
e.preventDefault();
e.stopPropagation();
handler(e);
}
};
// Add the event listener
element.addEventListener('keydown', listener);
// Store the binding for later cleanup
const binding: ShortcutBinding = {
element,
shortcut: keyboardShortcut,
handler,
namespace,
listener
};
const key = namespace || 'global';
if (!activeBindings.has(key)) {
activeBindings.set(key, []);
}
activeBindings.get(key)!.push(binding);
}
}
}
function removeNamespaceBindings(namespace: string) {
const bindings = activeBindings.get(namespace);
if (bindings) {
// Remove all event listeners for this namespace
bindings.forEach(binding => {
binding.element.removeEventListener('keydown', binding.listener);
});
activeBindings.delete(namespace);
}
}
export function matchesShortcut(e: KeyboardEvent, shortcut: string): boolean {
if (!shortcut) return false;
// Ensure we have a proper KeyboardEvent with key property
if (!e || typeof e.key !== 'string') {
console.warn('matchesShortcut called with invalid event:', e);
return false;
}
const parts = shortcut.toLowerCase().split('+');
const key = parts[parts.length - 1]; // Last part is the actual key
const modifiers = parts.slice(0, -1); // Everything before is modifiers
// Defensive check - ensure we have a valid key
if (!key || key.trim() === '') {
console.warn('Invalid shortcut format:', shortcut);
return false;
}
// Check if the main key matches
if (!keyMatches(e, key)) {
return false;
}
// Check modifiers
const expectedCtrl = modifiers.includes('ctrl') || modifiers.includes('control');
const expectedAlt = modifiers.includes('alt');
const expectedShift = modifiers.includes('shift');
const expectedMeta = modifiers.includes('meta') || modifiers.includes('cmd') || modifiers.includes('command');
return e.ctrlKey === expectedCtrl &&
e.altKey === expectedAlt &&
e.shiftKey === expectedShift &&
e.metaKey === expectedMeta;
}
export function keyMatches(e: KeyboardEvent, key: string): boolean {
// Defensive check for undefined/null key
if (!key) {
console.warn('keyMatches called with undefined/null key');
return false;
}
// Handle special key mappings and aliases
const keyMap: { [key: string]: string[] } = {
'return': ['Enter'],
'enter': ['Enter'], // alias for return
'del': ['Delete'],
'delete': ['Delete'], // alias for del
'esc': ['Escape'],
'escape': ['Escape'], // alias for esc
'space': [' ', 'Space'],
'tab': ['Tab'],
'backspace': ['Backspace'],
'home': ['Home'],
'end': ['End'],
'pageup': ['PageUp'],
'pagedown': ['PageDown'],
'up': ['ArrowUp'],
'down': ['ArrowDown'],
'left': ['ArrowLeft'],
'right': ['ArrowRight']
};
// Function keys
for (let i = 1; i <= 19; i++) {
keyMap[`f${i}`] = [`F${i}`];
}
const mappedKeys = keyMap[key.toLowerCase()];
if (mappedKeys) {
return mappedKeys.includes(e.key) || mappedKeys.includes(e.code);
}
// For number keys, use the physical key code regardless of modifiers
// This works across all keyboard layouts
if (key >= '0' && key <= '9') {
return e.code === `Digit${key}`;
}
// For letter keys, use the physical key code for consistency
if (key.length === 1 && key >= 'a' && key <= 'z') {
return e.code === `Key${key.toUpperCase()}`;
}
// For regular keys, check both key and code as fallback
return e.key.toLowerCase() === key.toLowerCase() ||
e.code.toLowerCase() === key.toLowerCase();
}
/**
* Normalize to the form expected by the jquery.hotkeys.js
* Simple normalization - just lowercase and trim whitespace
*/
function normalizeShortcut(shortcut: string): string {
if (!shortcut) {
return shortcut;
}
return shortcut.toLowerCase().replace("enter", "return").replace("delete", "del").replace("ctrl+alt", "alt+ctrl").replace("meta+alt", "alt+meta"); // alt needs to be first;
const normalized = shortcut.toLowerCase().trim().replace(/\s+/g, '');
// Warn about potentially problematic shortcuts
if (normalized.endsWith('+') || normalized.startsWith('+') || normalized.includes('++')) {
console.warn('Potentially malformed shortcut:', shortcut, '-> normalized to:', normalized);
}
return normalized;
}
export default {
@@ -51,6 +51,14 @@ export default class SpacedUpdate {
this.lastUpdated = Date.now();
}
/**
* Sets the update interval for the spaced update.
* @param interval The update interval in milliseconds.
*/
setUpdateInterval(interval: number) {
this.updateInterval = interval;
}
triggerUpdate() {
if (!this.changed) {
return;
+3 -1
View File
@@ -36,7 +36,9 @@ 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", () => {
.on("click", (e) => {
e.stopPropagation();
if (!isShare) {
copyTextWithToast($codeBlock.text());
} else {
-1
View File
@@ -1,5 +1,4 @@
import "jquery";
import "jquery-hotkeys";
import utils from "./services/utils.js";
import ko from "knockout";
import "./stylesheets/bootstrap.scss";
+19 -2
View File
@@ -29,6 +29,14 @@ async function formatCodeBlocks() {
await formatCodeBlocks($("#content"));
}
async function setupTextNote() {
formatCodeBlocks();
applyMath();
const setupMermaid = (await import("./share/mermaid.js")).default;
setupMermaid();
}
/**
* Fetch note with given ID from backend
*
@@ -47,8 +55,11 @@ async function fetchNote(noteId: string | null = null) {
document.addEventListener(
"DOMContentLoaded",
() => {
formatCodeBlocks();
applyMath();
const noteType = determineNoteType();
if (noteType === "text") {
setupTextNote();
}
const toggleMenuButton = document.getElementById("toggleMenuButton");
const layout = document.getElementById("layout");
@@ -60,6 +71,12 @@ document.addEventListener(
false
);
function determineNoteType() {
const bodyClass = document.body.className;
const match = bodyClass.match(/type-([^\s]+)/);
return match ? match[1] : null;
}
// workaround to prevent webpack from removing "fetchNote" as dead code:
// add fetchNote as property to the window object
Object.defineProperty(window, "fetchNote", {
+17
View File
@@ -0,0 +1,17 @@
import mermaid from "mermaid";
export default function setupMermaid() {
for (const codeBlock of document.querySelectorAll("#content pre code.language-mermaid")) {
const parentPre = codeBlock.parentElement;
if (!parentPre) {
continue;
}
const mermaidDiv = document.createElement("div");
mermaidDiv.classList.add("mermaid");
mermaidDiv.innerHTML = codeBlock.innerHTML;
parentPre.replaceWith(mermaidDiv);
}
mermaid.init();
}
@@ -81,8 +81,8 @@ body {
/* -- Overrides the default colors used by the ckeditor5-image package. --------------------- */
--ck-color-image-caption-background: var(--main-background-color);
--ck-color-image-caption-text: var(--main-text-color);
--ck-content-color-image-caption-background: var(--main-background-color);
--ck-content-color-image-caption-text: var(--main-text-color);
/* -- Overrides the default colors used by the ckeditor5-widget package. -------------------- */
+5
View File
@@ -320,3 +320,8 @@ h6 {
page-break-after: avoid;
break-after: avoid;
}
figure.table {
/* Workaround for https://github.com/ckeditor/ckeditor5/issues/18903. Remove once official fix is released */
display: table !important;
}
+91 -14
View File
@@ -139,12 +139,6 @@ textarea,
color: var(--muted-text-color);
}
/* Restore default apperance */
input[type="number"],
input[type="checkbox"] {
appearance: auto !important;
}
/* Add a gap between consecutive radios / check boxes */
label.tn-radio + label.tn-radio,
label.tn-checkbox + label.tn-checkbox {
@@ -192,6 +186,13 @@ samp {
font-family: var(--monospace-font-family) !important;
}
.badge {
--bs-badge-color: var(--muted-text-color);
margin-left: 8px;
background: var(--accented-background-color);
}
.input-group-text {
background-color: var(--accented-background-color) !important;
color: var(--muted-text-color) !important;
@@ -320,7 +321,8 @@ button kbd {
}
}
.dropdown-menu {
.dropdown-menu,
.tabulator-popup-container {
color: var(--menu-text-color) !important;
font-size: inherit;
background-color: var(--menu-background-color) !important;
@@ -330,7 +332,13 @@ button kbd {
--bs-dropdown-link-active-bg: var(--active-item-background-color) !important;
}
body.desktop .dropdown-menu {
.dropdown-menu .dropdown-divider {
break-before: avoid;
break-after: avoid;
}
body.desktop .dropdown-menu,
body.desktop .tabulator-popup-container {
border: 1px solid var(--dropdown-border-color);
box-shadow: 0px 10px 20px rgba(0, 0, 0, var(--dropdown-shadow-opacity));
animation: dropdown-menu-opening 100ms ease-in;
@@ -373,7 +381,8 @@ body.desktop .dropdown-menu {
}
.dropdown-menu a:hover:not(.disabled),
.dropdown-item:hover:not(.disabled, .dropdown-item-container) {
.dropdown-item:hover:not(.disabled, .dropdown-item-container),
.tabulator-menu-item:hover {
color: var(--hover-item-text-color) !important;
background-color: var(--hover-item-background-color) !important;
border-color: var(--hover-item-border-color) !important;
@@ -528,6 +537,7 @@ button.btn-sm {
/* Making this narrower because https://github.com/zadam/trilium/issues/502 (problem only in smaller font sizes) */
min-width: 0;
padding: 0;
z-index: 1000;
}
pre:not(.hljs) {
@@ -759,6 +769,14 @@ table.promoted-attributes-in-tooltip th {
font-size: small;
}
.note-tooltip-content .open-popup-button {
position: absolute;
right: 15px;
bottom: 8px;
font-size: 1.2em;
color: inherit;
}
.note-tooltip-attributes {
display: -webkit-box;
-webkit-box-orient: vertical;
@@ -900,6 +918,13 @@ div[data-notify="container"] {
font-family: var(--monospace-font-family);
}
.ck-content {
--ck-content-font-family: var(--detail-font-family);
--ck-content-font-size: 1.1em;
--ck-content-font-color: var(--main-text-color);
--ck-content-line-height: var(--bs-body-line-height);
}
.ck-content .table table th {
background-color: var(--accented-background-color);
}
@@ -1186,12 +1211,14 @@ body.mobile .dropdown-submenu > .dropdown-menu {
}
#context-menu-container,
#context-menu-container .dropdown-menu {
padding: 3px 0 0;
#context-menu-container .dropdown-menu,
.tabulator-popup-container {
padding: 3px 0;
z-index: 2000;
}
#context-menu-container .dropdown-item {
#context-menu-container .dropdown-item,
.tabulator-menu .tabulator-menu-item {
padding: 0 7px 0 10px;
cursor: pointer;
user-select: none;
@@ -1753,6 +1780,54 @@ textarea {
padding: 1rem;
}
/* Command palette styling */
.jump-to-note-dialog .command-suggestion {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.9em;
}
.jump-to-note-dialog .aa-suggestion .command-suggestion,
.jump-to-note-dialog .aa-suggestion .command-suggestion div {
padding: 0;
}
.jump-to-note-dialog .aa-cursor .command-suggestion,
.jump-to-note-dialog .aa-suggestion:hover .command-suggestion {
border-left-color: var(--link-color);
background-color: var(--hover-background-color);
}
.jump-to-note-dialog .command-icon {
color: var(--muted-text-color);
font-size: 1.125rem;
flex-shrink: 0;
margin-top: 0.125rem;
}
.jump-to-note-dialog .command-content {
flex-grow: 1;
min-width: 0;
}
.jump-to-note-dialog .command-name {
font-weight: bold;
}
.jump-to-note-dialog .command-description {
font-size: 0.8em;
line-height: 1.3;
opacity: 0.75;
}
.jump-to-note-dialog kbd.command-shortcut {
background-color: transparent;
color: inherit;
opacity: 0.75;
font-family: inherit !important;
}
.empty-table-placeholder {
text-align: center;
color: var(--muted-text-color);
@@ -1862,12 +1937,14 @@ body.zen .note-title-widget input {
/* Content renderer */
footer.file-footer {
footer.file-footer,
footer.webview-footer {
display: flex;
justify-content: center;
}
footer.file-footer button {
footer.file-footer button,
footer.webview-footer button {
margin: 5px;
}
+199
View File
@@ -0,0 +1,199 @@
.tabulator {
--table-background-color: var(--main-background-color);
--col-header-background-color: var(--main-background-color);
--col-header-hover-background-color: var(--accented-background-color);
--col-header-text-color: var(--main-text-color);
--col-header-arrow-active-color: var(--main-text-color);
--col-header-arrow-inactive-color: var(--more-accented-background-color);
--col-header-separator-border: none;
--col-header-bottom-border: 2px solid var(--main-border-color);
--row-background-color: var(--main-background-color);
--row-alternate-background-color: var(--main-background-color);
--row-moving-background-color: var(--accented-background-color);
--row-text-color: var(--main-text-color);
--row-delimiter-color: var(--more-accented-background-color);
--cell-horiz-padding-size: 8px;
--cell-vert-padding-size: 8px;
--cell-editable-hover-outline-color: var(--main-border-color);
--cell-read-only-text-color: var(--muted-text-color);
--cell-editing-border-color: var(--main-border-color);
--cell-editing-border-width: 2px;
--cell-editing-background-color: var(--ck-color-selector-focused-cell-background);
--cell-editing-text-color: initial;
background: unset;
border: unset;
}
.tabulator .tabulator-tableholder .tabulator-table {
background: var(--table-background-color);
}
/* Column headers */
.tabulator div.tabulator-header {
border-bottom: var(--col-header-bottom-border);
background: var(--col-header-background-color);
color: var(--col-header-text-color);
}
.tabulator .tabulator-col-content {
padding: 8px 4px !important;
}
@media (hover: hover) and (pointer: fine) {
.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover {
background-color: var(--col-header-hover-background-color);
}
}
.tabulator div.tabulator-header .tabulator-col.tabulator-moving {
border: none;
background: var(--col-header-hover-background-color);
}
.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow {
border-bottom-color: var(--col-header-arrow-active-color);
border-top-color: var(--col-header-arrow-active-color);
}
.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="none"] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow {
border-bottom-color: var(--col-header-arrow-inactive-color);
}
.tabulator div.tabulator-header .tabulator-frozen.tabulator-frozen-left {
margin-left: var(--cell-editing-border-width);
}
.tabulator div.tabulator-header .tabulator-col,
.tabulator div.tabulator-header .tabulator-frozen.tabulator-frozen-left {
background: var(--col-header-background-color);
border-right: var(--col-header-separator-border);
}
/* Table body */
.tabulator-tableholder {
padding-top: 10px;
height: unset !important; /* Don't extend on the full height */
}
/* Rows */
.tabulator-row .tabulator-cell {
padding: var(--cell-vert-padding-size) var(--cell-horiz-padding-size);
}
.tabulator-row .tabulator-cell input {
padding-left: var(--cell-horiz-padding-size) !important;
padding-right: var(--cell-horiz-padding-size) !important;
}
.tabulator-row {
background: transparent;
border-top: none;
border-bottom: 1px solid var(--row-delimiter-color);
color: var(--row-text-color);
}
.tabulator-row.tabulator-row-odd {
background: var(--row-background-color);
}
.tabulator-row.tabulator-row-even {
background: var(--row-alternate-background-color);
}
.tabulator-row.tabulator-moving {
border-color: transparent;
background-color: var(--row-moving-background-color);
}
/* Cell */
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left {
margin-right: var(--cell-editing-border-width);
}
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left,
.tabulator-row .tabulator-cell {
border-right-color: transparent;
}
.tabulator-row .tabulator-cell:not(.tabulator-editable) {
color: var(--cell-read-only-text-color);
}
.tabulator:not(.tabulator-editing) .tabulator-row .tabulator-cell.tabulator-editable:hover {
outline: 2px solid var(--cell-editable-hover-outline-color);
outline-offset: -1px;
}
.tabulator-row .tabulator-cell.tabulator-editing {
border-color: transparent;
}
.tabulator-row:not(.tabulator-moving) .tabulator-cell.tabulator-editing {
outline: calc(var(--cell-editing-border-width) - 1px) solid var(--cell-editing-border-color);
border-color: var(--cell-editing-border-color);
background: var(--cell-editing-background-color);
}
.tabulator-row:not(.tabulator-moving) .tabulator-cell.tabulator-editing > * {
color: var(--cell-editing-text-color);
}
.tabulator .tree-collapse,
.tabulator .tree-expand {
color: var(--row-text-color);
}
/* Align items without children/expander to the ones with. */
.tabulator-cell[tabulator-field="title"] > span:first-child, /* 1st level */
.tabulator-cell[tabulator-field="title"] > div:first-child + span { /* sub-level */
padding-left: 21px;
}
/* Checkbox cells */
.tabulator .tabulator-cell:has(svg),
.tabulator .tabulator-cell:has(input[type="checkbox"]) {
padding-left: 8px;
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.tabulator .tabulator-cell input[type="checkbox"] {
margin: 0;
}
.tabulator .tabulator-footer {
color: var(--main-text-color);
}
/* Context menus */
.tabulator-popup-container {
min-width: 10em;
border-radius: var(--bs-border-radius);
}
.tabulator-menu .tabulator-menu-item {
border: 1px solid transparent;
color: var(--menu-text-color);
font-size: 16px;
}
/* Footer */
:root .tabulator .tabulator-footer {
border-top: unset;
padding: 10px 0;
}
@@ -178,6 +178,9 @@
--alert-bar-background: #6b6b6b3b;
--badge-background-color: #ffffff1a;
--badge-text-color: var(--muted-text-color);
--promoted-attribute-card-background-color: var(--card-background-color);
--promoted-attribute-card-shadow-color: #000000b3;
@@ -171,6 +171,9 @@
--alert-bar-background: #32637b29;
--badge-background-color: #00000011;
--badge-text-color: var(--muted-text-color);
--promoted-attribute-card-background-color: var(--card-background-color);
--promoted-attribute-card-shadow-color: #00000033;
@@ -4,6 +4,7 @@
@import url(./pages.css);
@import url(./ribbon.css);
@import url(./notes/text.css);
@import url(./notes/collections/table.css);
@font-face {
font-family: "Inter";
@@ -171,9 +172,19 @@ html body .dropdown-item[disabled] {
opacity: var(--menu-item-disabled-opacity);
}
/* Badges */
:root .badge {
--bs-badge-color: var(--badge-text-color);
--bs-badge-font-weight: 500;
background: var(--badge-background-color);
text-transform: uppercase;
letter-spacing: .2pt;
}
/* Menu item icon */
.dropdown-item .bx {
transform: translateY(var(--menu-item-icon-vert-offset));
translate: 0 var(--menu-item-icon-vert-offset);
color: var(--menu-item-icon-color) !important;
font-size: 1.1em;
}
@@ -447,6 +458,11 @@ body.mobile .dropdown-menu .dropdown-item.submenu-open .dropdown-toggle::after {
padding: 1rem;
}
.note-list-wrapper .note-book-card .note-book-content.type-image .rendered-content,
.note-list-wrapper .note-book-card .note-book-content.type-pdf .rendered-content {
padding: 0;
}
.note-list-wrapper .note-book-card .note-book-content .rendered-content.text-with-ellipsis {
padding: 1rem !important;
}
@@ -128,10 +128,15 @@ div.tn-tool-dialog {
.jump-to-note-dialog .modal-header {
padding: unset !important;
padding-bottom: 26px !important;
}
.jump-to-note-dialog .modal-body {
padding: 26px 0 !important;
padding: 0 !important;
}
.jump-to-note-dialog .modal-footer {
padding-top: 26px;
}
/* Search box wrapper */
@@ -382,6 +387,10 @@ div.tn-tool-dialog {
/* DELETE NOTE PREVIEW DIALOG */
.delete-notes-dialog .modal-dialog {
--bs-modal-width: fit-content;
}
.delete-notes-list .note-path {
padding-left: 8px;
}
@@ -0,0 +1,13 @@
:root .tabulator {
--col-header-hover-background-color: var(--hover-item-background-color);
--col-header-arrow-active-color: var(--active-item-text-color);
--col-header-arrow-inactive-color: var(--main-border-color);
--row-moving-background-color: var(--more-accented-background-color);
--cell-editable-hover-outline-color: var(--input-focus-outline-color);
--cell-editing-border-color: var(--input-focus-outline-color);
--cell-editing-background-color: var(--input-background-color);
--cell-editing-text-color: var(--input-text-color);
}
@@ -46,6 +46,12 @@ div.promoted-attributes-container {
.image-properties > div:first-child > span > strong {
opacity: 0.65;
font-weight: 500;
vertical-align: top;
}
.note-info-widget-table td,
.file-properties-widget .file-table td {
vertical-align: top;
}
.file-properties-widget {
@@ -71,12 +71,13 @@ body.background-effects.platform-win32.layout-vertical #vertical-main-container
/* #endregion */
/* Matches when the left pane is collapsed */
:has(.layout-vertical #left-pane.hidden-int) {
#horizontal-main-container.left-pane-hidden {
--center-pane-border-radius: 0;
--tab-first-item-horiz-offset: 5px;
}
:has(#left-pane.hidden-int) #launcher-pane.vertical {
/* Add a border to the vertical launch bar if collapsed. */
body.layout-vertical #horizontal-main-container.left-pane-hidden #launcher-pane.vertical {
border-right: 2px solid var(--left-pane-collapsed-border-color);
}
@@ -1300,9 +1301,9 @@ div.promoted-attribute-cell .tn-checkbox {
height: 1cap;
}
/* The <div> containing the checkbox for a promoted boolean attribute */
div.promoted-attribute-cell div:has(input[type="checkbox"]) {
order: -1; /* Relocate the checkbox before the label */
/* Relocate the checkbox before the label */
div.promoted-attribute-cell.promoted-attribute-label-boolean > div:first-of-type {
order: -1;
margin-right: 1.5em;
}
@@ -1677,4 +1678,42 @@ div.find-replace-widget div.find-widget-found-wrapper > span {
#right-pane .highlights-list li:active {
background: transparent;
transition: none;
}
/** Canvas **/
.excalidraw {
--border-radius-lg: 6px;
}
.excalidraw .Island {
backdrop-filter: var(--dropdown-backdrop-filter);
}
.excalidraw .Island.App-toolbar {
--island-bg-color: var(--floating-button-background-color);
--shadow-island: 1px 1px 1px var(--floating-button-shadow-color);
}
.excalidraw .dropdown-menu {
border: unset !important;
box-shadow: unset !important;
background-color: transparent !important;
--island-bg-color: var(--menu-background-color);
--shadow-island: 0px 10px 20px rgba(0, 0, 0, var(--dropdown-shadow-opacity));
--default-border-color: var(--bs-dropdown-divider-bg);
--button-hover-bg: var(--hover-item-background-color);
}
.excalidraw .dropdown-menu .dropdown-menu-container {
border-radius: var(--dropdown-border-radius);
}
.excalidraw .dropdown-menu .dropdown-menu-container > div:not([class]):not(:last-child) {
margin-left: calc(var(--padding) * var(--space-factor) * -1) !important;
margin-right: calc(var(--padding) * var(--space-factor) * -1) !important;
}
.excalidraw .dropdown-menu:before {
content: unset !important;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -211,7 +211,7 @@
"okButton": "OK"
},
"jump_to_note": {
"search_placeholder": "search for note by its name",
"search_placeholder": "Search for note by its name or type > for commands...",
"close": "Close",
"search_button": "Search in full text <kbd>Ctrl+Enter</kbd>"
},
@@ -443,7 +443,8 @@
"other_notes_with_name": "Other notes with {{attributeType}} name \"{{attributeName}}\"",
"and_more": "... and {{count}} more.",
"print_landscape": "When exporting to PDF, changes the orientation of the page to landscape instead of portrait.",
"print_page_size": "When exporting to PDF, changes the size of the page. Supported values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>."
"print_page_size": "When exporting to PDF, changes the size of the page. Supported values: <code>A0</code>, <code>A1</code>, <code>A2</code>, <code>A3</code>, <code>A4</code>, <code>A5</code>, <code>A6</code>, <code>Legal</code>, <code>Letter</code>, <code>Tabloid</code>, <code>Ledger</code>.",
"color_type": "Color"
},
"attribute_editor": {
"help_text_body1": "To add label, just type e.g. <code>#rock</code> or if you want to add also value then e.g. <code>#year = 2020</code>",
@@ -758,9 +759,12 @@
"expand_all_children": "Expand all children",
"collapse": "Collapse",
"expand": "Expand",
"book_properties": "Book Properties",
"book_properties": "Collection Properties",
"invalid_view_type": "Invalid view type '{{type}}'",
"calendar": "Calendar"
"calendar": "Calendar",
"table": "Table",
"geo-map": "Geo Map",
"board": "Board"
},
"edited_notes": {
"no_edited_notes_found": "No edited notes on this day yet...",
@@ -837,7 +841,8 @@
"unknown_label_type": "Unknown label type '{{type}}'",
"unknown_attribute_type": "Unknown attribute type '{{type}}'",
"add_new_attribute": "Add new attribute",
"remove_this_attribute": "Remove this attribute"
"remove_this_attribute": "Remove this attribute",
"remove_color": "Remove the color label"
},
"script_executor": {
"query": "Query",
@@ -960,7 +965,7 @@
"no_attachments": "This note has no attachments."
},
"book": {
"no_children_help": "This note of type Book doesn't have any child notes so there's nothing to display. See <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> for details."
"no_children_help": "This collection doesn't have any child notes so there's nothing to display. See <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> for details."
},
"editable_code": {
"placeholder": "Type the content of your code note here..."
@@ -1023,7 +1028,7 @@
"title": "Consistency Checks",
"find_and_fix_button": "Find and fix consistency issues",
"finding_and_fixing_message": "Finding and fixing consistency issues...",
"issues_fixed_message": "Consistency issues should be fixed."
"issues_fixed_message": "Any consistency issue which may have been found is now fixed."
},
"database_anonymization": {
"title": "Database Anonymization",
@@ -1593,12 +1598,13 @@
"move-to": "Move to...",
"paste-into": "Paste into",
"paste-after": "Paste after",
"duplicate-subtree": "Duplicate subtree",
"duplicate": "Duplicate",
"export": "Export",
"import-into-note": "Import into note",
"apply-bulk-actions": "Apply bulk actions",
"converted-to-attachments": "{{count}} notes have been converted to attachments.",
"convert-to-attachment-confirm": "Are you sure you want to convert note selected notes into attachments of their parent notes?"
"convert-to-attachment-confirm": "Are you sure you want to convert note selected notes into attachments of their parent notes?",
"open-in-popup": "Quick edit"
},
"shared_info": {
"shared_publicly": "This note is shared publicly on",
@@ -1612,7 +1618,7 @@
"relation-map": "Relation Map",
"note-map": "Note Map",
"render-note": "Render Note",
"book": "Book",
"book": "Collection",
"mermaid-diagram": "Mermaid Diagram",
"canvas": "Canvas",
"web-view": "Web View",
@@ -1626,7 +1632,9 @@
"geo-map": "Geo Map",
"beta-feature": "Beta",
"ai-chat": "AI Chat",
"task-list": "Task List"
"task-list": "Task List",
"new-feature": "New",
"collections": "Collections"
},
"protect_note": {
"toggle-on": "Protect the note",
@@ -1828,7 +1836,8 @@
"link_context_menu": {
"open_note_in_new_tab": "Open note in a new tab",
"open_note_in_new_split": "Open note in a new split",
"open_note_in_new_window": "Open note in a new window"
"open_note_in_new_window": "Open note in a new window",
"open_note_in_popup": "Quick edit"
},
"electron_integration": {
"desktop-application": "Desktop Application",
@@ -1848,7 +1857,8 @@
"full-text-search": "Full text search"
},
"note_tooltip": {
"note-has-been-deleted": "Note has been deleted."
"note-has-been-deleted": "Note has been deleted.",
"quick-edit": "Quick edit"
},
"geo-map": {
"create-child-note-title": "Create a new child note and add it to the map",
@@ -1857,7 +1867,8 @@
},
"geo-map-context": {
"open-location": "Open location",
"remove-from-map": "Remove from map"
"remove-from-map": "Remove from map",
"add-note": "Add a marker at this location"
},
"help-button": {
"title": "Open the relevant help page"
@@ -1933,5 +1944,66 @@
"title": "Features",
"emoji_completion_enabled": "Enable Emoji auto-completion",
"note_completion_enabled": "Enable note auto-completion"
},
"table_view": {
"new-row": "New row",
"new-column": "New column",
"sort-column-by": "Sort by \"{{title}}\"",
"sort-column-ascending": "Ascending",
"sort-column-descending": "Descending",
"sort-column-clear": "Clear sorting",
"hide-column": "Hide column \"{{title}}\"",
"show-hide-columns": "Show/hide columns",
"row-insert-above": "Insert row above",
"row-insert-below": "Insert row below",
"row-insert-child": "Insert child note",
"add-column-to-the-left": "Add column to the left",
"add-column-to-the-right": "Add column to the right",
"edit-column": "Edit column",
"delete_column_confirmation": "Are you sure you want to delete this column? The corresponding attribute will be removed from all notes.",
"delete-column": "Delete column",
"new-column-label": "Label",
"new-column-relation": "Relation"
},
"book_properties_config": {
"hide-weekends": "Hide weekends",
"display-week-numbers": "Display week numbers",
"map-style": "Map style:",
"max-nesting-depth": "Max nesting depth:",
"raster": "Raster",
"vector_light": "Vector (Light)",
"vector_dark": "Vector (Dark)",
"show-scale": "Show scale"
},
"table_context_menu": {
"delete_row": "Delete row"
},
"board_view": {
"delete-note": "Delete Note",
"move-to": "Move to",
"insert-above": "Insert above",
"insert-below": "Insert below",
"delete-column": "Delete column",
"delete-column-confirmation": "Are you sure you want to delete this column? The corresponding attribute will be deleted in the notes under this column as well.",
"new-item": "New item",
"add-column": "Add Column"
},
"command_palette": {
"tree-action-name": "Tree: {{name}}",
"export_note_title": "Export Note",
"export_note_description": "Export current note",
"show_attachments_title": "Show Attachments",
"show_attachments_description": "View note attachments",
"search_notes_title": "Search Notes",
"search_notes_description": "Open advanced search",
"search_subtree_title": "Search in Subtree",
"search_subtree_description": "Search within current subtree",
"search_history_title": "Show Search History",
"search_history_description": "View previous searches",
"configure_launch_bar_title": "Configure Launch Bar",
"configure_launch_bar_description": "Open the launch bar configuration, to add or remove items."
},
"content_renderer": {
"open_externally": "Open externally"
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,7 @@
{
"revisions": {
"delete_button": ""
},
"code_block": {
"theme_none": "Sem destaque de sintaxe",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas escuros"
}
"code_block": {
"theme_none": "Sem destaque de sintaxe",
"theme_group_light": "Temas claros",
"theme_group_dark": "Temas escuros"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,170 @@
{
"about": {
"title": "O Trilium Belеškama",
"close": "Zatvori",
"homepage": "Početna stranica:",
"app_version": "Verzija aplikacije:",
"db_version": "Verzija baze podataka:",
"sync_version": "Verzija sinhronizacije:",
"build_date": "Datum izgradnje:",
"build_revision": "Revizija izgradnje:",
"data_directory": "Direktorijum sa podacima:"
},
"toast": {
"critical-error": {
"title": "Kritična greška",
"message": "Došlo je do kritične greške koja sprečava pokretanje klijentske aplikacije.\n\n{{message}}\n\nOva greška je najverovatnije izazvana neočekivanim problemom prilikom izvršavanja skripte. Pokušajte da pokrenete aplikaciju u bezbednom režimu i da pronađete šta izaziva grešku."
},
"widget-error": {
"title": "Pokretanje vidžeta nije uspelo",
"message-custom": "Prilagođeni viđet sa beleške sa ID-jem \"{{id}}\", nazivom \"{{title}}\" nije uspeo da se pokrene zbog:\n\n{{message}}",
"message-unknown": "Nepoznati vidžet nije mogao da se pokrene zbog:\n\n{{message}}"
},
"bundle-error": {
"title": "Pokretanje prilagođene skripte neuspešno",
"message": "Skripta iz beleške sa ID-jem \"{{id}}\", naslovom \"{{title}}\" nije mogla da se izvrši zbog:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Dodaj link",
"help_on_links": "Pomoć na linkovima",
"close": "Zatvori",
"note": "Beleška",
"search_note": "potražite belešku po njenom imenu",
"link_title_mirrors": "naziv linka preslikava trenutan naziv beleške",
"link_title_arbitrary": "naziv linka se može proizvoljno menjati",
"link_title": "Naziv linka",
"button_add_link": "Dodaj link <kbd>enter</kbd>"
},
"branch_prefix": {
"edit_branch_prefix": "Izmeni prefiks grane",
"help_on_tree_prefix": "Pomoć na prefiksu Drveta",
"close": "Zatvori",
"prefix": "Prefiks: ",
"save": "Sačuvaj",
"branch_prefix_saved": "Prefiks grane je sačuvan."
},
"bulk_actions": {
"bulk_actions": "Grupne akcije",
"close": "Zatvori",
"affected_notes": "Pogođene beleške",
"include_descendants": "Obuhvati potomke izabranih beleški",
"available_actions": "Dostupne akcije",
"chosen_actions": "Izabrane akcije",
"execute_bulk_actions": "Izvrši grupne akcije",
"bulk_actions_executed": "Grupne akcije su uspešno izvršene.",
"none_yet": "Nijedna za sad... dodajte akciju tako što ćete pritisnuti na neku od dostupnih akcija iznad.",
"labels": "Oznake",
"relations": "Odnosi",
"notes": "Beleške",
"other": "Ostalo"
},
"clone_to": {
"clone_notes_to": "Klonirajte beleške u...",
"close": "Zatvori",
"help_on_links": "Pomoć na linkovima",
"notes_to_clone": "Beleške za kloniranje",
"target_parent_note": "Ciljna nadređena beleška",
"search_for_note_by_its_name": "potražite belešku po njenom imenu",
"cloned_note_prefix_title": "Klonirana beleška će biti prikazana u drvetu beleški sa datim prefiksom",
"prefix_optional": "Prefiks (opciono)",
"clone_to_selected_note": "Kloniranje u izabranu belešku <kbd>enter</kbd>",
"no_path_to_clone_to": "Nema putanje za kloniranje.",
"note_cloned": "Beleška \"{{clonedTitle}}\" je klonirana u \"{{targetTitle}}\""
},
"confirm": {
"confirmation": "Potvrda",
"close": "Zatvori",
"cancel": "Otkaži",
"ok": "U redu",
"are_you_sure_remove_note": "Da li ste sigurni da želite da uklonite belešku \"{{title}}\" iz mape odnosa? ",
"if_you_dont_check": "Ako ne izaberete ovo, beleška će biti uklonjena samo sa mape odnosa.",
"also_delete_note": "Takođe obriši belešku"
},
"delete_notes": {
"delete_notes_preview": "Obriši pregled beleške",
"close": "Zatvori",
"delete_all_clones_description": "Obriši i sve klonove (može biti poništeno u skorašnjim izmenama)",
"erase_notes_description": "Normalno (blago) brisanje samo označava beleške kao obrisane i one mogu biti vraćene (u dijalogu skorašnjih izmena) u određenom vremenskom periodu. Biranje ove opcije će momentalno obrisati beleške i ove beleške neće biti moguće vratiti.",
"erase_notes_warning": "Trajno obriši beleške (ne može se opozvati), uključujući sve klonove. Ovo će prisiliti aplikaciju da se ponovo pokrene.",
"notes_to_be_deleted": "Sledeće beleške će biti obrisane ({{- noteCount}})",
"no_note_to_delete": "Nijedna beleška neće biti obrisana (samo klonovi).",
"broken_relations_to_be_deleted": "Sledeći odnosi će biti prekinuti i obrisani ({{- relationCount}})",
"cancel": "Otkaži",
"ok": "U redu",
"deleted_relation_text": "Beleška {{- note}} (za brisanje) je referencirana sa odnosom {{- relation}} koji potiče iz {{- source}}."
},
"export": {
"export_note_title": "Izvezi belešku",
"close": "Zatvori",
"export_type_subtree": "Ova beleška i svi njeni potomci",
"format_html": "HTML - preporučuje se jer čuva formatiranje",
"format_html_zip": "HTML u ZIP arhivi - ovo se preporučuje jer se na taj način čuva celokupno formatiranje.",
"format_markdown": "Markdown - ovo čuva većinu formatiranja.",
"format_opml": "OPML - format za razmenu okvira samo za tekst. Formatiranje, slike i datoteke nisu uključeni.",
"opml_version_1": "OPML v1.0 - samo običan tekst",
"opml_version_2": "OPML v2.0 - dozvoljava i HTML",
"export_type_single": "Samo ovu belešku bez njenih potomaka",
"export": "Izvoz",
"choose_export_type": "Molimo vas da prvo izaberete tip izvoza",
"export_status": "Status izvoza",
"export_in_progress": "Izvoz u toku: {{progressCount}}",
"export_finished_successfully": "Izvoz je uspešno završen.",
"format_pdf": "PDF - za namene štampanja ili deljenja."
},
"help": {
"fullDocumentation": "Pomoć (puna dokumentacija je dostupna <a class=\"external\" href=\"https://triliumnext.github.io/Docs/\">online</a>)",
"close": "Zatvori",
"noteNavigation": "Navigacija beleški",
"goUpDown": "<kbd>UP</kbd>, <kbd>DOWN</kbd> - kretanje gore/dole u listi sa beleškama",
"collapseExpand": "<kbd>LEFT</kbd>, <kbd>RIGHT</kbd> - sakupi/proširi čvor",
"notSet": "nije podešeno",
"goBackForwards": "idi u nazad/napred kroz istoriju",
"showJumpToNoteDialog": "prikaži <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/note-navigation.html#jump-to-note\">\"Idi na\" dijalog</a>",
"scrollToActiveNote": "skroluj do aktivne beleške",
"jumpToParentNote": "<kbd>Backspace</kbd> - idi do nadređene beleške",
"collapseWholeTree": "sakupi celo drvo beleški",
"collapseSubTree": "sakupi pod-drvo",
"tabShortcuts": "Prečice na karticama",
"newTabNoteLink": "<kbd>Ctrl+click</kbd> - (ili <kbd>middle mouse click</kbd>) na link beleške otvara belešku u novoj kartici",
"newTabWithActivationNoteLink": "<kbd>Ctrl+Shift+click</kbd> - (ili <kbd>Shift+middle mouse click</kbd>) na link beleške otvara i aktivira belešku u novoj kartici",
"onlyInDesktop": "Samo na dektop-u (Electron verzija)",
"openEmptyTab": "otvori praznu karticu",
"closeActiveTab": "zatvori aktivnu karticu",
"activateNextTab": "aktiviraj narednu karticu",
"activatePreviousTab": "aktiviraj prethodnu karticu",
"creatingNotes": "Pravljenje beleški",
"createNoteAfter": "napravi novu belešku nakon aktivne beleške",
"createNoteInto": "napravi novu pod-belešku u aktivnoj belešci",
"editBranchPrefix": "izmeni <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/tree-concepts.html#prefix\">prefiks</a> klona aktivne beleške",
"movingCloningNotes": "Premeštanje / kloniranje beleški",
"moveNoteUpDown": "pomeri belešku gore/dole u listi beleški",
"moveNoteUpHierarchy": "pomeri belešku na gore u hijerarhiji",
"multiSelectNote": "višestruki izbor beleški iznad/ispod",
"selectAllNotes": "izaberi sve beleške u trenutnom nivou",
"selectNote": "<kbd>Shift+click</kbd> - izaberi belešku",
"copyNotes": "kopiraj aktivnu belešku (ili trenutni izbor) u privremenu memoriju (koristi se za <a class=\"external\" href=\"https://triliumnext.github.io/Docs/Wiki/cloning-notes.html#cloning-notes\">kloniranje</a>)",
"cutNotes": "iseci trenutnu belešku (ili trenutni izbor) u privremenu memoriju (koristi se za premeštanje beleški)",
"pasteNotes": "nalepi belešku/e kao podbelešku u aktivnoj belešci (koja se ili premešta ili klonira u zavisnosti od toga da li je beleška kopirana ili isečena u privremenu memoriju)",
"deleteNotes": "obriši belešku / podstablo",
"editingNotes": "Izmena beleški",
"editNoteTitle": "u ravni drveta će se prebaciti sa ravni drveta na naslov beleške. Ulaz sa naslova beleške će prebaciti fokus na uređivač teksta. <kbd>Ctrl+.</kbd> će se vratiti sa uređivača na ravan drveta.",
"createEditLink": "<kbd>Ctrl+K</kbd> - napravi / izmeni spoljašnji link",
"createInternalLink": "napravi unutrašnji link",
"followLink": "prati link ispod kursora",
"insertDateTime": "ubaci trenutan datum i vreme na poziciju kursora",
"jumpToTreePane": "idi na ravan stabla i pomeri se do aktivne beleške",
"markdownAutoformat": "Autoformatiranje kao u Markdown-u",
"headings": "<code>##</code>, <code>###</code>, <code>####</code> itd. praćeno razmakom za naslove",
"bulletList": "<code>*</code> ili <code>-</code> praćeno razmakom za listu sa tačkama",
"numberedList": "<code>1.</code> ili <code>1)</code> praćeno razmakom za numerisanu listu",
"blockQuote": "započnite liniju sa <code>></code> praćeno sa razmakom za blok citat",
"troubleshooting": "Rešavanje problema",
"reloadFrontend": "ponovo učitaj Trilium frontend",
"showDevTools": "prikaži alate za programere",
"showSQLConsole": "prikaži SQL konzolu",
"other": "Ostalo",
"quickSearch": "fokus na unos za brzu pretragu",
"inPageSearch": "pretraga unutar stranice"
}
}
File diff suppressed because it is too large Load Diff
+5
View File
@@ -3,6 +3,11 @@ declare module "*.png" {
export default path;
}
declare module "*.json" {
var content: any;
export default content;
}
declare module "*?url" {
var path: string;
export default path;
-10
View File
@@ -97,16 +97,6 @@ declare global {
setNote(noteId: string);
}
interface JQueryStatic {
hotkeys: {
options: {
filterInputAcceptingElements: boolean;
filterContentEditable: boolean;
filterTextInputs: boolean;
}
}
}
var logError: (message: string, e?: Error | string) => void;
var logInfo: (message: string) => void;
var glob: CustomGlobals;
@@ -78,7 +78,7 @@ const TPL = /*html*/`
}
</style>
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 8px;">
<h5 class="attr-detail-title">${t("attribute_detail.attr_detail_title")}</h5>
<span class="bx bx-x close-attr-detail-button tn-tool-button" title="${t("attribute_detail.close_button_title")}"></span>
@@ -142,6 +142,7 @@ const TPL = /*html*/`
<option value="datetime">${t("attribute_detail.date_time")}</option>
<option value="time">${t("attribute_detail.time")}</option>
<option value="url">${t("attribute_detail.url")}</option>
<option value="color">${t("attribute_detail.color_type")}</option>
</select>
</td>
</tr>
@@ -295,6 +296,8 @@ interface AttributeDetailOpts {
x: number;
y: number;
focus?: "name";
parent?: HTMLElement;
hideMultiplicity?: boolean;
}
interface SearchRelatedResponse {
@@ -477,7 +480,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
});
}
async showAttributeDetail({ allAttributes, attribute, isOwned, x, y, focus }: AttributeDetailOpts) {
async showAttributeDetail({ allAttributes, attribute, isOwned, x, y, focus, hideMultiplicity }: AttributeDetailOpts) {
if (!attribute) {
this.hide();
@@ -528,7 +531,7 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.$rowPromotedAlias.toggle(!!definition.isPromoted);
this.$inputPromotedAlias.val(definition.promotedAlias || "").attr("disabled", disabledFn);
this.$rowMultiplicity.toggle(["label-definition", "relation-definition"].includes(this.attrType || ""));
this.$rowMultiplicity.toggle(["label-definition", "relation-definition"].includes(this.attrType || "") && !hideMultiplicity);
this.$inputMultiplicity.val(definition.multiplicity || "").attr("disabled", disabledFn);
this.$rowLabelType.toggle(this.attrType === "label-definition");
@@ -560,19 +563,22 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
this.toggleInt(true);
const offset = this.parent?.$widget.offset() || { top: 0, left: 0 };
const offset = this.parent?.$widget?.offset() || { top: 0, left: 0 };
const detPosition = this.getDetailPosition(x, offset);
const outerHeight = this.$widget.outerHeight();
const height = $(window).height();
if (detPosition && outerHeight && height) {
this.$widget
.css("left", detPosition.left)
.css("right", detPosition.right)
.css("top", y - offset.top + 70)
.css("max-height", outerHeight + y > height - 50 ? height - y - 50 : 10000);
if (!detPosition || !outerHeight || !height) {
console.warn("Can't position popup, is it attached?");
return;
}
this.$widget
.css("left", detPosition.left)
.css("right", detPosition.right)
.css("top", y - offset.top + 70)
.css("max-height", outerHeight + y > height - 50 ? height - y - 50 : 10000);
if (focus === "name") {
this.$inputName.trigger("focus").trigger("select");
}
@@ -4,7 +4,7 @@ import noteAutocompleteService, { type Suggestion } from "../../services/note_au
import server from "../../services/server.js";
import contextMenuService from "../../menus/context_menu.js";
import attributeParser, { type Attribute } from "../../services/attribute_parser.js";
import { AttributeEditor, type EditorConfig, type Element, type MentionFeed, type Node, type Position } from "@triliumnext/ckeditor5";
import { AttributeEditor, type EditorConfig, type ModelElement, type MentionFeed, type ModelNode, type ModelPosition } from "@triliumnext/ckeditor5";
import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js";
@@ -417,16 +417,16 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
this.$editor.tooltip("show");
}
getClickIndex(pos: Position) {
getClickIndex(pos: ModelPosition) {
let clickIndex = pos.offset - (pos.textNode?.startOffset ?? 0);
let curNode: Node | Text | Element | null = pos.textNode;
let curNode: ModelNode | Text | ModelElement | null = pos.textNode;
while (curNode?.previousSibling) {
curNode = curNode.previousSibling;
if ((curNode as Element).name === "reference") {
clickIndex += (curNode.getAttribute("notePath") as string).length + 1;
if ((curNode as ModelElement).name === "reference") {
clickIndex += (curNode.getAttribute("href") as string).length + 1;
} else if ("data" in curNode) {
clickIndex += (curNode.data as string).length;
}
@@ -1,9 +1,10 @@
import { ActionKeyboardShortcut } from "@triliumnext/commons";
import type { CommandNames } from "../../components/app_context.js";
import keyboardActionsService, { type Action } from "../../services/keyboard_actions.js";
import keyboardActionsService from "../../services/keyboard_actions.js";
import AbstractButtonWidget, { type AbstractButtonWidgetSettings } from "./abstract_button.js";
import type { ButtonNoteIdProvider } from "./button_from_note.js";
let actions: Action[];
let actions: ActionKeyboardShortcut[];
keyboardActionsService.getActions().then((as) => (actions = as));
@@ -49,7 +50,7 @@ export default class CommandButtonWidget extends AbstractButtonWidget<CommandBut
const action = actions.find((act) => act.actionName === this._command);
if (action && action.effectiveShortcuts.length > 0) {
if (action?.effectiveShortcuts && action.effectiveShortcuts.length > 0) {
return `${title} (${action.effectiveShortcuts.join(", ")})`;
} else {
return title;
@@ -186,10 +186,10 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment());
this.toggleDisabled(this.$findInTextButton, ["text", "code", "book", "mindMap"].includes(note.type));
this.toggleDisabled(this.$findInTextButton, ["text", "code", "book", "mindMap", "doc"].includes(note.type));
this.toggleDisabled(this.$showAttachmentsButton, !isInOptions);
this.toggleDisabled(this.$showSourceButton, ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "geoMap"].includes(note.type));
this.toggleDisabled(this.$showSourceButton, ["text", "code", "relationMap", "mermaid", "canvas", "mindMap"].includes(note.type));
const canPrint = ["text", "code"].includes(note.type);
this.toggleDisabled(this.$printActiveNoteButton, canPrint);
@@ -1,5 +1,5 @@
import type { default as Component, TypedComponent } from "../../components/component.js";
import BasicWidget, { TypedBasicWidget } from "../basic_widget.js";
import type { TypedComponent } from "../../components/component.js";
import { TypedBasicWidget } from "../basic_widget.js";
export default class Container<T extends TypedComponent<any>> extends TypedBasicWidget<T> {
doRender() {
@@ -5,7 +5,7 @@ import type Component from "../../components/component.js";
export default class LeftPaneContainer extends FlexContainer<Component> {
private currentLeftPaneVisible: boolean;
constructor() {
super("column");
@@ -24,6 +24,7 @@ export default class LeftPaneContainer extends FlexContainer<Component> {
this.currentLeftPaneVisible = leftPaneVisible ?? !this.currentLeftPaneVisible;
const visible = this.isEnabled();
this.toggleInt(visible);
this.parent?.$widget.toggleClass("left-pane-hidden", !visible);
if (visible) {
this.triggerEvent("focusTree", {});
@@ -268,7 +268,7 @@ export default class RibbonContainer extends NoteContextAwareWidget {
const action = actions.find((act) => act.actionName === toggleCommandName);
const title = $(this).attr("data-title");
if (action && action.effectiveShortcuts.length > 0) {
if (action?.effectiveShortcuts && action.effectiveShortcuts.length > 0) {
return `${title} (${action.effectiveShortcuts.join(", ")})`;
} else {
return title ?? "";
+91 -19
View File
@@ -6,6 +6,7 @@ import BasicWidget from "../basic_widget.js";
import shortcutService from "../../services/shortcuts.js";
import { Modal } from "bootstrap";
import { openDialog } from "../../services/dialog.js";
import commandRegistry from "../../services/command_registry.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">
@@ -34,7 +35,8 @@ export default class JumpToNoteDialog extends BasicWidget {
private modal!: bootstrap.Modal;
private $autoComplete!: JQuery<HTMLElement>;
private $results!: JQuery<HTMLElement>;
private $showInFullTextButton!: JQuery<HTMLElement>;
private $modalFooter!: JQuery<HTMLElement>;
private isCommandMode: boolean = false;
constructor() {
super();
@@ -48,13 +50,44 @@ export default class JumpToNoteDialog extends BasicWidget {
this.$autoComplete = this.$widget.find(".jump-to-note-autocomplete");
this.$results = this.$widget.find(".jump-to-note-results");
this.$showInFullTextButton = this.$widget.find(".show-in-full-text-button");
this.$showInFullTextButton.on("click", (e) => this.showInFullText(e));
this.$modalFooter = this.$widget.find(".modal-footer");
this.$modalFooter.find(".show-in-full-text-button").on("click", (e) => this.showInFullText(e));
shortcutService.bindElShortcut(this.$widget, "ctrl+return", (e) => this.showInFullText(e));
// Monitor input changes to detect command mode switches
this.$autoComplete.on("input", () => {
this.updateCommandModeState();
});
}
private updateCommandModeState() {
const currentValue = String(this.$autoComplete.val() || "");
const newCommandMode = currentValue.startsWith(">");
if (newCommandMode !== this.isCommandMode) {
this.isCommandMode = newCommandMode;
this.updateButtonVisibility();
}
}
private updateButtonVisibility() {
if (this.isCommandMode) {
this.$modalFooter.hide();
} else {
this.$modalFooter.show();
}
}
async jumpToNoteEvent() {
await this.openDialog();
}
async commandPaletteEvent() {
await this.openDialog(true);
}
private async openDialog(commandMode = false) {
const dialogPromise = openDialog(this.$widget);
if (utils.isMobile()) {
dialogPromise.then(($dialog) => {
@@ -81,50 +114,89 @@ export default class JumpToNoteDialog extends BasicWidget {
}
// first open dialog, then refresh since refresh is doing focus which should be visible
this.refresh();
this.refresh(commandMode);
this.lastOpenedTs = Date.now();
}
async refresh() {
async refresh(commandMode = false) {
noteAutocompleteService
.initNoteAutocomplete(this.$autoComplete, {
allowCreatingNotes: true,
hideGoToSelectedNoteButton: true,
allowJumpToSearchNotes: true,
container: this.$results[0]
container: this.$results[0],
isCommandPalette: true
})
// clear any event listener added in previous invocation of this function
.off("autocomplete:noteselected")
.off("autocomplete:commandselected")
.on("autocomplete:noteselected", function (event, suggestion, dataset) {
if (!suggestion.notePath) {
return false;
}
appContext.tabManager.getActiveContext()?.setNote(suggestion.notePath);
})
.on("autocomplete:commandselected", async (event, suggestion, dataset) => {
if (!suggestion.commandId) {
return false;
}
this.modal.hide();
await commandRegistry.executeCommand(suggestion.commandId);
});
// if you open the Jump To dialog soon after using it previously, it can often mean that you
// actually want to search for the same thing (e.g., you opened the wrong note at first try)
// so we'll keep the content.
// if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) {
noteAutocompleteService.showRecentNotes(this.$autoComplete);
if (commandMode) {
// Start in command mode - manually trigger command search
this.$autoComplete.autocomplete("val", ">");
this.isCommandMode = true;
this.updateButtonVisibility();
// Manually populate with all commands immediately
noteAutocompleteService.showAllCommands(this.$autoComplete);
this.$autoComplete.trigger("focus");
} else {
this.$autoComplete
// hack, the actual search value is stored in <pre> element next to the search input
// this is important because the search input value is replaced with the suggestion note's title
.autocomplete("val", this.$autoComplete.next().text())
.trigger("focus")
.trigger("select");
// if you open the Jump To dialog soon after using it previously, it can often mean that you
// actually want to search for the same thing (e.g., you opened the wrong note at first try)
// so we'll keep the content.
// if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) {
this.isCommandMode = false;
this.updateButtonVisibility();
noteAutocompleteService.showRecentNotes(this.$autoComplete);
} else {
this.$autoComplete
// hack, the actual search value is stored in <pre> element next to the search input
// this is important because the search input value is replaced with the suggestion note's title
.autocomplete("val", this.$autoComplete.next().text())
.trigger("focus")
.trigger("select");
// Update command mode state based on the restored value
this.updateCommandModeState();
// If we restored a command mode value, manually trigger command display
if (this.isCommandMode) {
// Clear the value first, then set it to ">" to trigger a proper change
this.$autoComplete.autocomplete("val", "");
noteAutocompleteService.showAllCommands(this.$autoComplete);
}
}
}
}
showInFullText(e: JQuery.TriggeredEvent) {
showInFullText(e: JQuery.TriggeredEvent | KeyboardEvent) {
// stop from propagating upwards (dangerous, especially with ctrl+enter executable javascript notes)
e.preventDefault();
e.stopPropagation();
// Don't perform full text search in command mode
if (this.isCommandMode) {
return;
}
const searchString = String(this.$autoComplete.val());
this.triggerCommand("searchNotes", { searchString });
@@ -154,13 +154,21 @@ export default class NoteTypeChooserDialog extends BasicWidget {
this.$noteTypeDropdown.append($('<h6 class="dropdown-header">').append(t("note_type_chooser.templates")));
} else {
const commandItem = noteType as MenuCommandItem<CommandNames>;
this.$noteTypeDropdown.append(
$('<a class="dropdown-item" tabindex="0">')
.attr("data-note-type", commandItem.type || "")
.attr("data-template-note-id", commandItem.templateNoteId || "")
.append($("<span>").addClass(commandItem.uiIcon || ""))
.append(` ${noteType.title}`)
);
const listItem = $('<a class="dropdown-item" tabindex="0">')
.attr("data-note-type", commandItem.type || "")
.attr("data-template-note-id", commandItem.templateNoteId || "")
.append($("<span>").addClass(commandItem.uiIcon || ""))
.append(` ${noteType.title}`);
if (commandItem.badges) {
for (let badge of commandItem.badges) {
listItem.append($(`<span class="badge">`)
.addClass(badge.className || "")
.text(badge.title));
}
}
this.$noteTypeDropdown.append(listItem);
}
}
@@ -0,0 +1,161 @@
import type { EventNames, EventData } from "../../components/app_context.js";
import NoteContext from "../../components/note_context.js";
import { openDialog } from "../../services/dialog.js";
import BasicWidget from "../basic_widget.js";
import Container from "../containers/container.js";
import TypeWidget from "../type_widgets/type_widget.js";
const TPL = /*html*/`\
<div class="popup-editor-dialog modal fade mx-auto" tabindex="-1" role="dialog">
<style>
body.desktop .modal.popup-editor-dialog .modal-dialog {
max-width: 75vw;
}
.modal.popup-editor-dialog .modal-header .modal-title {
font-size: 1.1em;
}
.modal.popup-editor-dialog .modal-body {
padding: 0;
height: 75vh;
overflow: auto;
}
.modal.popup-editor-dialog .note-detail-editable-text {
padding: 0 1em;
}
.modal.popup-editor-dialog .title-row,
.modal.popup-editor-dialog .modal-title,
.modal.popup-editor-dialog .note-icon-widget {
height: 32px;
}
.modal.popup-editor-dialog .note-icon-widget {
width: 32px;
margin: 0;
padding: 0;
}
.modal.popup-editor-dialog .note-icon-widget button.note-icon,
.modal.popup-editor-dialog .note-title-widget input.note-title {
font-size: 1em;
}
.modal.popup-editor-dialog .classic-toolbar-widget {
position: sticky;
top: 0;
left: 0;
right: 0;
background: var(--modal-background-color);
z-index: 998;
}
.modal.popup-editor-dialog .note-detail-file {
padding: 0;
}
</style>
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title">
<!-- This is where the first child will be injected -->
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- This is where all but the first child will be injected. -->
</div>
</div>
</div>
</div>
`;
export default class PopupEditorDialog extends Container<BasicWidget> {
private noteContext: NoteContext;
private $modalHeader!: JQuery<HTMLElement>;
private $modalBody!: JQuery<HTMLElement>;
constructor() {
super();
this.noteContext = new NoteContext("_popup-editor");
}
doRender() {
// This will populate this.$widget with the content of the children.
super.doRender();
// Now we wrap it in the modal.
const $newWidget = $(TPL);
this.$modalHeader = $newWidget.find(".modal-title");
this.$modalBody = $newWidget.find(".modal-body");
const children = this.$widget.children();
this.$modalHeader.append(children[0]);
this.$modalBody.append(children.slice(1));
this.$widget = $newWidget;
this.setVisibility(false);
}
async openInPopupEvent({ noteIdOrPath }: EventData<"openInPopup">) {
const $dialog = await openDialog(this.$widget, false, {
focus: false
});
await this.noteContext.setNote(noteIdOrPath, {
viewScope: {
readOnlyTemporarilyDisabled: true
}
});
const activeEl = document.activeElement;
if (activeEl && "blur" in activeEl) {
(activeEl as HTMLElement).blur();
}
$dialog.on("shown.bs.modal", async () => {
// Reduce the z-index of modals so that ckeditor popups are properly shown on top of it.
// The backdrop instance is not shared so it's OK to make a one-off modification.
$("body > .modal-backdrop").css("z-index", "998");
$dialog.css("z-index", "999");
await this.handleEventInChildren("activeContextChanged", { noteContext: this.noteContext });
this.setVisibility(true);
await this.handleEventInChildren("focusOnDetail", { ntxId: this.noteContext.ntxId });
});
$dialog.on("hidden.bs.modal", () => {
const $typeWidgetEl = $dialog.find(".note-detail-printable");
if ($typeWidgetEl.length) {
const typeWidget = glob.getComponentByEl($typeWidgetEl[0]) as TypeWidget;
typeWidget.cleanup();
}
this.setVisibility(false);
});
}
setVisibility(visible: boolean) {
const $bodyItems = this.$modalBody.find("> div");
if (visible) {
$bodyItems.fadeIn();
this.$modalHeader.children().show();
} else {
$bodyItems.hide();
this.$modalHeader.children().hide();
}
}
handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
// Avoid events related to the current tab interfere with our popup.
if (["noteSwitched", "noteSwitchedAndActivated"].includes(name)) {
return Promise.resolve();
}
return super.handleEventInChildren(name, data);
}
}
@@ -88,7 +88,9 @@ export default class SortChildNotesDialog extends BasicWidget {
this.$widget = $(TPL);
this.$form = this.$widget.find(".sort-child-notes-form");
this.$form.on("submit", async () => {
this.$form.on("submit", async (e) => {
e.preventDefault();
const sortBy = this.$form.find("input[name='sort-by']:checked").val();
const sortDirection = this.$form.find("input[name='sort-direction']:checked").val();
const foldersFirst = this.$form.find("input[name='sort-folders-first']").is(":checked");
+4 -2
View File
@@ -97,6 +97,7 @@ const TPL = /*html*/`
</div>
</div>`;
const SUPPORTED_NOTE_TYPES = ["text", "code", "render", "mindMap", "doc"];
export default class FindWidget extends NoteContextAwareWidget {
private searchTerm: string | null;
@@ -188,7 +189,7 @@ export default class FindWidget extends NoteContextAwareWidget {
return;
}
if (!["text", "code", "render", "mindMap"].includes(this.note?.type ?? "")) {
if (!SUPPORTED_NOTE_TYPES.includes(this.note?.type ?? "")) {
return;
}
@@ -251,6 +252,7 @@ export default class FindWidget extends NoteContextAwareWidget {
const readOnly = await this.noteContext?.isReadOnly();
return readOnly ? this.htmlHandler : this.textHandler;
case "mindMap":
case "doc":
return this.htmlHandler;
default:
console.warn("FindWidget: Unsupported note type for find widget", this.note?.type);
@@ -354,7 +356,7 @@ export default class FindWidget extends NoteContextAwareWidget {
}
isEnabled() {
return super.isEnabled() && ["text", "code", "render", "mindMap"].includes(this.note?.type ?? "");
return super.isEnabled() && SUPPORTED_NOTE_TYPES.includes(this.note?.type ?? "");
}
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
@@ -23,7 +23,9 @@ const TPL = /*html*/`\
export default class GeoMapButtons extends NoteContextAwareWidget {
isEnabled() {
return super.isEnabled() && this.note?.type === "geoMap";
return super.isEnabled()
&& this.note?.getLabelValue("viewType") === "geoMap"
&& !this.note.hasLabel("readOnly");
}
doRender() {
@@ -17,7 +17,6 @@ export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
contentWidget: null,
doc: null,
file: null,
geoMap: "81SGnPGMk7Xc",
image: null,
launcher: null,
mermaid: null,
@@ -32,9 +31,12 @@ export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = {
};
export const byBookType: Record<ViewTypeOptions, string | null> = {
list: null,
grid: null,
calendar: "xWbu3jpNWapp"
list: "mULW0Q3VojwY",
grid: "8QqnMzx393bx",
calendar: "xWbu3jpNWapp",
table: "2FvYrpmOXm29",
geoMap: "81SGnPGMk7Xc",
board: "CtBQqbwXDx1w"
};
export default class ContextualHelpButton extends NoteContextAwareWidget {
@@ -39,10 +39,20 @@ export default class ToggleReadOnlyButton extends OnClickButtonWidget {
}
isEnabled() {
return super.isEnabled()
&& this.note?.type === "mermaid"
&& this.note?.isContentAvailable()
&& this.noteContext?.viewScope?.viewMode === "default";
if (!super.isEnabled()) {
return false;
}
if (!this?.note?.isContentAvailable()) {
return false;
}
if (this.noteContext?.viewScope?.viewMode !== "default") {
return false;
}
return this.note.type === "mermaid" ||
(this.note.getLabelValue("viewType") === "geoMap");
}
}
-58
View File
@@ -1,58 +0,0 @@
import type { Map } from "leaflet";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
const TPL = /*html*/`\
<div class="geo-map-widget">
<style>
.note-detail-geo-map,
.geo-map-widget,
.geo-map-container {
height: 100%;
overflow: hidden;
}
.leaflet-top,
.leaflet-bottom {
z-index: 900;
}
</style>
<div class="geo-map-container"></div>
</div>`;
export type Leaflet = typeof L;
export type InitCallback = (L: Leaflet) => void;
export default class GeoMapWidget extends NoteContextAwareWidget {
map?: Map;
$container!: JQuery<HTMLElement>;
private initCallback?: InitCallback;
constructor(widgetMode: "type", initCallback?: InitCallback) {
super();
this.initCallback = initCallback;
}
doRender() {
this.$widget = $(TPL);
this.$container = this.$widget.find(".geo-map-container");
const map = L.map(this.$container[0], {
worldCopyJump: true
});
this.map = map;
if (this.initCallback) {
this.initCallback(L);
}
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
detectRetina: true
}).addTo(map);
}
}
+1 -3
View File
@@ -28,7 +28,6 @@ import ContentWidgetTypeWidget from "./type_widgets/content_widget.js";
import AttachmentListTypeWidget from "./type_widgets/attachment_list.js";
import AttachmentDetailTypeWidget from "./type_widgets/attachment_detail.js";
import MindMapWidget from "./type_widgets/mind_map.js";
import GeoMapTypeWidget from "./type_widgets/geo_map.js";
import utils from "../services/utils.js";
import type { NoteType } from "../entities/fnote.js";
import type TypeWidget from "./type_widgets/type_widget.js";
@@ -71,7 +70,6 @@ const typeWidgetClasses = {
attachmentDetail: AttachmentDetailTypeWidget,
attachmentList: AttachmentListTypeWidget,
mindMap: MindMapWidget,
geoMap: GeoMapTypeWidget,
aiChat: AiChatTypeWidget,
// Split type editors
@@ -197,7 +195,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
// https://github.com/zadam/trilium/issues/2522
const isBackendNote = this.noteContext?.noteId === "_backendLog";
const isSqlNote = this.mime === "text/x-sqlite;schema=trilium";
const isFullHeightNoteType = ["canvas", "webView", "noteMap", "mindMap", "geoMap", "mermaid"].includes(this.type ?? "");
const isFullHeightNoteType = ["canvas", "webView", "noteMap", "mindMap", "mermaid", "file"].includes(this.type ?? "");
const isFullHeight = (!this.noteContext?.hasNoteList() && isFullHeightNoteType && !isSqlNote)
|| this.noteContext?.viewScope?.viewMode === "attachments"
|| isBackendNote;
+51 -10
View File
@@ -1,7 +1,7 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import NoteListRenderer from "../services/note_list_renderer.js";
import type FNote from "../entities/fnote.js";
import type { CommandListener, CommandListenerData, EventData } from "../components/app_context.js";
import type { CommandListener, CommandListenerData, CommandMappings, CommandNames, EventData, EventNames } from "../components/app_context.js";
import type ViewMode from "./view_widgets/view_mode.js";
const TPL = /*html*/`
@@ -36,10 +36,31 @@ export default class NoteListWidget extends NoteContextAwareWidget {
private isIntersecting?: boolean;
private noteIdRefreshed?: string;
private shownNoteId?: string | null;
private viewMode?: ViewMode | null;
private viewMode?: ViewMode<any> | null;
private displayOnlyCollections: boolean;
/**
* @param displayOnlyCollections if set to `true` then only collection-type views are displayed such as geo-map and the calendar. The original book types grid and list will be ignored.
*/
constructor(displayOnlyCollections: boolean) {
super();
this.displayOnlyCollections = displayOnlyCollections;
}
isEnabled() {
return super.isEnabled() && this.noteContext?.hasNoteList();
if (!super.isEnabled()) {
return false;
}
if (this.displayOnlyCollections && this.note?.type !== "book") {
const viewType = this.note?.getLabelValue("viewType");
if (!viewType || ["grid", "list"].includes(viewType)) {
return false;
}
}
return this.noteContext?.hasNoteList();
}
doRender() {
@@ -76,7 +97,11 @@ export default class NoteListWidget extends NoteContextAwareWidget {
}
async renderNoteList(note: FNote) {
const noteListRenderer = new NoteListRenderer(this.$content, note, note.getChildNoteIds());
const noteListRenderer = new NoteListRenderer({
$parent: this.$content,
parentNote: note,
parentNotePath: this.notePath
});
this.$widget.toggleClass("full-height", noteListRenderer.isFullHeight);
await noteListRenderer.renderList();
this.viewMode = noteListRenderer.viewMode;
@@ -120,12 +145,6 @@ export default class NoteListWidget extends NoteContextAwareWidget {
this.refresh();
this.checkRenderStatus();
}
// Inform the view mode of changes and refresh if needed.
if (this.viewMode && this.viewMode.onEntitiesReloaded(e)) {
this.refresh();
this.checkRenderStatus();
}
}
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
@@ -134,4 +153,26 @@ export default class NoteListWidget extends NoteContextAwareWidget {
}
}
triggerCommand<K extends CommandNames>(name: K, data?: CommandMappings[K]): Promise<unknown> | undefined | null {
// Pass the commands to the view mode, which is not actually attached to the hierarchy.
if (this.viewMode?.triggerCommand(name, data)) {
return;
}
return super.triggerCommand(name, data);
}
handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<unknown[] | unknown> | null {
super.handleEventInChildren(name, data);
if (this.viewMode) {
const ret = this.viewMode.handleEvent(name, data);
if (ret) {
return ret;
}
}
return null;
}
}
+11 -3
View File
@@ -324,7 +324,13 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
}
const mapRootNoteId = this.getMapRootNoteId();
const data = await this.loadNotesAndRelations(mapRootNoteId);
const labelValues = (name: string) => this.note?.getLabels(name).map(l => l.value) ?? [];
const excludeRelations = labelValues("mapExcludeRelation");
const includeRelations = labelValues("mapIncludeRelation");
const data = await this.loadNotesAndRelations(mapRootNoteId, excludeRelations, includeRelations);
const nodeLinkRatio = data.nodes.length / data.links.length;
const magnifiedRatio = Math.pow(nodeLinkRatio, 1.5);
@@ -473,8 +479,10 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
ctx.restore();
}
async loadNotesAndRelations(mapRootNoteId: string): Promise<NotesAndRelationsData> {
const resp = await server.post<PostNotesMapResponse>(`note-map/${mapRootNoteId}/${this.mapType}`);
async loadNotesAndRelations(mapRootNoteId: string, excludeRelations: string[], includeRelations: string[]): Promise<NotesAndRelationsData> {
const resp = await server.post<PostNotesMapResponse>(`note-map/${mapRootNoteId}/${this.mapType}`, {
excludeRelations, includeRelations
});
this.calculateNodeSizes(resp);
+27 -6
View File
@@ -186,6 +186,15 @@ interface RefreshContext {
noteIdsToReload: Set<string>;
}
/**
* The information contained within a drag event.
*/
export interface DragData {
noteId: string;
branchId: string;
title: string;
}
export default class NoteTreeWidget extends NoteContextAwareWidget {
private $tree!: JQuery<HTMLElement>;
private $treeActions!: JQuery<HTMLElement>;
@@ -231,15 +240,21 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.$tree.on("mousedown", ".fancytree-title", (e) => {
if (e.which === 2) {
const node = $.ui.fancytree.getNode(e as unknown as Event);
const notePath = treeService.getNotePath(node);
if (notePath) {
e.stopPropagation();
e.preventDefault();
appContext.tabManager.openTabWithNoteWithHoisting(notePath, {
activate: e.shiftKey ? true : false
});
}
}
});
this.$tree.on("mouseup", ".fancytree-title", (e) => {
// Prevent middle click from pasting in the editor.
if (e.which === 2) {
e.stopPropagation();
e.preventDefault();
}
@@ -698,7 +713,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
});
} else {
this.$tree.on("contextmenu", ".fancytree-node", (e) => {
this.showContextMenu(e);
if (!utils.isCtrlKey(e)) {
this.showContextMenu(e);
} else {
const node = $.ui.fancytree.getNode(e as unknown as Event);
const notePath = treeService.getNotePath(node);
appContext.triggerCommand("openInPopup", { noteIdOrPath: notePath });
}
return false; // blocks default browser right click menu
});
@@ -706,9 +727,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
for (const key in hotKeys) {
const handler = hotKeys[key];
$(this.tree.$container).on("keydown", null, key, (evt) => {
shortcutService.bindElShortcut($(this.tree.$container), key, () => {
const node = this.tree.getActiveNode();
return handler(node, evt);
return handler(node, {} as JQuery.KeyDownEvent);
// return false from the handler will stop default handling.
});
}
@@ -1531,7 +1552,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const hotKeyMap: Record<string, (node: Fancytree.FancytreeNode, e: JQuery.KeyDownEvent) => boolean> = {};
for (const action of actions) {
for (const shortcut of action.effectiveShortcuts) {
for (const shortcut of action.effectiveShortcuts ?? []) {
hotKeyMap[shortcutService.normalizeShortcut(shortcut)] = (node) => {
const notePath = treeService.getNotePath(node);
+14 -60
View File
@@ -1,59 +1,15 @@
import server from "../services/server.js";
import { Dropdown } from "bootstrap";
import { NOTE_TYPES } from "../services/note_types.js";
import { t } from "../services/i18n.js";
import dialogService from "../services/dialog.js";
import mimeTypesService from "../services/mime_types.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import dialogService from "../services/dialog.js";
import { t } from "../services/i18n.js";
import type FNote from "../entities/fnote.js";
import type { NoteType } from "../entities/fnote.js";
import server from "../services/server.js";
import type { EventData } from "../components/app_context.js";
import { Dropdown } from "bootstrap";
import type { NoteType } from "../entities/fnote.js";
import type FNote from "../entities/fnote.js";
interface NoteTypeMapping {
type: NoteType;
mime?: string;
title: string;
isBeta?: boolean;
selectable: boolean;
}
const NOTE_TYPES: NoteTypeMapping[] = [
// The suggested note type ordering method: insert the item into the corresponding group,
// then ensure the items within the group are ordered alphabetically.
// The default note type (always the first item)
{ type: "text", mime: "text/html", title: t("note_types.text"), selectable: true },
// Text notes group
{ type: "book", mime: "", title: t("note_types.book"), selectable: true },
// Graphic notes
{ type: "canvas", mime: "application/json", title: t("note_types.canvas"), selectable: true },
{ type: "mermaid", mime: "text/mermaid", title: t("note_types.mermaid-diagram"), selectable: true },
// Map notes
{ type: "geoMap", mime: "application/json", title: t("note_types.geo-map"), isBeta: true, selectable: true },
{ type: "mindMap", mime: "application/json", title: t("note_types.mind-map"), selectable: true },
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), selectable: true },
// Misc note types
{ type: "render", mime: "", title: t("note_types.render-note"), selectable: true },
{ type: "webView", mime: "", title: t("note_types.web-view"), selectable: true },
// Code notes
{ type: "code", mime: "text/plain", title: t("note_types.code"), selectable: true },
// Reserved types (cannot be created by the user)
{ type: "contentWidget", mime: "", title: t("note_types.widget"), selectable: false },
{ type: "doc", mime: "", title: t("note_types.doc"), selectable: false },
{ type: "file", title: t("note_types.file"), selectable: false },
{ type: "image", title: t("note_types.image"), selectable: false },
{ type: "launcher", mime: "", title: t("note_types.launcher"), selectable: false },
{ type: "noteMap", mime: "", title: t("note_types.note-map"), selectable: false },
{ type: "search", title: t("note_types.saved-search"), selectable: false },
{ type: "aiChat", mime: "application/json", title: t("note_types.ai-chat"), selectable: false }
];
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => !nt.selectable).map((nt) => nt.type);
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type);
const TPL = /*html*/`
<div class="dropdown note-type-widget">
@@ -63,13 +19,6 @@ const TPL = /*html*/`
overflow-y: auto;
overflow-x: hidden;
}
.note-type-dropdown .badge {
margin-left: 8px;
background: var(--accented-background-color);
font-weight: normal;
color: var(--menu-text-color);
}
</style>
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle select-button note-type-button">
<span class="note-type-desc"></span>
@@ -116,10 +65,15 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
return;
}
for (const noteType of NOTE_TYPES.filter((nt) => nt.selectable)) {
for (const noteType of NOTE_TYPES.filter((nt) => !nt.reserved && !nt.static)) {
let $typeLink: JQuery<HTMLElement>;
const $title = $("<span>").text(noteType.title);
if (noteType.isNew) {
$title.append($(`<span class="badge new-note-type-badge">`).text(t("note_types.new-feature")));
}
if (noteType.isBeta) {
$title.append($(`<span class="badge">`).text(t("note_types.beta-feature")));
}
+1 -1
View File
@@ -64,7 +64,7 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
}
#isFullWidthNote(note: FNote) {
if (["image", "mermaid", "book", "render", "canvas", "webView", "mindMap", "geoMap"].includes(note.type)) {
if (["image", "mermaid", "book", "render", "canvas", "webView", "mindMap"].includes(note.type)) {
return true;
}
@@ -3,6 +3,18 @@ import attributeService from "../../services/attributes.js";
import { t } from "../../services/i18n.js";
import type FNote from "../../entities/fnote.js";
import type { EventData } from "../../components/app_context.js";
import { bookPropertiesConfig, BookProperty } from "./book_properties_config.js";
import attributes from "../../services/attributes.js";
import type { ViewTypeOptions } from "../../services/note_list_renderer.js";
const VIEW_TYPE_MAPPINGS: Record<ViewTypeOptions, string> = {
grid: t("book_properties.grid"),
list: t("book_properties.list"),
calendar: t("book_properties.calendar"),
table: t("book_properties.table"),
geoMap: t("book_properties.geo-map"),
board: t("book_properties.board")
};
const TPL = /*html*/`
<div class="book-properties-widget">
@@ -15,42 +27,56 @@ const TPL = /*html*/`
.book-properties-widget > * {
margin-right: 15px;
}
.book-properties-container {
display: flex;
align-items: center;
}
.book-properties-container > div {
margin-right: 15px;
}
.book-properties-container > .type-number > label {
display: flex;
align-items: baseline;
}
.book-properties-container input[type="checkbox"] {
margin-right: 5px;
}
.book-properties-container label {
display: flex;
justify-content: center;
align-items: center;
text-overflow: clip;
white-space: nowrap;
}
</style>
<div style="display: flex; align-items: baseline">
<span style="white-space: nowrap">${t("book_properties.view_type")}:&nbsp; &nbsp;</span>
<select class="view-type-select form-select form-select-sm">
<option value="grid">${t("book_properties.grid")}</option>
<option value="list">${t("book_properties.list")}</option>
<option value="calendar">${t("book_properties.calendar")}</option>
${Object.entries(VIEW_TYPE_MAPPINGS)
.filter(([type]) => type !== "raster")
.map(([type, label]) => `
<option value="${type}">${label}</option>
`).join("")}
</select>
</div>
<button type="button"
class="collapse-all-button btn btn-sm"
title="${t("book_properties.collapse_all_notes")}">
<span class="bx bx-layer-minus"></span>
${t("book_properties.collapse")}
</button>
<button type="button"
class="expand-children-button btn btn-sm"
title="${t("book_properties.expand_all_children")}">
<span class="bx bx-move-vertical"></span>
${t("book_properties.expand")}
</button>
<div class="book-properties-container">
</div>
</div>
`;
export default class BookPropertiesWidget extends NoteContextAwareWidget {
private $viewTypeSelect!: JQuery<HTMLElement>;
private $expandChildrenButton!: JQuery<HTMLElement>;
private $collapseAllButton!: JQuery<HTMLElement>;
private $propertiesContainer!: JQuery<HTMLElement>;
private labelsToWatch: string[] = [];
get name() {
return "bookProperties";
@@ -67,7 +93,6 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
getTitle() {
return {
show: this.isEnabled(),
activate: true,
title: t("book_properties.book_properties"),
icon: "bx bx-book"
};
@@ -80,32 +105,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
this.$viewTypeSelect = this.$widget.find(".view-type-select");
this.$viewTypeSelect.on("change", () => this.toggleViewType(String(this.$viewTypeSelect.val())));
this.$expandChildrenButton = this.$widget.find(".expand-children-button");
this.$expandChildrenButton.on("click", async () => {
if (!this.noteId || !this.note) {
return;
}
if (!this.note?.isLabelTruthy("expanded")) {
await attributeService.addLabel(this.noteId, "expanded");
}
this.triggerCommand("refreshNoteList", { noteId: this.noteId });
});
this.$collapseAllButton = this.$widget.find(".collapse-all-button");
this.$collapseAllButton.on("click", async () => {
if (!this.noteId || !this.note) {
return;
}
// owned is important - we shouldn't remove inherited expanded labels
for (const expandedAttr of this.note.getOwnedLabels("expanded")) {
await attributeService.removeAttributeById(this.noteId, expandedAttr.attributeId);
}
this.triggerCommand("refreshNoteList", { noteId: this.noteId });
});
this.$propertiesContainer = this.$widget.find(".book-properties-container");
}
async refreshWithNote(note: FNote) {
@@ -117,8 +117,15 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
this.$viewTypeSelect.val(viewType);
this.$expandChildrenButton.toggle(viewType === "list");
this.$collapseAllButton.toggle(viewType === "list");
this.$propertiesContainer.empty();
const bookPropertiesData = bookPropertiesConfig[viewType];
if (bookPropertiesData) {
for (const property of bookPropertiesData.properties) {
this.$propertiesContainer.append(this.renderBookProperty(property));
this.labelsToWatch.push(property.bindToLabel);
}
}
}
async toggleViewType(type: string) {
@@ -126,7 +133,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
return;
}
if (!["list", "grid", "calendar"].includes(type)) {
if (!VIEW_TYPE_MAPPINGS.hasOwnProperty(type)) {
throw new Error(t("book_properties.invalid_view_type", { type }));
}
@@ -134,8 +141,122 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.getAttributeRows().find((attr) => attr.noteId === this.noteId && attr.name === "viewType")) {
if (loadResults.getAttributeRows().find((attr) =>
attr.noteId === this.noteId
&& (attr.name === "viewType" || this.labelsToWatch.includes(attr.name ?? "")))) {
this.refresh();
}
}
renderBookProperty(property: BookProperty) {
const $container = $("<div>");
$container.addClass(`type-${property.type}`);
const note = this.note;
if (!note) {
return $container;
}
switch (property.type) {
case "checkbox":
const $label = $("<label>").text(property.label);
const $checkbox = $("<input>", {
type: "checkbox",
class: "form-check-input",
});
$checkbox.on("change", () => {
if ($checkbox.prop("checked")) {
attributes.setLabel(note.noteId, property.bindToLabel);
} else {
attributes.removeOwnedLabelByName(note, property.bindToLabel);
}
});
$checkbox.prop("checked", note.hasOwnedLabel(property.bindToLabel));
$label.prepend($checkbox);
$container.append($label);
break;
case "button":
const $button = $("<button>", {
type: "button",
class: "btn btn-sm"
}).text(property.label);
if (property.title) {
$button.attr("title", property.title);
}
if (property.icon) {
$button.prepend($("<span>", { class: property.icon }));
}
$button.on("click", () => {
property.onClick({
note,
triggerCommand: this.triggerCommand.bind(this)
});
});
$container.append($button);
break;
case "number":
const $numberInput = $("<input>", {
type: "number",
class: "form-control form-control-sm",
value: note.getLabelValue(property.bindToLabel) || "",
width: property.width ?? 100,
min: property.min ?? 0
});
$numberInput.on("change", () => {
const value = $numberInput.val();
if (value === "") {
attributes.removeOwnedLabelByName(note, property.bindToLabel);
} else {
attributes.setLabel(note.noteId, property.bindToLabel, String(value));
}
});
$container.append($("<label>")
.text(property.label)
.append("&nbsp;".repeat(2))
.append($numberInput));
break;
case "combobox":
const $select = $("<select>", {
class: "form-select form-select-sm"
});
const actualValue = note.getLabelValue(property.bindToLabel) ?? property.defaultValue ?? "";
for (const option of property.options) {
if ("items" in option) {
const $optGroup = $("<optgroup>", { label: option.name });
for (const item of option.items) {
buildComboBoxItem(item, actualValue).appendTo($optGroup);
}
$optGroup.appendTo($select);
} else {
buildComboBoxItem(option, actualValue).appendTo($select);
}
}
$select.on("change", () => {
const value = $select.val();
if (value === null || value === "") {
attributes.removeOwnedLabelByName(note, property.bindToLabel);
} else {
attributes.setLabel(note.noteId, property.bindToLabel, String(value));
}
});
$container.append($("<label>")
.text(property.label)
.append("&nbsp;".repeat(2))
.append($select));
break;
}
return $container;
}
}
function buildComboBoxItem({ value, label }: { value: string, label: string }, actualValue: string) {
const $option = $("<option>", {
value,
text: label
});
if (actualValue === value) {
$option.prop("selected", true);
}
return $option;
}
@@ -0,0 +1,169 @@
import { t } from "i18next";
import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { ViewTypeOptions } from "../../services/note_list_renderer"
import NoteContextAwareWidget from "../note_context_aware_widget";
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, type MapLayer } from "../view_widgets/geo_view/map_layer";
interface BookConfig {
properties: BookProperty[];
}
interface CheckBoxProperty {
type: "checkbox",
label: string;
bindToLabel: string
}
interface ButtonProperty {
type: "button",
label: string;
title?: string;
icon?: string;
onClick: (context: BookContext) => void;
}
interface NumberProperty {
type: "number",
label: string;
bindToLabel: string;
width?: number;
min?: number;
}
interface ComboBoxItem {
value: string;
label: string;
}
interface ComboBoxGroup {
name: string;
items: ComboBoxItem[];
}
interface ComboBoxProperty {
type: "combobox",
label: string;
bindToLabel: string;
/**
* The default value is used when the label is not set.
*/
defaultValue?: string;
options: (ComboBoxItem | ComboBoxGroup)[];
}
export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty;
interface BookContext {
note: FNote;
triggerCommand: NoteContextAwareWidget["triggerCommand"];
}
export const bookPropertiesConfig: Record<ViewTypeOptions, BookConfig> = {
grid: {
properties: []
},
list: {
properties: [
{
label: t("book_properties.collapse"),
title: t("book_properties.collapse_all_notes"),
type: "button",
icon: "bx bx-layer-minus",
async onClick({ note, triggerCommand }) {
const { noteId } = note;
// owned is important - we shouldn't remove inherited expanded labels
for (const expandedAttr of note.getOwnedLabels("expanded")) {
await attributes.removeAttributeById(noteId, expandedAttr.attributeId);
}
triggerCommand("refreshNoteList", { noteId: noteId });
},
},
{
label: t("book_properties.expand"),
title: t("book_properties.expand_all_children"),
type: "button",
icon: "bx bx-move-vertical",
async onClick({ note, triggerCommand }) {
const { noteId } = note;
if (!note.isLabelTruthy("expanded")) {
await attributes.addLabel(noteId, "expanded");
}
triggerCommand("refreshNoteList", { noteId });
},
}
]
},
calendar: {
properties: [
{
label: t("book_properties_config.hide-weekends"),
type: "checkbox",
bindToLabel: "calendar:hideWeekends"
},
{
label: t("book_properties_config.display-week-numbers"),
type: "checkbox",
bindToLabel: "calendar:weekNumbers"
}
]
},
geoMap: {
properties: [
{
label: t("book_properties_config.map-style"),
type: "combobox",
bindToLabel: "map:style",
defaultValue: DEFAULT_MAP_LAYER_NAME,
options: [
{
name: t("book_properties_config.raster"),
items: Object.entries(MAP_LAYERS)
.filter(([_, layer]) => layer.type === "raster")
.map(buildMapLayer)
},
{
name: t("book_properties_config.vector_light"),
items: Object.entries(MAP_LAYERS)
.filter(([_, layer]) => layer.type === "vector" && !layer.isDarkTheme)
.map(buildMapLayer)
},
{
name: t("book_properties_config.vector_dark"),
items: Object.entries(MAP_LAYERS)
.filter(([_, layer]) => layer.type === "vector" && layer.isDarkTheme)
.map(buildMapLayer)
}
]
},
{
label: t("book_properties_config.show-scale"),
type: "checkbox",
bindToLabel: "map:scale"
}
]
},
table: {
properties: [
{
label: t("book_properties_config.max-nesting-depth"),
type: "number",
bindToLabel: "maxNestingDepth",
width: 65
}
]
},
board: {
properties: []
}
};
function buildMapLayer([ id, layer ]: [ string, MapLayer ]): ComboBoxItem {
return {
value: id,
label: layer.name
};
}
@@ -48,6 +48,18 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget {
this.contentSized();
}
isEnabled(): boolean | null | undefined {
if (options.get("textNoteEditorType") !== "ckeditor-classic") {
return false;
}
if (!this.note || this.note.type !== "text") {
return false;
}
return true;
}
async getTitle() {
return {
show: await this.#shouldDisplay(),
@@ -58,11 +70,7 @@ export default class ClassicEditorToolbar extends NoteContextAwareWidget {
}
async #shouldDisplay() {
if (options.get("textNoteEditorType") !== "ckeditor-classic") {
return false;
}
if (!this.note || this.note.type !== "text") {
if (!this.isEnabled()) {
return false;
}
@@ -53,12 +53,56 @@ const TPL = /*html*/`
word-break:keep-all;
white-space: nowrap;
}
.promoted-attribute-cell input[type="checkbox"] {
width: 22px !important;
flex-grow: 0;
width: unset;
}
/* Restore default apperance */
.promoted-attribute-cell input[type="number"],
.promoted-attribute-cell input[type="checkbox"] {
appearance: auto;
}
.promoted-attribute-cell input[type="color"] {
width: 24px;
height: 24px;
margin-top: 2px;
appearance: none;
padding: 0;
border: 0;
outline: none;
border-radius: 25% !important;
}
.promoted-attribute-cell input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
.promoted-attribute-cell input[type="color"]::-webkit-color-swatch {
border: none;
border-radius: 25%;
}
.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"] {
position: relative;
opacity: 0.5;
}
.promoted-attribute-label-color input[type="hidden"][value=""] + input[type="color"]:after {
content: "";
position: absolute;
top: 10px;
left: 0px;
right: 0;
height: 2px;
background: rgba(0, 0, 0, 0.5);
transform: rotate(45deg);
pointer-events: none;
}
</style>
<div class="promoted-attributes-container"></div>
@@ -69,11 +113,6 @@ interface AttributeResult {
attributeId: string;
}
/**
* This widget is quite special because it's used in the desktop ribbon, but in mobile outside of ribbon.
* This works without many issues (apart from autocomplete), but it should be kept in mind when changing things
* and testing.
*/
export default class PromotedAttributesWidget extends NoteContextAwareWidget {
private $container!: JQuery<HTMLElement>;
@@ -117,7 +156,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
// the order of attributes is important as well
ownedAttributes.sort((a, b) => a.position - b.position);
if (promotedDefAttrs.length === 0) {
if (promotedDefAttrs.length === 0 || note.getLabelValue("viewType") === "table") {
this.toggleInt(false);
return;
}
@@ -188,6 +227,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
.append($multiplicityCell);
if (valueAttr.type === "label") {
$wrapper.addClass(`promoted-attribute-label-${definition.labelType}`);
if (definition.labelType === "text") {
$input.prop("type", "text");
@@ -262,6 +302,35 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
.on("click", () => window.open($input.val() as string, "_blank"));
$input.after($openButton);
} else if (definition.labelType === "color") {
const defaultColor = "#ffffff";
$input.prop("type", "hidden");
$input.val(valueAttr.value ?? "");
// We insert a separate input since the color input does not support empty value.
// This is a workaround to allow clearing the color input.
const $colorInput = $("<input>")
.prop("type", "color")
.prop("value", valueAttr.value || defaultColor)
.addClass("form-control promoted-attribute-input")
.on("change", e => setValue((e.target as HTMLInputElement).value, e));
$input.after($colorInput);
const $clearButton = $("<span>")
.addClass("input-group-text bx bxs-tag-x")
.prop("title", t("promoted_attributes.remove_color"))
.on("click", e => setValue("", e));
const setValue = (color: string, event: JQuery.TriggeredEvent<HTMLElement, undefined, HTMLElement, HTMLElement>) => {
$input.val(color);
if (!color) {
$colorInput.val(defaultColor);
}
event.target = $input[0]; // Set the event target to the main input
this.promotedAttributeChanged(event);
};
$colorInput.after($clearButton);
} else {
ws.logError(t("promoted_attributes.unknown_label_type", { type: definition.labelType }));
}
+5 -1
View File
@@ -65,7 +65,11 @@ export default class SearchResultWidget extends NoteContextAwareWidget {
return;
}
const noteListRenderer = new NoteListRenderer(this.$content, note, note.getChildNoteIds(), true);
const noteListRenderer = new NoteListRenderer({
$parent: this.$content,
parentNote: note,
showNotePath: true
});
await noteListRenderer.renderList();
}
+2 -9
View File
@@ -8,6 +8,7 @@ import appContext, { type CommandNames, type CommandListenerData, type EventData
import froca from "../services/froca.js";
import attributeService from "../services/attributes.js";
import type NoteContext from "../components/note_context.js";
import { setupHorizontalScrollViaWheel } from "./widget_utils.js";
const isDesktop = utils.isDesktop();
@@ -386,15 +387,7 @@ export default class TabRowWidget extends BasicWidget {
};
setupScrollEvents() {
this.$tabScrollingContainer.on('wheel', (event) => {
const wheelEvent = event.originalEvent as WheelEvent;
if (utils.isCtrlKey(event) || event.altKey || event.shiftKey) {
return;
}
event.preventDefault();
event.stopImmediatePropagation();
event.currentTarget.scrollLeft += wheelEvent.deltaY + wheelEvent.deltaX;
});
setupHorizontalScrollViaWheel(this.$tabScrollingContainer);
this.$scrollButtonLeft[0].addEventListener('click', () => this.scrollTabContainer(-210));
this.$scrollButtonRight[0].addEventListener('click', () => this.scrollTabContainer(210));
@@ -130,7 +130,8 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
constructor() {
super();
this.editorTypeWidget = new EditableCodeTypeWidget();
this.editorTypeWidget = new EditableCodeTypeWidget(true);
this.editorTypeWidget.updateBackgroundColor = () => {};
this.editorTypeWidget.isEnabled = () => true;
@@ -146,6 +147,8 @@ export default abstract class AbstractSplitTypeWidget extends TypeWidget {
doRender(): void {
this.$widget = $(TPL);
this.spacedUpdate.setUpdateInterval(750);
// Preview pane
this.$previewCol = this.$widget.find(".note-detail-split-preview-col");
this.$preview = this.$widget.find(".note-detail-split-preview");
+15 -1
View File
@@ -36,7 +36,21 @@ export default class BookTypeWidget extends TypeWidget {
}
async doRefresh(note: FNote) {
this.$helpNoChildren.toggle(!this.note?.hasChildren() && this.note?.getAttributeValue("label", "viewType") !== "calendar");
this.$helpNoChildren.toggle(this.shouldDisplayNoChildrenWarning());
}
shouldDisplayNoChildrenWarning() {
if (this.note?.hasChildren()) {
return false;
}
switch (this.note?.getAttributeValue("label", "viewType")) {
case "list":
case "grid":
return true;
default:
return false;
}
}
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {

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