1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-09-02 20:46:21 +02:00

Compare commits

..

241 Commits

Author SHA1 Message Date
Laurent Cozic
7683284352 Android 3.0.2 2024-04-15 19:17:10 +01:00
Laurent Cozic
e84ea8ab04 Desktop release v3.0.3 2024-04-15 18:47:03 +01:00
Henry Heino
b638056150 Mobile: Support description banners on plugin-registered settings screens (#10286) 2024-04-15 18:18:22 +01:00
Henry Heino
ff86c253d3 Mobile: Support accepting Joplin Cloud shares (#10300) 2024-04-15 18:17:34 +01:00
Henry Heino
86d9f7e1cb Chore: Mobile: Remove no-longer-necessary Icon warning workaround (#10301) 2024-04-15 18:16:24 +01:00
Henry Heino
385fe7bbe0 Desktop: Fixes #10067: Fix dragging notebooks to the toplevel notebook (#10302) 2024-04-15 18:15:45 +01:00
Henry Heino
313587097a Server: Decrease maximum email and full_name sizes (#10303) 2024-04-15 18:15:29 +01:00
Henry Heino
8393ccc7f8 Chore: Desktop: Use stronger types in Sidebar.tsx (#10305) 2024-04-15 18:15:18 +01:00
Henry Heino
e3ba605592 Chore: Fixes #10306: Remove unnecessary initial commit in default plugins build (#10308) 2024-04-15 18:14:47 +01:00
Henry Heino
035557de9f Desktop: Allow creating plugins that process pasted text in the beta editor (#10310) 2024-04-15 18:14:38 +01:00
Sy Sagar
97ff2b51f1 Docs: build for wsl/linux based system. (#10312)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-04-15 18:14:21 +01:00
Henry Heino
87f7fb6841 Mobile: Fixes #10313: Fix error on retry or ignore attachment too large error (#10314) 2024-04-15 18:13:41 +01:00
github-actions[bot]
9fe31544f7 @SySagar has signed the CLA in laurent22/joplin#10312 2024-04-15 05:50:29 +00:00
Laurent Cozic
89dfbe3ec1 Server: Optimize delta change query to prevent timeouts on large datasets 2024-04-13 14:44:50 +01:00
github-actions[bot]
0c640c5e77 @totikom has signed the CLA in laurent22/joplin#10307 2024-04-12 17:17:16 +00:00
github-actions[bot]
a3eec19b32 @G0maa has signed the CLA in laurent22/joplin#10298 2024-04-11 11:57:26 +00:00
Henry Heino
c0c3b4d23e Mobile: Plugins: Fix event listener memory leak when disabling/uninstalling plugins (#10280)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-04-11 09:04:47 +01:00
Henry Heino
1812587970 Chore: Fixes #10285: Fix failing tarExtract test (#10295) 2024-04-11 08:43:39 +01:00
Henry Heino
1bb724fe0e Android: Fix plugin card titles are clipped (#10296) 2024-04-11 08:37:20 +01:00
Henry Heino
346f49fa66 Chore: Improve types for mobile and desktop themeStyle (#10297) 2024-04-11 08:35:20 +01:00
Henry Heino
55d25308f8 Chore: Refactor warning banner out of ScreenHeader into a separate file (#10268) 2024-04-10 15:31:04 +01:00
Fabio Neto
514a8cf841 Desktop: Fixes #9970: Focus is lost when the hyperlink modal is cancelled (#10258)
Signed-off-by: Fabio Neto <fabiogvdneto@tecnico.ulisboa.pt>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-04-10 15:30:22 +01:00
pedr
837826ea4f Server: Improve log quality by increasing specificity of error (#10287) 2024-04-10 11:40:12 +01:00
Henry Heino
681d1d67f3 Mobile: Resolves #10288: Show plugin versions in settings (#10289) 2024-04-10 11:39:18 +01:00
Henry Heino
d2f3252de2 Desktop: Allow marking sync errors as ignored in "sync status" (#10290) 2024-04-10 11:38:23 +01:00
pedr
859d3e867e Desktop, Mobile: Fixes #10292: Email to note address not presented in configuration screen before synchronisation (#10293) 2024-04-10 11:37:00 +01:00
Henry Heino
238683e36f Desktop: Fix "open profile directory" shows a warning message (#10294) 2024-04-10 11:35:35 +01:00
Henry Heino
2ae08ff46e Mobile: Support importing from JEX files (#10269) 2024-04-08 17:57:01 +01:00
Pratyay Roy
ce672915da Mobile: Resolves #10207: display recommended plugin alert (#10281) 2024-04-08 14:52:07 +01:00
Henry Heino
a2071bfed2 Chore: Mobile: Plugin settings screen: Improve accessibility and tests (#10267) 2024-04-08 12:36:40 +01:00
Henry Heino
03c3feef16 Mobile: Resolves #10245: Allow marking items as "ignored" in sync status (#10261) 2024-04-08 12:35:57 +01:00
Self Not Found
b3f4414026 CI: Bump action versions (#10265) 2024-04-07 14:42:50 +01:00
Henry Heino
04c6863d7f Mobile: Fixes #10270: Fix dropdowns invisble when opening settings by clicking "synchronize" (#10271) 2024-04-07 14:38:51 +01:00
Arda Kılıçdağı
b678e2fb5d Turkish translations updated (#10277) 2024-04-07 14:38:24 +01:00
Laurent Cozic
1179de7c36 Destkop, Cli, Mobile: Resolves #6173: Do not repeat failed requests with ENOTFOUND error 2024-04-07 11:29:30 +01:00
Henry Heino
58ca1a938b Chore: Refactor EventManager to use stronger types (#10272) 2024-04-06 18:08:16 +01:00
Liffindra Angga Zaaldian
9713034f18 Update Indonesian translations (#10276) 2024-04-06 18:04:50 +01:00
Laurent Cozic
28b8818c4d Chore: Disabled not very useful test that can fail easily as we add more sort fields 2024-04-05 12:19:57 +01:00
Laurent Cozic
2e2a2b3193 Tools: Implement @typescript-eslint/no-explicit-any rule 2024-04-05 12:16:49 +01:00
Laurent Cozic
42900bcc66 Desktop: Fixed sorting labels 2024-04-05 10:41:14 +01:00
Laurent Cozic
ce451c5850 Desktop: Resolves #5819: Allow sorting by due date and completion date in detailed note list 2024-04-05 10:34:05 +01:00
Laurent Cozic
5b4477f7bd Desktop: Fixed rendering of alarm time in detailed note list 2024-04-05 10:32:52 +01:00
github-actions[bot]
660ebcfc77 @AliceHincu has signed the CLA in laurent22/joplin#10222 2024-04-04 20:04:18 +00:00
Laurent Cozic
86872fb07c Chore: Fix dic 2024-04-04 15:37:07 +01:00
Cyrus Yip
c55bb95950 Doc: add InfiniCLOUD to WebDAV section (#10266) 2024-04-04 12:45:04 +01:00
Laurent Cozic
d613d1ab4e Tools: Switch from "docker-compose" to "docker compose" 2024-04-04 12:43:35 +01:00
github-actions[bot]
cd5d648eec @CyrusYip has signed the CLA in laurent22/joplin#10266 2024-04-04 11:41:47 +00:00
Henry Heino
384b17738a Mobile: Plugin API: Fix error when calling plugins.dataDir (#10262) 2024-04-04 12:10:59 +01:00
Henry Heino
500c8facdb Mobile: Plugin API: Fix crash when a plugin registers an enum setting with no default (#10263) 2024-04-04 12:10:13 +01:00
cagnusmarlsen
6f76fe728f Desktop: Fixes #10182: Detailed note list doesn't follow preferred date and time formats (#10204) 2024-04-04 11:38:38 +01:00
github-actions[bot]
2488dd3806 @jaibal1152 has signed the CLA in laurent22/joplin#10260 2024-04-03 18:13:28 +00:00
Self Not Found
9d3cccdf71 Mobile: Show sync version and client id in More Info (#10254) 2024-04-03 19:04:16 +01:00
Henry Heino
8dc75efc4c Desktop: Security: Make attachment and file links safer (#10250) 2024-04-03 18:57:52 +01:00
Henry Heino
7caed19a32 Mobile: Plugin API: Improve support for the Kanban and similar plugins (#10247) 2024-04-03 18:56:54 +01:00
Henry Heino
a301470ac5 Mobile: Default to tab indentation for consistency with desktop platforms (#10242) 2024-04-03 18:54:34 +01:00
Henry Heino
f899c97c4c Mobile,Desktop: Resolves #10206: Allow marking a plugin as mobile-only or desktop-only (#10229) 2024-04-03 18:51:09 +01:00
Daniel Nunes
8630c8e630 Desktop: Fixes #9950: Link pased in RTE editor is not underlined until switch to another note (#10202) 2024-04-03 18:43:30 +01:00
github-actions[bot]
29daec2c07 @fabiogvdneto has signed the CLA in laurent22/joplin#10258 2024-04-03 17:42:28 +00:00
Janhavi Alekar
ce3a28de70 Desktop: Fixes #10025: Toggle external editing button overlaps with bold button. (#10069) 2024-04-03 18:29:22 +01:00
pedr
1e6cc11868 Desktop, Mobile, Cli: Avoid unnecessary requests if Joplin Cloud credentials are empty (#10256) 2024-04-03 15:43:22 +01:00
github-actions[bot]
3e0f6994a1 @henriqueeapsilva has signed the CLA in laurent22/joplin#10257 2024-04-03 14:15:55 +00:00
ihan1004
c10e617870 All: Translation: Update ko.po (#10239) 2024-04-01 15:08:56 -04:00
github-actions[bot]
cade585292 @RafaR13 has signed the CLA in laurent22/joplin#10246 2024-04-01 18:59:05 +00:00
Laurent Cozic
f60e105d77 Desktop: Fixes #10108: When creating a note or to-do, focus is not set correctly 2024-04-01 16:51:47 +01:00
Laurent Cozic
b622f9b938 Chore: Fixed naming 2024-04-01 16:47:50 +01:00
Laurent Cozic
00084c5798 Desktop, Mobile: Improve focus handling 2024-04-01 15:34:22 +01:00
Laurent Cozic
554fb7026a Chore: Remove old note list files 2024-04-01 14:19:48 +01:00
Laurent Cozic
04a6c36b5c Chore: Fixed populateDatabase tool 2024-04-01 13:44:07 +01:00
Joplin Bot
adbf819cc0 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-04-01 00:41:42 +00:00
github-actions[bot]
3062f83367 @Wr4th100 has signed the CLA in laurent22/joplin#10228 2024-03-29 15:43:55 +00:00
Henry Heino
bdb2e588f3 iOS: Resolves #10154: Allow installing recommended plugins (#10223) 2024-03-29 12:40:54 +00:00
Henry Heino
688d807eee Mobile: Fixes #10188: Fix "new note" button is shown in the trash notebook (#10227) 2024-03-29 12:11:15 +00:00
Henry Heino
06aa64016f Desktop: Resolves #9794: Plugin API: Add support for loading PDFs with the imaging API (#10177) 2024-03-27 18:53:24 +00:00
github-actions[bot]
06c7c132b8 @Aitchessbee has signed the CLA in laurent22/joplin#10220 2024-03-27 11:34:22 +00:00
github-actions[bot]
fcb837ca99 @thisisnihal has signed the CLA in laurent22/joplin#10217 2024-03-26 18:17:15 +00:00
Mohamad Ashraf
7f34afcaea Desktop: Fixes#10196: Added hover effect to detailed renderer (#10213) 2024-03-26 14:05:40 +00:00
Mohammad Ashouri
876fa324e5 All: Update farsi/persian translation fa.po (#10181) 2024-03-26 11:47:26 +00:00
Mohamad Ashraf
f781183250 Desktop: Fixes #10078: Fixed auto scrolling with moving a note (#10193) 2024-03-26 11:45:15 +00:00
Henry Heino
c1ae449ce2 Android,Desktop: Plugin API: Fix unable to require @codemirror/search (#10205) 2024-03-26 11:36:30 +00:00
Henry Heino
c4aa18a63e Doc: Mark several desktop-only plugin API features as "desktop-only" (#10208) 2024-03-26 11:36:15 +00:00
Henry Heino
27b86fbb00 Android: Plugin API: Support clipboard.availableFormats (#10209) 2024-03-26 11:36:04 +00:00
Henry Heino
569c6d8479 Tools: Fix build failures when compiled .js files are out-of-date (#10210) 2024-03-26 11:35:30 +00:00
Henry Heino
d3e2d3fc4a Android: Plugins: Autohide the plugin panel toggle in toolbar to increase size for notebook dropdown (#10212) 2024-03-26 11:35:15 +00:00
github-actions[bot]
9b5ee63638 @DarkFalc0n has signed the CLA in laurent22/joplin#10215 2024-03-26 10:56:22 +00:00
Joplin Bot
dee68681f5 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-26 00:37:42 +00:00
Laurent Cozic
f85db1496e Desktop: Fixed error when exporting certain notes that contain plugin content 2024-03-25 12:52:06 +00:00
Henry Heino
0839b0314e Desktop: Upgrade the built-in Backup plugin to version 1.4.1 (#10197) 2024-03-25 11:40:44 +00:00
Henry Heino
40dbb8bd7f Android: Toggle plugin panels using a button in the toolbar (#10180) 2024-03-25 11:39:48 +00:00
Edward Jiles
2de5c1bbf8 Mobile, Desktop: Make tables horizontally scrollable (#10161)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2024-03-25 11:27:48 +00:00
github-actions[bot]
20edc63785 @Mohamad-Shiro has signed the CLA in laurent22/joplin#10193 2024-03-23 23:05:37 +00:00
Meow
b936f9ba7c All: Bump @codemirror/view version. (#10174) 2024-03-23 15:03:59 +00:00
Henry Heino
a5419e61d5 Doc: Plugin API: Document how to add support for the Rich Text Editor to a renderer plugin (#10178) 2024-03-23 14:41:17 +00:00
Henry Heino
ec4d4141ef Doc: Plugin API: Update editor content script documentation to include information about the mobile and beta editors (#10179) 2024-03-23 14:40:14 +00:00
Henry Heino
b5a16f756a Chore: Mobile: Migrate the tags dialog to TypeScript (#10185) 2024-03-23 14:21:37 +00:00
github-actions[bot]
0331d2a8db @kartiksaxena532 has signed the CLA in laurent22/joplin#10187 2024-03-23 13:19:45 +00:00
Joplin Bot
79eedcf6a6 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-22 00:37:51 +00:00
Laurent Cozic
0715340dc0 Android 3.0.1 2024-03-21 18:28:16 +00:00
Laurent Cozic
a59ad20bd5 Desktop release v3.0.2 2024-03-21 15:51:26 +00:00
Laurent Cozic
e3de158d18 Tools: Fixed building macOS app on CI 2024-03-21 15:51:01 +00:00
Laurent Cozic
e9514e742b Desktop release v3.0.1 2024-03-21 14:55:18 +00:00
Henry Heino
d2c060cd97 Chore: Mobile: Fix note viewer startup error (#10164) 2024-03-21 10:50:44 +00:00
Henry Heino
57fc70cec1 Mobile: Plugins: Fix warning after reloading plugins (#10165) 2024-03-21 10:50:32 +00:00
Henry Heino
180f52dab2 Mobile: Fixes #10166: Fix clicking on a link results in a blank screen (#10168) 2024-03-21 10:48:35 +00:00
Henry Heino
0c6df3dd73 Mobile: Fixes #10170, #8779: Fix shared data lost if Joplin is closed immediately after receiving a share (#10171) 2024-03-21 10:45:54 +00:00
github-actions[bot]
b29bf7de5d @itzTheMeow has signed the CLA in laurent22/joplin#10174 2024-03-21 01:12:20 +00:00
Henry Heino
382f0d8218 Mobile: Fixes #10172: Fix trash folder sometimes has wrong icon (#10173) 2024-03-21 01:09:01 +00:00
Joplin Bot
55d72a8f68 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-20 12:20:52 +00:00
cagnusmarlsen
e9ebd845b9 Desktop: Fixes #10077: Special characters in notebooks and tags are not sorted alphabetically (#10085)
Co-authored-by: Martin Dörfelt <martin.d@andix.de>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-20 11:17:46 +00:00
pedr
ea29cf4e13 Clipper, Desktop: Prevent race condition when download limit is reached (#10124) 2024-03-20 11:11:57 +00:00
Henry Heino
d260d0efce Mobile: Fixes #10130: Improve note editor performance when quickly entering text (#10134) 2024-03-20 11:02:10 +00:00
Henry Heino
e92f89df99 Android: Add support for renderer plugins (#10135) 2024-03-20 11:01:09 +00:00
Henry Heino
44e8950f1b Android: Fixes #10152: Fix broken plugin API: editor.execCommand (#10153) 2024-03-20 10:58:42 +00:00
Siddhant Paritosh Rao
32141d4e23 Mobile: Fixes #10143: Shows only the real folders in the dropdown of parent folders. (#10147) 2024-03-20 10:53:36 +00:00
Henry Heino
7d068cfb87 Android: Allow debugging plugins (#10156) 2024-03-20 10:52:58 +00:00
Henry Heino
9c3e751ebc Server: Avoid logging automated resource deletions (#10157) 2024-03-20 10:52:42 +00:00
Henry Heino
eecad1aefc Desktop: Resolves #8931: Improve support for plugins in the Rich Text Editor (implement webviewApi.postMesage) (#10158) 2024-03-20 10:52:29 +00:00
Laurent Cozic
eb06ac673b Revert "Desktop: Fixes #10036: Applied font family and font size to RTE (#10102)"
This reverts commit 85d98f5254.

Reason: Introducing too many issues, some of them know, some of them unknown.
2024-03-20 10:50:11 +00:00
ERYpTION
5e2c54f2ad All: Translation: Update da_DK.po (#10127)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-18 23:57:08 -04:00
Joplin Bot
15649c89f1 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-18 18:16:55 +00:00
Laurent Cozic
42483a4d46 Tools: Improve getting GitHub name for git-changelog 2024-03-18 18:11:19 +00:00
Laurent Cozic
56b010ba0e Merge branch 'release-2.14' into dev 2024-03-18 16:03:58 +00:00
Laurent Cozic
cfd98e3a4d Desktop release v2.14.20 2024-03-18 16:01:54 +00:00
Laurent Cozic
40db753417 Deskop, Cli: Fixes #10125: ENEX does not import correctly when title of note matches the name of the attachment 2024-03-18 16:00:39 +00:00
Laurent Cozic
3d2c100fe9 Desktop: Fixes #10097: Fix OCR not working for certain languages 2024-03-18 12:16:17 +00:00
Laurent Cozic
fd4d7ead43 Merge branch 'release-2.14' into dev 2024-03-18 10:17:39 +00:00
Laurent Cozic
073df50244 lock file 2024-03-18 09:11:32 +00:00
Laurent Cozic
6973734d5b Doc: Fixed sponsors 2024-03-17 11:59:46 +00:00
Laurent Cozic
b44b30124c Doc: Added documentation for the Joplin Cloud "custom banner" feature 2024-03-17 11:52:02 +00:00
Laurent Cozic
60f447dd49 Doc: Added documentation for the Joplin Cloud "custom banner" feature 2024-03-17 11:51:10 +00:00
Ton Hoang Nguyen (Bill)
7638bdf171 Desktop: Resolves #9984: Allow 'All Notes' to have 'Toggle own sort order' (#10021)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-16 09:16:42 +00:00
github-actions[bot]
9dc480e8d1 @chaNcharge has signed the CLA in laurent22/joplin#10137 2024-03-16 06:37:23 +00:00
Laurent Cozic
c2dbb9606f Doc: Updated sponsors 2024-03-15 15:43:01 +00:00
github-actions[bot]
a40c3b792e @7adidaz has signed the CLA in laurent22/joplin#10128 2024-03-15 11:48:06 +00:00
Henry Heino
08aa2ae939 Android: Fixes #10122: Fix screen reader touch-to-focus broken on note list and note viewer pages (#10123) 2024-03-15 10:16:16 +00:00
Abdelrrahman Elhaddad
85d98f5254 Desktop: Fixes #10036: Applied font family and font size to RTE (#10102) 2024-03-15 09:30:45 +00:00
Khương Duy
310a90744a Desktop: Fixes #10060: Fix "New note" button rendering when startup with Trash can selected. (#10076) 2024-03-15 09:29:24 +00:00
Henry Heino
b3ec92a57e Mobile: Add support for plugin panels and dialogs (#10121) 2024-03-14 19:04:32 +00:00
Siddhant Paritosh Rao
b9eb4522f5 Mobile: Resolves #10092: Added empty trash option on long pressing the trash folder (#10120) 2024-03-14 18:42:58 +00:00
Henry Heino
04298f0eba Mobile: Fix note editor's settings and plugins updated on every keystroke (#10116) 2024-03-14 18:42:22 +00:00
Henry Heino
a53a8d67a1 Mobile: Fix plugin API memory leak (#10115) 2024-03-14 18:42:13 +00:00
Henry Heino
6467bf0fc1 Desktop: Link "browse all plugins" to joplinapp.org/plugins (#10113) 2024-03-14 18:40:47 +00:00
Henry Heino
4ac0cdf556 Chore: Add RemoteMessenger documentation to plugin technical spec (#10112) 2024-03-14 18:39:27 +00:00
Henry Heino
3e34f150b8 Desktop: Upgrade to Electron 29 (#10110) 2024-03-14 18:39:02 +00:00
Henry Heino
e72cce0d07 Clipper: Improve support for future versions of Chrome (upgrade to manifest version 3) (#10109) 2024-03-14 18:38:20 +00:00
Henry Heino
78b8839ae3 CLI: Resolves #10090: Allow deleting notes and notebooks permanently (#10107)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-14 18:38:07 +00:00
Henry Heino
49cd17e520 Chore: Allow disabling deletion logging (#10105) 2024-03-14 18:34:11 +00:00
Henry Heino
c16ce1c434 Desktop,Mobile: Resolves #10073, #10080: Fix conflicts notebook doesn't work with the trash feature (#10104) 2024-03-14 18:30:49 +00:00
Laurent Cozic
8eea3953f3 Update translations 2024-03-14 09:52:40 +00:00
Laurent Cozic
8cb9c08bcb Cli: Clarify that the "restore" command is to restore items from the trash 2024-03-12 18:20:49 +00:00
Laurent Cozic
bc7a0fa095 Chore: Disable OCR language file caching on dev to fix bug 2024-03-12 17:40:53 +00:00
Laurent Cozic
298549e51a Update dictionary 2024-03-12 17:35:50 +00:00
Siddhant Paritosh Rao
da393f6c34 Mobile: Fixes #10065: New note button crashes app when there are no notebooks (#10087) 2024-03-11 15:22:26 +00:00
pedr
8bdac6ffbf Mobile: Change Joplin Cloud login process to allow MFA via browser (#9776) 2024-03-11 15:17:23 +00:00
Henry Heino
55cafb8891 Android: Add support for Markdown editor plugins (#10086)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-11 15:02:15 +00:00
Henry Heino
238468ddaa Chore: Joplin generator: Remove @joplin/lib dev dependency from generated projects (#10075) 2024-03-11 10:11:07 +00:00
Laurent Cozic
b152732d7f Tools: Build doc on CI (#10100) 2024-03-11 10:06:55 +00:00
pedr
56dde88003 Cli: Change Joplin Cloud login process (#9722) 2024-03-11 09:58:54 +00:00
pedr
9e0a0468b2 Chore: Make resource creation from files faster by executing them in parallel (#9952) 2024-03-11 09:39:57 +00:00
Laurent Cozic
e203397f89 Doc: Fixed links 2024-03-10 23:59:48 +00:00
github-actions[bot]
3e22041672 @danimnunes has signed the CLA in laurent22/joplin#10099 2024-03-10 16:44:05 +00:00
Arda Kılıçdağı
0d018a8d7a All: Translation: Update tr_TR.po (#10098) 2024-03-09 23:08:01 -05:00
Henry Heino
c3954d7326 Chore: Fix spelling error (#10096) 2024-03-09 21:14:19 +00:00
Henry Heino
d7401d70a7 Chore: Mobile: Migrate global-style.js to TypeScript (#10091) 2024-03-09 11:15:13 +00:00
Henry Heino
bae16f7a65 Desktop: Fixes #10082: Fix text not shown in plugin message boxes (#10084) 2024-03-09 11:05:38 +00:00
Henry Heino
25cd5affca Chore: Apply changes from mobile plugins to lib/ and app-desktop/ (#10079) 2024-03-09 11:03:57 +00:00
Henry Heino
91004f5714 Desktop: Fixes #10020: Beta markdown editor: Support overriding built-in keyboard shortcuts (#10022) 2024-03-09 10:49:28 +00:00
Henry Heino
c35085d1d5 Desktop: Improve beta editor support for the Rich Markdown plugin (#9935) 2024-03-09 10:48:22 +00:00
pedr
17a8ce5010 Api: Add capability of limiting downloads (#9788) 2024-03-09 10:45:21 +00:00
pedr
4d8fcff6d5 Desktop: Change Joplin Cloud login process to allow MFA via browser (#9445)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-09 10:35:54 +00:00
Henry Heino
75cb639ed2 Cli,Desktop,Mobile: Resolves #9465: Log user actions (deletions) (#9585) 2024-03-09 10:33:05 +00:00
github-actions[bot]
3222b620b9 @AdarshSajwan2003 has signed the CLA in laurent22/joplin#10093 2024-03-08 19:59:55 +00:00
Joplin Bot
d7a0d74c4d Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-08 12:19:09 +00:00
github-actions[bot]
52810c51f5 @Sidd-R has signed the CLA in laurent22/joplin#10087 2024-03-08 02:14:22 +00:00
github-actions[bot]
1b96a16586 @ab-elhaddad has signed the CLA in laurent22/joplin#10083 2024-03-07 19:35:54 +00:00
Laurent Cozic
971c4e5e84 Desktop release v2.14.19 2024-03-07 10:01:48 +00:00
Henry Heino
9ef0a504ec Desktop: Re-enable UNC links (#10071) 2024-03-07 10:01:27 +00:00
github-actions[bot]
e59211deea @khuongduy354 has signed the CLA in laurent22/joplin#10076 2024-03-07 07:01:48 +00:00
Laurent Cozic
3177729663 Desktop release v2.14.18 2024-03-06 16:02:00 +00:00
github-actions[bot]
70c7804a43 @Amit91848 has signed the CLA in laurent22/joplin#10070 2024-03-06 15:59:21 +00:00
Radith Samarakoon
c40682f16f Desktop: Resolves #9981: Fix Vim keymap error with beta editor (#10049) 2024-03-06 14:13:57 +00:00
Henry Heino
406c778cfd Desktop: Fixes #10062: Fix pasting images from the rich text editor into the rich text editor (#10064) 2024-03-06 14:13:24 +00:00
Henry Heino
9d17ab429d Chore: Mobile: Update fsDriver in preparation for mobile plugins (#10066) 2024-03-06 10:03:11 +00:00
Henry Heino
20f8bb76f7 Desktop: Resolves #9927: Beta editor: Fix search results not highlighted (#9928) 2024-03-06 09:53:07 +00:00
cagnusmarlsen
5e4c35a18f Desktop: Fixes #9960: Creating a profile changes the language of Joplin (#10038) 2024-03-05 18:09:23 +00:00
NightKnight
9a6484c488 Tools: Fix packages\lib\fsDriver.test.ts test file on Windows (#10053) 2024-03-05 18:08:47 +00:00
Henry Heino
1dfebf5ed3 iOS: Fixes #10047: Allow pasting URLs copied from the share sheet (#10048) 2024-03-05 16:57:18 +00:00
Marph
be2f4d3d79 Desktop: Configure RTE to handle the first table row as header (#10059) 2024-03-05 16:52:43 +00:00
Laurent Cozic
a1cea6776f Desktop: Fixes #10058: OCR does not start when German language is selected 2024-03-05 16:36:27 +00:00
Laurent Cozic
3c10282848 Doc: Set blog page title to "News" 2024-03-05 14:14:31 +00:00
Laurent Cozic
d9a16b5c0f Desktop: Fixes #10050: Fixed OCR memory leak when processing PDF documents 2024-03-05 11:42:54 +00:00
github-actions[bot]
28c7268f82 @JanhaviAlekar has signed the CLA in laurent22/joplin#10055 2024-03-05 10:54:43 +00:00
github-actions[bot]
3eab87ae69 @Deadreyo has signed the CLA in laurent22/joplin#10053 2024-03-05 08:45:27 +00:00
Laurent Cozic
9acbac6613 Desktop: Fixed sizing of new note buttons 2024-03-04 12:12:42 +00:00
Henry Heino
9a10cd4bec Desktop: Fixes #10023: Beta editor plugins: Fix opening and closing settings can break some plugins with legacy code (#10024) 2024-03-04 10:34:33 +00:00
Radith Samarakoon
5aba1e38a2 Desktop: Resolves #9998: Fixed text wrapping in Spellcheck button (#10005) 2024-03-04 10:33:39 +00:00
Michael Sorens
b812027281 Doc: Update on synchronization (#9721)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2024-03-04 10:23:39 +00:00
Henry Heino
dfc08da40c Desktop,Mobile: Resolves #10031: Upgrade CodeMirror 6 packages (#10032) 2024-03-04 10:20:06 +00:00
Laurent Cozic
f5f47f3c08 Merge branch 'release-2.14' into dev 2024-03-04 09:31:37 +00:00
Laurent Cozic
8d5ee36745 Desktop: Fixes #10044: Certain RTE menu items are not visible in dark mode 2024-03-04 09:25:51 +00:00
github-actions[bot]
7068670554 @chenrui333 has signed the CLA in laurent22/joplin#10039 2024-03-02 22:13:36 +00:00
Laurent Cozic
6e3162f92f Tools: Always run all checks on CI 2024-03-02 17:59:42 +00:00
Mr-Kanister
6494b74d0c Desktop: Fixes #9453: Improve visibility of selected note in OLED dark theme (#10026) 2024-03-02 16:04:38 +00:00
Sagnik Mandal
d26d9f16d9 Desktop: Fixes #10007: Fixed Toggle Comment & Delete/Duplicate/Sort Line Options in Beta Editor (#10016) 2024-03-02 15:58:15 +00:00
Henry Heino
4c6969b17d Chore: Plugin API: Rename CodeMirrorContentScriptModule to MarkdownEditorContentScriptModule (#10015) 2024-03-02 15:57:40 +00:00
Sagnik Mandal
9a2a251eec Desktop: Fixes #9985: Filter Sync Target Info Logs (#10014) 2024-03-02 15:57:29 +00:00
Henry Heino
f6c7213f69 Desktop: Resolves #9890: Fix hiding the note preview pane is very slow for large notes (#10006) 2024-03-02 15:55:35 +00:00
Henry Heino
4827d0bf92 Desktop: Fixes #9982: Show focus indicator when navigating with keyboard (#9989) 2024-03-02 15:54:16 +00:00
pedr
f0f6590312 Desktop: Fixes #9919: Command palette not showing note title (#9961) 2024-03-02 15:53:46 +00:00
Henry Heino
fa83d48141 Chore: Plugin generator: Update types (#10010) 2024-03-02 15:53:10 +00:00
cagnusmarlsen
c409160ad7 Desktop: Resolves #9980: Support Ctrl+Enter keyboard shortcut (Cmd+Enter on MacOS) (#10003) 2024-03-02 15:52:55 +00:00
pedr
ff1f1b190e Desktop: Fixes #9264: Preserve indentation from plain text when pasting on Rich Text Editor (#9828) 2024-03-02 15:43:38 +00:00
Laurent Cozic
53d5cf55bc Desktop: Add support for multiple columns note list (#9924) 2024-03-02 15:29:18 +00:00
Laurent Cozic
f19b1c5364 All: Resolves #483: Add trash folder (#9671) 2024-03-02 14:25:27 +00:00
Joplin Bot
07fbd547dc Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-02 12:16:54 +00:00
Laurent Cozic
cb540a5abb Chore: Setup new release 3.0 2024-03-02 11:04:23 +00:00
github-actions[bot]
a7b303259c @GuptTmay has signed the CLA in laurent22/joplin#10035 2024-03-02 06:25:15 +00:00
Joplin Bot
ee181c1fd6 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-02 06:17:18 +00:00
Joplin Bot
2e8fc99c5c Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-02 00:36:16 +00:00
Laurent Cozic
4a78cd2564 Merge branch 'release-2.14' into dev 2024-03-01 19:10:39 +00:00
Laurent Cozic
95e42c4ca7 CLI v2.14.1 2024-03-01 19:10:10 +00:00
Laurent Cozic
98bb0250f2 Lock file 2024-03-01 19:07:01 +00:00
Laurent Cozic
5aba5e544d Releasing sub-packages 2024-03-01 19:06:13 +00:00
Laurent Cozic
0ca36bbf66 Doc: Add news item for release 2.14 2024-03-01 18:44:00 +00:00
Joplin Bot
5d3034d418 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-01 18:16:00 +00:00
Laurent Cozic
438dddda6e iOS 12.14.6 2024-03-01 18:05:20 +00:00
Laurent Cozic
8bd6132398 Update translations 2024-03-01 18:04:19 +00:00
Laurent Cozic
d1c6c0622b Tools: Fix spelling regex 2024-03-01 18:04:02 +00:00
ERYpTION
bd5b3feabe All: Translation: Update da_DK.po (#10029) 2024-03-01 04:29:15 -05:00
Joplin Bot
22f4d19dd1 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-03-01 00:40:46 +00:00
Laurent Cozic
a86f859b42 Tools: Fixed order of release notes posted to forum 2024-02-29 19:45:48 +00:00
Laurent Cozic
4bef8aa632 Tools: Only process supported files with spellchecker 2024-02-28 14:26:52 +00:00
Laurent Cozic
40ae03c438 Tools: Add info link when spelling mistake check fails 2024-02-27 14:22:26 +00:00
Laurent Cozic
afedc53354 Tools: Improve error message and suggested command when checkIgnoredFiles pre-commit hook fails 2024-02-27 11:46:02 +00:00
Laurent Cozic
0d5bca20d3 lock file 2024-02-27 11:34:47 +00:00
github-actions[bot]
f254255ba5 @criticic has signed the CLA in laurent22/joplin#10014 2024-02-27 11:30:31 +00:00
Laurent Cozic
fc1c1a3c20 Tools: Remove message "add --no-verify to bypass" when pre-commit fails 2024-02-27 11:18:16 +00:00
Wesley D
3c31b2bc38 Doc: Change macOS and Linux symlink suggestion (#10009) 2024-02-27 10:20:58 +00:00
Najam Ul Saqib
b4cc6803e7 Doc: Fix a minor issue in comment (#10011) 2024-02-27 10:17:55 +00:00
github-actions[bot]
2ac7997c07 @njmulsqb has signed the CLA in laurent22/joplin#10011 2024-02-27 07:04:55 +00:00
Joplin Bot
0055345689 Doc: Auto-update documentation
Auto-updated using release-website.sh
2024-02-27 00:36:49 +00:00
github-actions[bot]
f43f5c0a34 @WesleyDavid has signed the CLA in laurent22/joplin#10009 2024-02-26 21:03:49 +00:00
1112 changed files with 62221 additions and 36148 deletions

View File

@@ -52,7 +52,7 @@ packages/app-desktop/packageInfo.js
packages/app-desktop/services/electron-context-menu.js
packages/app-desktop/vendor/lib/
packages/app-mobile/android
packages/app-mobile/components/NoteEditor/**/*.bundle.js
packages/app-mobile/**/*.bundle.js
packages/app-mobile/ios
packages/app-mobile/lib/rnInjectedJs/
packages/app-mobile/locales
@@ -109,7 +109,10 @@ packages/app-cli/app/command-mkbook.test.js
packages/app-cli/app/command-mkbook.js
packages/app-cli/app/command-mv.js
packages/app-cli/app/command-ren.js
packages/app-cli/app/command-restore.js
packages/app-cli/app/command-rmbook.test.js
packages/app-cli/app/command-rmbook.js
packages/app-cli/app/command-rmnote.test.js
packages/app-cli/app/command-rmnote.js
packages/app-cli/app/command-set.js
packages/app-cli/app/command-settingschema.js
@@ -117,6 +120,7 @@ packages/app-cli/app/command-sync.js
packages/app-cli/app/command-testing.js
packages/app-cli/app/command-use.js
packages/app-cli/app/command-version.js
packages/app-cli/app/gui/FolderListWidget.js
packages/app-cli/app/gui/StatusBarWidget.js
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/setupCommand.js
@@ -143,6 +147,7 @@ packages/app-desktop/bridge.js
packages/app-desktop/checkForUpdates.js
packages/app-desktop/commands/copyDevCommand.js
packages/app-desktop/commands/editProfileConfig.js
packages/app-desktop/commands/emptyTrash.js
packages/app-desktop/commands/exportFolders.js
packages/app-desktop/commands/exportNotes.js
packages/app-desktop/commands/focusElement.js
@@ -168,8 +173,6 @@ packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.test.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.js
packages/app-desktop/gui/Dialog.js
packages/app-desktop/gui/DialogButtonRow.js
packages/app-desktop/gui/DialogButtonRow/useKeyboardHandler.js
@@ -188,6 +191,7 @@ packages/app-desktop/gui/IconButton.js
packages/app-desktop/gui/ImportScreen.js
packages/app-desktop/gui/ItemList.js
packages/app-desktop/gui/JoplinCloudConfigScreen.js
packages/app-desktop/gui/JoplinCloudLoginScreen.js
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js
packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js
packages/app-desktop/gui/KeymapConfig/styles/index.js
@@ -217,10 +221,13 @@ packages/app-desktop/gui/MainScreen/commands/openItem.js
packages/app-desktop/gui/MainScreen/commands/openNote.js
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.js
packages/app-desktop/gui/MainScreen/commands/openTag.js
packages/app-desktop/gui/MainScreen/commands/permanentlyDeleteNote.js
packages/app-desktop/gui/MainScreen/commands/print.js
packages/app-desktop/gui/MainScreen/commands/renameFolder.js
packages/app-desktop/gui/MainScreen/commands/renameTag.js
packages/app-desktop/gui/MainScreen/commands/resetLayout.js
packages/app-desktop/gui/MainScreen/commands/restoreFolder.js
packages/app-desktop/gui/MainScreen/commands/restoreNote.js
packages/app-desktop/gui/MainScreen/commands/revealResourceFile.js
packages/app-desktop/gui/MainScreen/commands/search.js
packages/app-desktop/gui/MainScreen/commands/setTags.js
@@ -248,26 +255,30 @@ packages/app-desktop/gui/Navigator.js
packages/app-desktop/gui/NoteContentPropertiesDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/normalizeAccelerator.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/normalizeAccelerator.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useExternalPlugins.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearchExtension.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearchHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useStyles.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useWebviewIpcMessage.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/Editor.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useCursorUtils.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useCursorUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useExternalPlugins.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useJoplinCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useJoplinMode.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useLineSorting.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useListIdent.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/Editor.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
@@ -279,6 +290,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
packages/app-desktop/gui/NoteEditor/NoteEditor.js
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
@@ -310,9 +322,7 @@ packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
packages/app-desktop/gui/NoteList/NoteList.js
packages/app-desktop/gui/NoteList/NoteList2.js
packages/app-desktop/gui/NoteList/NoteListSource.js
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
packages/app-desktop/gui/NoteList/commands/index.js
packages/app-desktop/gui/NoteList/utils/canManuallySortNotes.js
@@ -329,9 +339,18 @@ packages/app-desktop/gui/NoteList/utils/useVisibleRange.js
packages/app-desktop/gui/NoteListControls/NoteListControls.js
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
packages/app-desktop/gui/NoteListControls/commands/index.js
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListHeader/NoteListHeader.js
packages/app-desktop/gui/NoteListHeader/NoteListHeaderItem.js
packages/app-desktop/gui/NoteListHeader/types.js
packages/app-desktop/gui/NoteListHeader/useDragAndDrop.test.js
packages/app-desktop/gui/NoteListHeader/useDragAndDrop.js
packages/app-desktop/gui/NoteListHeader/utils/getColumnTitle.js
packages/app-desktop/gui/NoteListHeader/utils/useContextMenu.js
packages/app-desktop/gui/NoteListHeader/utils/validateColumns.test.js
packages/app-desktop/gui/NoteListHeader/utils/validateColumns.js
packages/app-desktop/gui/NoteListItem/NoteListItem.js
packages/app-desktop/gui/NoteListItem/utils/getNoteTitleHtml.js
packages/app-desktop/gui/NoteListItem/utils/prepareViewProps.test.js
packages/app-desktop/gui/NoteListItem/utils/prepareViewProps.js
packages/app-desktop/gui/NoteListItem/utils/types.js
packages/app-desktop/gui/NoteListItem/utils/useItemElement.js
@@ -346,6 +365,7 @@ packages/app-desktop/gui/NoteSearchBar.js
packages/app-desktop/gui/NoteStatusBar.js
packages/app-desktop/gui/NoteTextViewer.js
packages/app-desktop/gui/NoteToolbar/NoteToolbar.js
packages/app-desktop/gui/NotyfContext.js
packages/app-desktop/gui/OneDriveLoginScreen.js
packages/app-desktop/gui/PasswordInput/PasswordInput.js
packages/app-desktop/gui/PdfViewer.js
@@ -390,6 +410,7 @@ packages/app-desktop/gui/ToolbarBase.js
packages/app-desktop/gui/ToolbarButton/ToolbarButton.js
packages/app-desktop/gui/ToolbarButton/styles/index.js
packages/app-desktop/gui/ToolbarSpace.js
packages/app-desktop/gui/TrashNotification/TrashNotification.js
packages/app-desktop/gui/dialogs.js
packages/app-desktop/gui/hooks/useEffectDebugger.js
packages/app-desktop/gui/hooks/useImperativeHandlerDebugger.js
@@ -405,6 +426,7 @@ packages/app-desktop/gui/style/StyledMessage.js
packages/app-desktop/gui/style/StyledTextInput.js
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
packages/app-desktop/gui/utils/dragAndDrop.js
packages/app-desktop/gui/utils/loadScript.js
packages/app-desktop/gulpfile.js
packages/app-desktop/integration-tests/main.spec.js
@@ -438,7 +460,6 @@ packages/app-desktop/services/plugins/hooks/useThemeCss.js
packages/app-desktop/services/plugins/hooks/useViewIsReady.js
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js
packages/app-desktop/services/restart.js
packages/app-desktop/services/share/invitationRespond.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
@@ -452,10 +473,17 @@ packages/app-desktop/utils/7zip/pathToBundled7Zip.js
packages/app-desktop/utils/checkForUpdatesUtils.test.js
packages/app-desktop/utils/checkForUpdatesUtils.js
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
packages/app-desktop/utils/isSafeToOpen.test.js
packages/app-desktop/utils/isSafeToOpen.js
packages/app-desktop/utils/markupLanguageUtils.js
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
packages/app-desktop/utils/restartInSafeModeFromMain.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/commands/index.js
packages/app-mobile/commands/openItem.js
packages/app-mobile/commands/openNote.js
packages/app-mobile/commands/scrollToHash.js
packages/app-mobile/commands/util/goToNote.js
packages/app-mobile/components/ActionButton.js
packages/app-mobile/components/BackButtonDialogBox.js
packages/app-mobile/components/CameraView.js
@@ -466,14 +494,24 @@ packages/app-mobile/components/ExtendedWebView.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/Icon.js
packages/app-mobile/components/Modal.js
packages/app-mobile/components/ModalDialog.js
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.test.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/noteBodyViewerBundle.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/types.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/addPluginAssets.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/makeResourceModel.js
packages/app-mobile/components/NoteBodyViewer/hooks/useContentScripts.js
packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.test.js
packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnMessage.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
packages/app-mobile/components/NoteBodyViewer/hooks/useRenderer.js
packages/app-mobile/components/NoteBodyViewer/hooks/useRerenderHandler.js
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js
packages/app-mobile/components/NoteBodyViewer/types.js
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.js
packages/app-mobile/components/NoteEditor/EditLinkDialog.js
packages/app-mobile/components/NoteEditor/ImageEditor/ImageEditor.js
packages/app-mobile/components/NoteEditor/ImageEditor/autosave.js
@@ -495,17 +533,25 @@ packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useActionButto
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useHeaderButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useInlineFormattingButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useListButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/usePluginButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
packages/app-mobile/components/NoteEditor/NoteEditor.js
packages/app-mobile/components/NoteEditor/SearchPanel.js
packages/app-mobile/components/NoteEditor/commandDeclarations.js
packages/app-mobile/components/NoteEditor/hooks/useCodeMirrorPlugins.js
packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.test.js
packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.js
packages/app-mobile/components/NoteEditor/hooks/useKeyboardVisible.js
packages/app-mobile/components/NoteEditor/types.js
packages/app-mobile/components/NoteList.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js
packages/app-mobile/components/ScreenHeader.js
packages/app-mobile/components/ScreenHeader/WarningBanner.test.js
packages/app-mobile/components/ScreenHeader/WarningBanner.js
packages/app-mobile/components/ScreenHeader/WarningBox.js
packages/app-mobile/components/ScreenHeader/index.js
packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/TextInput.js
@@ -516,15 +562,20 @@ packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/getResponsiveValue.test.js
packages/app-mobile/components/getResponsiveValue.js
packages/app-mobile/components/global-style.js
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
packages/app-mobile/components/screens/ConfigScreen/FileSystemPathSelector.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/ExportDebugReportButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/ExportProfileButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.test.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteImportButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/TaskButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/exportAllFolders.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/exportDebugReport.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/exportProfile.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/makeImportExportCacheDirectory.js
packages/app-mobile/components/screens/ConfigScreen/SectionDescription.js
packages/app-mobile/components/screens/ConfigScreen/SectionHeader.js
packages/app-mobile/components/screens/ConfigScreen/SectionSelector.js
packages/app-mobile/components/screens/ConfigScreen/SettingComponent.js
@@ -532,16 +583,59 @@ packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
packages/app-mobile/components/screens/ConfigScreen/SettingsToggle.js
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/ActionButton.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.test.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginToggle.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginUploadButton.js
packages/app-mobile/components/screens/ConfigScreen/plugins/SearchPlugins.test.js
packages/app-mobile/components/screens/ConfigScreen/plugins/SearchPlugins.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/newRepoApi.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/pluginServiceSetup.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/openWebsiteForPlugin.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/useRepoApi.js
packages/app-mobile/components/screens/ConfigScreen/types.js
packages/app-mobile/components/screens/JoplinCloudLoginScreen.js
packages/app-mobile/components/screens/LogScreen.js
packages/app-mobile/components/screens/Note.js
packages/app-mobile/components/screens/NoteTagsDialog.js
packages/app-mobile/components/screens/Notes.js
packages/app-mobile/components/screens/ShareManager/AcceptedShareItem.js
packages/app-mobile/components/screens/ShareManager/IncomingShareItem.js
packages/app-mobile/components/screens/ShareManager/index.test.js
packages/app-mobile/components/screens/ShareManager/index.js
packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js
packages/app-mobile/components/screens/encryption-config.js
packages/app-mobile/components/screens/search.js
packages/app-mobile/components/screens/status.js
packages/app-mobile/components/side-menu-content.js
packages/app-mobile/components/voiceTyping/VoiceTypingDialog.js
packages/app-mobile/gulpfile.js
packages/app-mobile/plugins/PlatformImplementation.js
packages/app-mobile/plugins/PluginRunner/PluginRunner.js
packages/app-mobile/plugins/PluginRunner/PluginRunnerWebView.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/initializeDialogWebView.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/initializePluginBackgroundIframe.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/pluginRunnerBackgroundPage.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.test.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/reportUnhandledErrors.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/wrapConsoleLog.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginDialogManager.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginDialogWebView.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginPanelViewer.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginUserWebView.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useDialogMessenger.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useDialogSize.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useViewInfos.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useWebViewSetup.js
packages/app-mobile/plugins/PluginRunner/types.js
packages/app-mobile/plugins/PluginRunner/utils/createOnLogHandler.js
packages/app-mobile/plugins/hooks/usePlugin.js
packages/app-mobile/plugins/loadPlugins.js
packages/app-mobile/root.js
packages/app-mobile/services/AlarmServiceDriver.android.js
packages/app-mobile/services/AlarmServiceDriver.ios.js
@@ -558,14 +652,29 @@ packages/app-mobile/utils/ShareExtension.js
packages/app-mobile/utils/ShareUtils.test.js
packages/app-mobile/utils/ShareUtils.js
packages/app-mobile/utils/TlsUtils.js
packages/app-mobile/utils/appDefaultState.js
packages/app-mobile/utils/autodetectTheme.js
packages/app-mobile/utils/checkPermissions.js
packages/app-mobile/utils/createRootStyle.js
packages/app-mobile/utils/debounce.js
packages/app-mobile/utils/fs-driver/constants.js
packages/app-mobile/utils/fs-driver/fs-driver-rn.js
packages/app-mobile/utils/fs-driver/runOnDeviceTests.js
packages/app-mobile/utils/fs-driver/tarCreate.js
packages/app-mobile/utils/fs-driver/tarExtract.test.js
packages/app-mobile/utils/fs-driver/tarExtract.js
packages/app-mobile/utils/fs-driver/testUtil/createFilesFromPathRecord.js
packages/app-mobile/utils/fs-driver/testUtil/verifyDirectoryMatches.js
packages/app-mobile/utils/initializeCommandService.js
packages/app-mobile/utils/ipc/RNToWebViewMessenger.js
packages/app-mobile/utils/ipc/WebViewToRNMessenger.js
packages/app-mobile/utils/pickDocument.js
packages/app-mobile/utils/polyfills/bufferPolyfill.js
packages/app-mobile/utils/polyfills/index.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/showMessageBox.js
packages/app-mobile/utils/testing/createMockReduxStore.js
packages/app-mobile/utils/types.js
packages/default-plugins/build.js
packages/default-plugins/buildDefaultPlugins.js
@@ -575,6 +684,7 @@ packages/default-plugins/utils/getCurrentCommitHash.js
packages/default-plugins/utils/getPathToPatchFileFor.js
packages/default-plugins/utils/readRepositoryJson.js
packages/default-plugins/utils/waitForCliInput.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5BuiltInOptions.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.js
packages/editor/CodeMirror/CodeMirror5Emulation/Decorator.js
@@ -583,10 +693,15 @@ packages/editor/CodeMirror/CodeMirrorControl.js
packages/editor/CodeMirror/configFromSettings.js
packages/editor/CodeMirror/createEditor.test.js
packages/editor/CodeMirror/createEditor.js
packages/editor/CodeMirror/editorCommands/duplicateLine.test.js
packages/editor/CodeMirror/editorCommands/duplicateLine.js
packages/editor/CodeMirror/editorCommands/editorCommands.js
packages/editor/CodeMirror/editorCommands/insertLineAfter.test.js
packages/editor/CodeMirror/editorCommands/insertLineAfter.js
packages/editor/CodeMirror/editorCommands/sortSelectedLines.test.js
packages/editor/CodeMirror/editorCommands/sortSelectedLines.js
packages/editor/CodeMirror/editorCommands/supportsCommand.js
packages/editor/CodeMirror/editorCommands/swapLine.test.js
packages/editor/CodeMirror/editorCommands/swapLine.js
packages/editor/CodeMirror/getScrollFraction.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/allLanguages.js
@@ -613,6 +728,7 @@ packages/editor/CodeMirror/testUtil/createEditorSettings.js
packages/editor/CodeMirror/testUtil/createTestEditor.js
packages/editor/CodeMirror/testUtil/forceFullParse.js
packages/editor/CodeMirror/testUtil/loadLanguages.js
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
packages/editor/CodeMirror/testUtil/typeText.js
packages/editor/CodeMirror/theme.js
packages/editor/CodeMirror/util/isInSyntaxNode.js
@@ -639,6 +755,7 @@ packages/generator-joplin/generators/app/templates/api/noteListType.js
packages/generator-joplin/generators/app/templates/api/types.js
packages/generator-joplin/generators/app/templates/api_index.js
packages/generator-joplin/generators/app/templates/src/index.js
packages/generator-joplin/tools/updateCategories.js
packages/htmlpack/src/index.js
packages/lib/ArrayUtils.js
packages/lib/AsyncActionQueue.js
@@ -678,16 +795,22 @@ packages/lib/commands/openMasterPasswordDialog.js
packages/lib/commands/synchronize.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/config/config-shared.js
packages/lib/components/shared/config/plugins/types.js
packages/lib/components/shared/config/plugins/useOnDeleteHandler.js
packages/lib/components/shared/config/plugins/useOnInstallHandler.test.js
packages/lib/components/shared/config/plugins/useOnInstallHandler.js
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.js
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.js
packages/lib/components/shared/note-screen-shared.js
packages/lib/components/shared/reduxSharedMiddleware.js
packages/lib/components/shared/side-menu-shared.test.js
packages/lib/components/shared/side-menu-shared.js
packages/lib/database-driver-better-sqlite.js
packages/lib/database.js
packages/lib/debug/DebugService.js
packages/lib/determineBaseAppDirs.js
packages/lib/dom.js
packages/lib/downloadController.js
packages/lib/errorUtils.js
packages/lib/errors.js
packages/lib/eventManager.js
@@ -704,6 +827,7 @@ packages/lib/geolocation-node.js
packages/lib/hooks/useAsyncEffect.js
packages/lib/hooks/useElementSize.js
packages/lib/hooks/useEventListener.js
packages/lib/hooks/usePrevious.js
packages/lib/htmlUtils.test.js
packages/lib/htmlUtils.js
packages/lib/htmlUtils2.test.js
@@ -748,11 +872,17 @@ packages/lib/models/Tag.test.js
packages/lib/models/Tag.js
packages/lib/models/dateTimeFormats.test.js
packages/lib/models/settings/FileHandler.js
packages/lib/models/settings/settingValidations.test.js
packages/lib/models/settings/settingValidations.js
packages/lib/models/utils/getCollator.js
packages/lib/models/utils/getConflictFolderId.js
packages/lib/models/utils/isItemId.js
packages/lib/models/utils/itemCanBeEncrypted.js
packages/lib/models/utils/onFolderDrop.test.js
packages/lib/models/utils/onFolderDrop.js
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/readOnly.test.js
packages/lib/models/utils/readOnly.js
packages/lib/models/utils/resourceUtils.js
packages/lib/models/utils/types.js
@@ -782,6 +912,7 @@ packages/lib/services/KvStore.js
packages/lib/services/MigrationService.js
packages/lib/services/NavService.js
packages/lib/services/PostMessageService.js
packages/lib/services/ReportService.test.js
packages/lib/services/ReportService.js
packages/lib/services/ResourceEditWatcher/index.js
packages/lib/services/ResourceEditWatcher/reducer.js
@@ -809,6 +940,7 @@ packages/lib/services/database/migrations/43.js
packages/lib/services/database/migrations/44.js
packages/lib/services/database/migrations/45.js
packages/lib/services/database/migrations/46.js
packages/lib/services/database/migrations/47.js
packages/lib/services/database/migrations/index.js
packages/lib/services/database/sqlStringToLines.js
packages/lib/services/database/types.js
@@ -848,6 +980,7 @@ packages/lib/services/interop/InteropService_Importer_Raw.js
packages/lib/services/interop/Module.test.js
packages/lib/services/interop/Module.js
packages/lib/services/interop/types.js
packages/lib/services/joplinCloudUtils.js
packages/lib/services/joplinServer/personalizedUserContentBaseUrl.js
packages/lib/services/keychain/KeychainService.js
packages/lib/services/keychain/KeychainServiceDriver.dummy.js
@@ -856,6 +989,12 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js
packages/lib/services/keychain/KeychainServiceDriverBase.js
packages/lib/services/noteList/defaultLeftToRightListRenderer.js
packages/lib/services/noteList/defaultListRenderer.js
packages/lib/services/noteList/defaultMultiColumnsRenderer.js
packages/lib/services/noteList/depNameToNoteProp.js
packages/lib/services/noteList/renderTemplate.test.js
packages/lib/services/noteList/renderTemplate.js
packages/lib/services/noteList/renderViewProps.test.js
packages/lib/services/noteList/renderViewProps.js
packages/lib/services/noteList/renderers.js
packages/lib/services/ocr/OcrDriverBase.js
packages/lib/services/ocr/OcrService.test.js
@@ -901,6 +1040,13 @@ packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js
packages/lib/services/plugins/reducer.js
packages/lib/services/plugins/utils/createViewHandle.js
packages/lib/services/plugins/utils/executeSandboxCall.js
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
packages/lib/services/plugins/utils/getPluginSettingKeyPrefix.js
packages/lib/services/plugins/utils/getPluginSettingValue.js
packages/lib/services/plugins/utils/isCompatible/index.test.js
packages/lib/services/plugins/utils/isCompatible/index.js
packages/lib/services/plugins/utils/isCompatible/minVersionForPlatform.js
packages/lib/services/plugins/utils/isCompatible/types.js
packages/lib/services/plugins/utils/loadContentScripts.js
packages/lib/services/plugins/utils/makeListener.js
packages/lib/services/plugins/utils/manifestFromObject.js
@@ -908,6 +1054,8 @@ packages/lib/services/plugins/utils/mapEventHandlersToIds.js
packages/lib/services/plugins/utils/types.js
packages/lib/services/plugins/utils/validatePluginId.test.js
packages/lib/services/plugins/utils/validatePluginId.js
packages/lib/services/plugins/utils/validatePluginPlatforms.test.js
packages/lib/services/plugins/utils/validatePluginPlatforms.js
packages/lib/services/plugins/utils/validatePluginVersion.test.js
packages/lib/services/plugins/utils/validatePluginVersion.js
packages/lib/services/profileConfig/index.test.js
@@ -923,6 +1071,7 @@ packages/lib/services/rest/actionApi.desktop.js
packages/lib/services/rest/routes/auth.js
packages/lib/services/rest/routes/events.test.js
packages/lib/services/rest/routes/events.js
packages/lib/services/rest/routes/folders.test.js
packages/lib/services/rest/routes/folders.js
packages/lib/services/rest/routes/master_keys.js
packages/lib/services/rest/routes/notes.test.js
@@ -954,6 +1103,7 @@ packages/lib/services/search/gotoAnythingStyleQuery.js
packages/lib/services/search/queryBuilder.js
packages/lib/services/share/ShareService.test.js
packages/lib/services/share/ShareService.js
packages/lib/services/share/invitationRespond.js
packages/lib/services/share/reducer.js
packages/lib/services/spellChecker/SpellCheckerService.js
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
@@ -991,10 +1141,22 @@ packages/lib/services/synchronizer/utils/handleSyncStartupOperation.js
packages/lib/services/synchronizer/utils/resourceRemotePath.js
packages/lib/services/synchronizer/utils/syncDeleteStep.js
packages/lib/services/synchronizer/utils/types.js
packages/lib/services/trash/emptyTrash.test.js
packages/lib/services/trash/emptyTrash.js
packages/lib/services/trash/getTrashFolderId.js
packages/lib/services/trash/index.test.js
packages/lib/services/trash/index.js
packages/lib/services/trash/isTrashableItem.js
packages/lib/services/trash/permanentlyDeleteOldItems.test.js
packages/lib/services/trash/permanentlyDeleteOldItems.js
packages/lib/services/trash/restoreItems.test.js
packages/lib/services/trash/restoreItems.js
packages/lib/shim-init-node.js
packages/lib/shim.js
packages/lib/string-utils.test.js
packages/lib/string-utils.js
packages/lib/testing/share/makeMockShareInvitation.js
packages/lib/testing/share/mockShareService.js
packages/lib/testing/syncTargetUtils.js
packages/lib/testing/test-utils-synchronizer.js
packages/lib/testing/test-utils.js
@@ -1010,7 +1172,20 @@ packages/lib/themes/solarizedLight.js
packages/lib/themes/type.js
packages/lib/time.js
packages/lib/types.js
packages/lib/utils/ActionLogger.test.js
packages/lib/utils/ActionLogger.js
packages/lib/utils/credentialFiles.js
packages/lib/utils/focusHandler.js
packages/lib/utils/ipc/RemoteMessenger.test.js
packages/lib/utils/ipc/RemoteMessenger.js
packages/lib/utils/ipc/TestMessenger.js
packages/lib/utils/ipc/WindowMessenger.js
packages/lib/utils/ipc/types.js
packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.test.js
packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.js
packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.test.js
packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.js
packages/lib/utils/ipc/utils/separateCallbacksFromSerializableArray.js
packages/lib/utils/joplinCloud.js
packages/lib/utils/processStartFlags.js
packages/lib/utils/replaceUnsupportedCharacters.test.js
@@ -1082,6 +1257,7 @@ packages/renderer/MdToHtml/rules/link_open.js
packages/renderer/MdToHtml/rules/mermaid.js
packages/renderer/MdToHtml/rules/sanitize_html.js
packages/renderer/MdToHtml/rules/source_map.js
packages/renderer/MdToHtml/rules/tableHorizontallyScrollable.js
packages/renderer/MdToHtml/setupLinkify.js
packages/renderer/MdToHtml/validateLinks.js
packages/renderer/assetsToHeaders.js
@@ -1113,6 +1289,7 @@ packages/tools/packageJsonLint.js
packages/tools/postPreReleasesToForum.js
packages/tools/release-android.js
packages/tools/release-cli.js
packages/tools/release-clipper.js
packages/tools/release-electron.js
packages/tools/release-ios.js
packages/tools/release-plugin-repo-cli.js

View File

@@ -101,6 +101,19 @@ module.exports = {
'no-unneeded-ternary': 'error',
'github/array-foreach': ['error'],
'no-restricted-properties': ['error',
{
'property': 'focus',
'message': 'Please use focusHandler::focus() instead',
},
{
'property': 'blur',
'message': 'Please use focusHandler::blur() instead',
},
],
'@typescript-eslint/no-explicit-any': ['error'],
// -------------------------------
// Formatting
// -------------------------------

View File

@@ -90,7 +90,7 @@ if [ "$RUN_TESTS" == "1" ]; then
# On Linux, we run the Joplin Server tests using PostgreSQL
if [ "$IS_LINUX" == "1" ]; then
echo "Running Joplin Server tests using PostgreSQL..."
sudo docker-compose --file docker-compose.db-dev.yml up -d
sudo docker compose --file docker-compose.db-dev.yml up -d
cmdResult=$?
if [ $cmdResult -ne 0 ]; then
exit $cmdResult
@@ -141,15 +141,13 @@ fi
# for Linux only is sufficient.
# =============================================================================
if [ "$IS_PULL_REQUEST" == "1" ]; then
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Validating translations..."
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Validating translations..."
node packages/tools/validate-translation.js
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
node packages/tools/validate-translation.js
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
fi
@@ -179,15 +177,13 @@ fi
# See coding_style.md
# =============================================================================
if [ "$IS_PULL_REQUEST" == "1" ]; then
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Checking for files that should have been ignored..."
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Checking for files that should have been ignored..."
node packages/tools/checkIgnoredFiles.js
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
node packages/tools/checkIgnoredFiles.js
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
fi
@@ -196,14 +192,16 @@ fi
# =============================================================================
if [ "$RUN_TESTS" == "1" ]; then
echo "Step: Check that the website still builds..."
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Check that the website still builds..."
mkdir -p ../joplin-website/docs
ll ../joplin-website/docs/api/references/plugin_api
SKIP_SPONSOR_PROCESSING=1 yarn buildWebsite
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
mkdir -p ../joplin-website/docs
CROWDIN_PERSONAL_TOKEN="$CROWDIN_PERSONAL_TOKEN" yarn crowdinDownload
SKIP_SPONSOR_PROCESSING=1 yarn buildWebsite
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
fi
fi
@@ -211,15 +209,13 @@ fi
# Spellchecking
# =============================================================================
if [ "$IS_PULL_REQUEST" == "1" ]; then
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Spellchecking..."
if [ "$IS_LINUX" == "1" ]; then
echo "Step: Spellchecking..."
yarn spellcheck --all
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
yarn spellcheck --all
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
fi

View File

@@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v2
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'yarn'

View File

@@ -20,8 +20,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: olegtarasov/get-tag@v2.1
- uses: actions/setup-node@v2
- uses: olegtarasov/get-tag@v2.1.3
- uses: actions/setup-node@v4
with:
# We need to pin the version to 18.15, because 18.16+ fails with this error:
# https://github.com/facebook/react-native/issues/36440
@@ -40,7 +40,7 @@ jobs:
brew install pango
# See github-action-main.yml for explanation
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'

View File

@@ -9,7 +9,7 @@ jobs:
if: github.repository == 'laurent22/joplin'
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
- uses: actions/stale@v9
with:
# Use this to do a dry run from a pull request
# debug-only: true

View File

@@ -82,8 +82,8 @@ jobs:
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
- uses: actions/checkout@v4
- uses: olegtarasov/get-tag@v2.1
- uses: actions/setup-node@v2
- uses: olegtarasov/get-tag@v2.1.3
- uses: actions/setup-node@v4
with:
# We need to pin the version to 18.15, because 18.16+ fails with this error:
# https://github.com/facebook/react-native/issues/36440
@@ -109,7 +109,7 @@ jobs:
# Python to an earlier version.
# Fixes error `ModuleNotFoundError: No module named 'distutils'`
# Ref: https://github.com/nodejs/node-gyp/issues/2869
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
@@ -127,6 +127,7 @@ jobs:
BUILD_SEQUENCIAL: 1
SERVER_REPOSITORY: joplin/server
SERVER_TAG_PREFIX: server
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
run: |
"${GITHUB_WORKSPACE}/.github/scripts/run_ci.sh"
@@ -182,7 +183,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v2
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'yarn'

213
.gitignore vendored
View File

@@ -89,7 +89,10 @@ packages/app-cli/app/command-mkbook.test.js
packages/app-cli/app/command-mkbook.js
packages/app-cli/app/command-mv.js
packages/app-cli/app/command-ren.js
packages/app-cli/app/command-restore.js
packages/app-cli/app/command-rmbook.test.js
packages/app-cli/app/command-rmbook.js
packages/app-cli/app/command-rmnote.test.js
packages/app-cli/app/command-rmnote.js
packages/app-cli/app/command-set.js
packages/app-cli/app/command-settingschema.js
@@ -97,6 +100,7 @@ packages/app-cli/app/command-sync.js
packages/app-cli/app/command-testing.js
packages/app-cli/app/command-use.js
packages/app-cli/app/command-version.js
packages/app-cli/app/gui/FolderListWidget.js
packages/app-cli/app/gui/StatusBarWidget.js
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/setupCommand.js
@@ -123,6 +127,7 @@ packages/app-desktop/bridge.js
packages/app-desktop/checkForUpdates.js
packages/app-desktop/commands/copyDevCommand.js
packages/app-desktop/commands/editProfileConfig.js
packages/app-desktop/commands/emptyTrash.js
packages/app-desktop/commands/exportFolders.js
packages/app-desktop/commands/exportNotes.js
packages/app-desktop/commands/focusElement.js
@@ -148,8 +153,6 @@ packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.test.js
packages/app-desktop/gui/ConfigScreen/controls/plugins/useOnInstallHandler.js
packages/app-desktop/gui/Dialog.js
packages/app-desktop/gui/DialogButtonRow.js
packages/app-desktop/gui/DialogButtonRow/useKeyboardHandler.js
@@ -168,6 +171,7 @@ packages/app-desktop/gui/IconButton.js
packages/app-desktop/gui/ImportScreen.js
packages/app-desktop/gui/ItemList.js
packages/app-desktop/gui/JoplinCloudConfigScreen.js
packages/app-desktop/gui/JoplinCloudLoginScreen.js
packages/app-desktop/gui/KeymapConfig/KeymapConfigScreen.js
packages/app-desktop/gui/KeymapConfig/ShortcutRecorder.js
packages/app-desktop/gui/KeymapConfig/styles/index.js
@@ -197,10 +201,13 @@ packages/app-desktop/gui/MainScreen/commands/openItem.js
packages/app-desktop/gui/MainScreen/commands/openNote.js
packages/app-desktop/gui/MainScreen/commands/openPdfViewer.js
packages/app-desktop/gui/MainScreen/commands/openTag.js
packages/app-desktop/gui/MainScreen/commands/permanentlyDeleteNote.js
packages/app-desktop/gui/MainScreen/commands/print.js
packages/app-desktop/gui/MainScreen/commands/renameFolder.js
packages/app-desktop/gui/MainScreen/commands/renameTag.js
packages/app-desktop/gui/MainScreen/commands/resetLayout.js
packages/app-desktop/gui/MainScreen/commands/restoreFolder.js
packages/app-desktop/gui/MainScreen/commands/restoreNote.js
packages/app-desktop/gui/MainScreen/commands/revealResourceFile.js
packages/app-desktop/gui/MainScreen/commands/search.js
packages/app-desktop/gui/MainScreen/commands/setTags.js
@@ -228,26 +235,30 @@ packages/app-desktop/gui/Navigator.js
packages/app-desktop/gui/NoteContentPropertiesDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/normalizeAccelerator.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/normalizeAccelerator.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useExternalPlugins.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearchExtension.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearchHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useStyles.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useWebviewIpcMessage.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/CodeMirror.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/Editor.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useCursorUtils.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useCursorUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useExternalPlugins.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useJoplinCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useJoplinMode.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useLineSorting.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useListIdent.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v5/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/CodeMirror.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/Editor.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
@@ -259,6 +270,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
packages/app-desktop/gui/NoteEditor/NoteEditor.js
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
@@ -290,9 +302,7 @@ packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
packages/app-desktop/gui/NoteList/NoteList.js
packages/app-desktop/gui/NoteList/NoteList2.js
packages/app-desktop/gui/NoteList/NoteListSource.js
packages/app-desktop/gui/NoteList/commands/focusElementNoteList.js
packages/app-desktop/gui/NoteList/commands/index.js
packages/app-desktop/gui/NoteList/utils/canManuallySortNotes.js
@@ -309,9 +319,18 @@ packages/app-desktop/gui/NoteList/utils/useVisibleRange.js
packages/app-desktop/gui/NoteListControls/NoteListControls.js
packages/app-desktop/gui/NoteListControls/commands/focusSearch.js
packages/app-desktop/gui/NoteListControls/commands/index.js
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListHeader/NoteListHeader.js
packages/app-desktop/gui/NoteListHeader/NoteListHeaderItem.js
packages/app-desktop/gui/NoteListHeader/types.js
packages/app-desktop/gui/NoteListHeader/useDragAndDrop.test.js
packages/app-desktop/gui/NoteListHeader/useDragAndDrop.js
packages/app-desktop/gui/NoteListHeader/utils/getColumnTitle.js
packages/app-desktop/gui/NoteListHeader/utils/useContextMenu.js
packages/app-desktop/gui/NoteListHeader/utils/validateColumns.test.js
packages/app-desktop/gui/NoteListHeader/utils/validateColumns.js
packages/app-desktop/gui/NoteListItem/NoteListItem.js
packages/app-desktop/gui/NoteListItem/utils/getNoteTitleHtml.js
packages/app-desktop/gui/NoteListItem/utils/prepareViewProps.test.js
packages/app-desktop/gui/NoteListItem/utils/prepareViewProps.js
packages/app-desktop/gui/NoteListItem/utils/types.js
packages/app-desktop/gui/NoteListItem/utils/useItemElement.js
@@ -326,6 +345,7 @@ packages/app-desktop/gui/NoteSearchBar.js
packages/app-desktop/gui/NoteStatusBar.js
packages/app-desktop/gui/NoteTextViewer.js
packages/app-desktop/gui/NoteToolbar/NoteToolbar.js
packages/app-desktop/gui/NotyfContext.js
packages/app-desktop/gui/OneDriveLoginScreen.js
packages/app-desktop/gui/PasswordInput/PasswordInput.js
packages/app-desktop/gui/PdfViewer.js
@@ -370,6 +390,7 @@ packages/app-desktop/gui/ToolbarBase.js
packages/app-desktop/gui/ToolbarButton/ToolbarButton.js
packages/app-desktop/gui/ToolbarButton/styles/index.js
packages/app-desktop/gui/ToolbarSpace.js
packages/app-desktop/gui/TrashNotification/TrashNotification.js
packages/app-desktop/gui/dialogs.js
packages/app-desktop/gui/hooks/useEffectDebugger.js
packages/app-desktop/gui/hooks/useImperativeHandlerDebugger.js
@@ -385,6 +406,7 @@ packages/app-desktop/gui/style/StyledMessage.js
packages/app-desktop/gui/style/StyledTextInput.js
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
packages/app-desktop/gui/utils/dragAndDrop.js
packages/app-desktop/gui/utils/loadScript.js
packages/app-desktop/gulpfile.js
packages/app-desktop/integration-tests/main.spec.js
@@ -418,7 +440,6 @@ packages/app-desktop/services/plugins/hooks/useThemeCss.js
packages/app-desktop/services/plugins/hooks/useViewIsReady.js
packages/app-desktop/services/plugins/hooks/useWebviewToPluginMessages.js
packages/app-desktop/services/restart.js
packages/app-desktop/services/share/invitationRespond.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/PerFolderSortOrderService.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
@@ -432,10 +453,17 @@ packages/app-desktop/utils/7zip/pathToBundled7Zip.js
packages/app-desktop/utils/checkForUpdatesUtils.test.js
packages/app-desktop/utils/checkForUpdatesUtils.js
packages/app-desktop/utils/checkForUpdatesUtilsTestData.js
packages/app-desktop/utils/isSafeToOpen.test.js
packages/app-desktop/utils/isSafeToOpen.js
packages/app-desktop/utils/markupLanguageUtils.js
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
packages/app-desktop/utils/restartInSafeModeFromMain.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/commands/index.js
packages/app-mobile/commands/openItem.js
packages/app-mobile/commands/openNote.js
packages/app-mobile/commands/scrollToHash.js
packages/app-mobile/commands/util/goToNote.js
packages/app-mobile/components/ActionButton.js
packages/app-mobile/components/BackButtonDialogBox.js
packages/app-mobile/components/CameraView.js
@@ -446,14 +474,24 @@ packages/app-mobile/components/ExtendedWebView.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/Icon.js
packages/app-mobile/components/Modal.js
packages/app-mobile/components/ModalDialog.js
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.test.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/noteBodyViewerBundle.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/types.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/addPluginAssets.js
packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/makeResourceModel.js
packages/app-mobile/components/NoteBodyViewer/hooks/useContentScripts.js
packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.test.js
packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnMessage.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
packages/app-mobile/components/NoteBodyViewer/hooks/useRenderer.js
packages/app-mobile/components/NoteBodyViewer/hooks/useRerenderHandler.js
packages/app-mobile/components/NoteBodyViewer/hooks/useSource.js
packages/app-mobile/components/NoteBodyViewer/types.js
packages/app-mobile/components/NoteEditor/CodeMirror/CodeMirror.js
packages/app-mobile/components/NoteEditor/CodeMirror/webviewLogger.js
packages/app-mobile/components/NoteEditor/EditLinkDialog.js
packages/app-mobile/components/NoteEditor/ImageEditor/ImageEditor.js
packages/app-mobile/components/NoteEditor/ImageEditor/autosave.js
@@ -475,17 +513,25 @@ packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useActionButto
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useHeaderButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useInlineFormattingButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/useListButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/buttons/usePluginButtons.js
packages/app-mobile/components/NoteEditor/MarkdownToolbar/types.js
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
packages/app-mobile/components/NoteEditor/NoteEditor.js
packages/app-mobile/components/NoteEditor/SearchPanel.js
packages/app-mobile/components/NoteEditor/commandDeclarations.js
packages/app-mobile/components/NoteEditor/hooks/useCodeMirrorPlugins.js
packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.test.js
packages/app-mobile/components/NoteEditor/hooks/useEditorCommandHandler.js
packages/app-mobile/components/NoteEditor/hooks/useKeyboardVisible.js
packages/app-mobile/components/NoteEditor/types.js
packages/app-mobile/components/NoteList.js
packages/app-mobile/components/ProfileSwitcher/ProfileEditor.js
packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.js
packages/app-mobile/components/ProfileSwitcher/useProfileConfig.js
packages/app-mobile/components/ScreenHeader.js
packages/app-mobile/components/ScreenHeader/WarningBanner.test.js
packages/app-mobile/components/ScreenHeader/WarningBanner.js
packages/app-mobile/components/ScreenHeader/WarningBox.js
packages/app-mobile/components/ScreenHeader/index.js
packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/TextInput.js
@@ -496,15 +542,20 @@ packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/getResponsiveValue.test.js
packages/app-mobile/components/getResponsiveValue.js
packages/app-mobile/components/global-style.js
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
packages/app-mobile/components/screens/ConfigScreen/FileSystemPathSelector.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/ExportDebugReportButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/ExportProfileButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.test.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteExportButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/NoteImportButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/TaskButton.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/exportAllFolders.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/exportDebugReport.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/exportProfile.js
packages/app-mobile/components/screens/ConfigScreen/NoteExportSection/utils/makeImportExportCacheDirectory.js
packages/app-mobile/components/screens/ConfigScreen/SectionDescription.js
packages/app-mobile/components/screens/ConfigScreen/SectionHeader.js
packages/app-mobile/components/screens/ConfigScreen/SectionSelector.js
packages/app-mobile/components/screens/ConfigScreen/SettingComponent.js
@@ -512,16 +563,59 @@ packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
packages/app-mobile/components/screens/ConfigScreen/SettingsToggle.js
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/ActionButton.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.test.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginStates.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginToggle.js
packages/app-mobile/components/screens/ConfigScreen/plugins/PluginUploadButton.js
packages/app-mobile/components/screens/ConfigScreen/plugins/SearchPlugins.test.js
packages/app-mobile/components/screens/ConfigScreen/plugins/SearchPlugins.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/newRepoApi.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/pluginServiceSetup.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/openWebsiteForPlugin.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/useRepoApi.js
packages/app-mobile/components/screens/ConfigScreen/types.js
packages/app-mobile/components/screens/JoplinCloudLoginScreen.js
packages/app-mobile/components/screens/LogScreen.js
packages/app-mobile/components/screens/Note.js
packages/app-mobile/components/screens/NoteTagsDialog.js
packages/app-mobile/components/screens/Notes.js
packages/app-mobile/components/screens/ShareManager/AcceptedShareItem.js
packages/app-mobile/components/screens/ShareManager/IncomingShareItem.js
packages/app-mobile/components/screens/ShareManager/index.test.js
packages/app-mobile/components/screens/ShareManager/index.js
packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js
packages/app-mobile/components/screens/encryption-config.js
packages/app-mobile/components/screens/search.js
packages/app-mobile/components/screens/status.js
packages/app-mobile/components/side-menu-content.js
packages/app-mobile/components/voiceTyping/VoiceTypingDialog.js
packages/app-mobile/gulpfile.js
packages/app-mobile/plugins/PlatformImplementation.js
packages/app-mobile/plugins/PluginRunner/PluginRunner.js
packages/app-mobile/plugins/PluginRunner/PluginRunnerWebView.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/initializeDialogWebView.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/initializePluginBackgroundIframe.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/pluginRunnerBackgroundPage.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.test.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/reportUnhandledErrors.js
packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/wrapConsoleLog.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginDialogManager.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginDialogWebView.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginPanelViewer.js
packages/app-mobile/plugins/PluginRunner/dialogs/PluginUserWebView.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useDialogMessenger.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useDialogSize.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useViewInfos.js
packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useWebViewSetup.js
packages/app-mobile/plugins/PluginRunner/types.js
packages/app-mobile/plugins/PluginRunner/utils/createOnLogHandler.js
packages/app-mobile/plugins/hooks/usePlugin.js
packages/app-mobile/plugins/loadPlugins.js
packages/app-mobile/root.js
packages/app-mobile/services/AlarmServiceDriver.android.js
packages/app-mobile/services/AlarmServiceDriver.ios.js
@@ -538,14 +632,29 @@ packages/app-mobile/utils/ShareExtension.js
packages/app-mobile/utils/ShareUtils.test.js
packages/app-mobile/utils/ShareUtils.js
packages/app-mobile/utils/TlsUtils.js
packages/app-mobile/utils/appDefaultState.js
packages/app-mobile/utils/autodetectTheme.js
packages/app-mobile/utils/checkPermissions.js
packages/app-mobile/utils/createRootStyle.js
packages/app-mobile/utils/debounce.js
packages/app-mobile/utils/fs-driver/constants.js
packages/app-mobile/utils/fs-driver/fs-driver-rn.js
packages/app-mobile/utils/fs-driver/runOnDeviceTests.js
packages/app-mobile/utils/fs-driver/tarCreate.js
packages/app-mobile/utils/fs-driver/tarExtract.test.js
packages/app-mobile/utils/fs-driver/tarExtract.js
packages/app-mobile/utils/fs-driver/testUtil/createFilesFromPathRecord.js
packages/app-mobile/utils/fs-driver/testUtil/verifyDirectoryMatches.js
packages/app-mobile/utils/initializeCommandService.js
packages/app-mobile/utils/ipc/RNToWebViewMessenger.js
packages/app-mobile/utils/ipc/WebViewToRNMessenger.js
packages/app-mobile/utils/pickDocument.js
packages/app-mobile/utils/polyfills/bufferPolyfill.js
packages/app-mobile/utils/polyfills/index.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/showMessageBox.js
packages/app-mobile/utils/testing/createMockReduxStore.js
packages/app-mobile/utils/types.js
packages/default-plugins/build.js
packages/default-plugins/buildDefaultPlugins.js
@@ -555,6 +664,7 @@ packages/default-plugins/utils/getCurrentCommitHash.js
packages/default-plugins/utils/getPathToPatchFileFor.js
packages/default-plugins/utils/readRepositoryJson.js
packages/default-plugins/utils/waitForCliInput.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5BuiltInOptions.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.js
packages/editor/CodeMirror/CodeMirror5Emulation/Decorator.js
@@ -563,10 +673,15 @@ packages/editor/CodeMirror/CodeMirrorControl.js
packages/editor/CodeMirror/configFromSettings.js
packages/editor/CodeMirror/createEditor.test.js
packages/editor/CodeMirror/createEditor.js
packages/editor/CodeMirror/editorCommands/duplicateLine.test.js
packages/editor/CodeMirror/editorCommands/duplicateLine.js
packages/editor/CodeMirror/editorCommands/editorCommands.js
packages/editor/CodeMirror/editorCommands/insertLineAfter.test.js
packages/editor/CodeMirror/editorCommands/insertLineAfter.js
packages/editor/CodeMirror/editorCommands/sortSelectedLines.test.js
packages/editor/CodeMirror/editorCommands/sortSelectedLines.js
packages/editor/CodeMirror/editorCommands/supportsCommand.js
packages/editor/CodeMirror/editorCommands/swapLine.test.js
packages/editor/CodeMirror/editorCommands/swapLine.js
packages/editor/CodeMirror/getScrollFraction.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/allLanguages.js
@@ -593,6 +708,7 @@ packages/editor/CodeMirror/testUtil/createEditorSettings.js
packages/editor/CodeMirror/testUtil/createTestEditor.js
packages/editor/CodeMirror/testUtil/forceFullParse.js
packages/editor/CodeMirror/testUtil/loadLanguages.js
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
packages/editor/CodeMirror/testUtil/typeText.js
packages/editor/CodeMirror/theme.js
packages/editor/CodeMirror/util/isInSyntaxNode.js
@@ -619,6 +735,7 @@ packages/generator-joplin/generators/app/templates/api/noteListType.js
packages/generator-joplin/generators/app/templates/api/types.js
packages/generator-joplin/generators/app/templates/api_index.js
packages/generator-joplin/generators/app/templates/src/index.js
packages/generator-joplin/tools/updateCategories.js
packages/htmlpack/src/index.js
packages/lib/ArrayUtils.js
packages/lib/AsyncActionQueue.js
@@ -658,16 +775,22 @@ packages/lib/commands/openMasterPasswordDialog.js
packages/lib/commands/synchronize.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/config/config-shared.js
packages/lib/components/shared/config/plugins/types.js
packages/lib/components/shared/config/plugins/useOnDeleteHandler.js
packages/lib/components/shared/config/plugins/useOnInstallHandler.test.js
packages/lib/components/shared/config/plugins/useOnInstallHandler.js
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.test.js
packages/lib/components/shared/config/shouldShowMissingPasswordWarning.js
packages/lib/components/shared/note-screen-shared.js
packages/lib/components/shared/reduxSharedMiddleware.js
packages/lib/components/shared/side-menu-shared.test.js
packages/lib/components/shared/side-menu-shared.js
packages/lib/database-driver-better-sqlite.js
packages/lib/database.js
packages/lib/debug/DebugService.js
packages/lib/determineBaseAppDirs.js
packages/lib/dom.js
packages/lib/downloadController.js
packages/lib/errorUtils.js
packages/lib/errors.js
packages/lib/eventManager.js
@@ -684,6 +807,7 @@ packages/lib/geolocation-node.js
packages/lib/hooks/useAsyncEffect.js
packages/lib/hooks/useElementSize.js
packages/lib/hooks/useEventListener.js
packages/lib/hooks/usePrevious.js
packages/lib/htmlUtils.test.js
packages/lib/htmlUtils.js
packages/lib/htmlUtils2.test.js
@@ -728,11 +852,17 @@ packages/lib/models/Tag.test.js
packages/lib/models/Tag.js
packages/lib/models/dateTimeFormats.test.js
packages/lib/models/settings/FileHandler.js
packages/lib/models/settings/settingValidations.test.js
packages/lib/models/settings/settingValidations.js
packages/lib/models/utils/getCollator.js
packages/lib/models/utils/getConflictFolderId.js
packages/lib/models/utils/isItemId.js
packages/lib/models/utils/itemCanBeEncrypted.js
packages/lib/models/utils/onFolderDrop.test.js
packages/lib/models/utils/onFolderDrop.js
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/readOnly.test.js
packages/lib/models/utils/readOnly.js
packages/lib/models/utils/resourceUtils.js
packages/lib/models/utils/types.js
@@ -762,6 +892,7 @@ packages/lib/services/KvStore.js
packages/lib/services/MigrationService.js
packages/lib/services/NavService.js
packages/lib/services/PostMessageService.js
packages/lib/services/ReportService.test.js
packages/lib/services/ReportService.js
packages/lib/services/ResourceEditWatcher/index.js
packages/lib/services/ResourceEditWatcher/reducer.js
@@ -789,6 +920,7 @@ packages/lib/services/database/migrations/43.js
packages/lib/services/database/migrations/44.js
packages/lib/services/database/migrations/45.js
packages/lib/services/database/migrations/46.js
packages/lib/services/database/migrations/47.js
packages/lib/services/database/migrations/index.js
packages/lib/services/database/sqlStringToLines.js
packages/lib/services/database/types.js
@@ -828,6 +960,7 @@ packages/lib/services/interop/InteropService_Importer_Raw.js
packages/lib/services/interop/Module.test.js
packages/lib/services/interop/Module.js
packages/lib/services/interop/types.js
packages/lib/services/joplinCloudUtils.js
packages/lib/services/joplinServer/personalizedUserContentBaseUrl.js
packages/lib/services/keychain/KeychainService.js
packages/lib/services/keychain/KeychainServiceDriver.dummy.js
@@ -836,6 +969,12 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js
packages/lib/services/keychain/KeychainServiceDriverBase.js
packages/lib/services/noteList/defaultLeftToRightListRenderer.js
packages/lib/services/noteList/defaultListRenderer.js
packages/lib/services/noteList/defaultMultiColumnsRenderer.js
packages/lib/services/noteList/depNameToNoteProp.js
packages/lib/services/noteList/renderTemplate.test.js
packages/lib/services/noteList/renderTemplate.js
packages/lib/services/noteList/renderViewProps.test.js
packages/lib/services/noteList/renderViewProps.js
packages/lib/services/noteList/renderers.js
packages/lib/services/ocr/OcrDriverBase.js
packages/lib/services/ocr/OcrService.test.js
@@ -881,6 +1020,13 @@ packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js
packages/lib/services/plugins/reducer.js
packages/lib/services/plugins/utils/createViewHandle.js
packages/lib/services/plugins/utils/executeSandboxCall.js
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
packages/lib/services/plugins/utils/getPluginSettingKeyPrefix.js
packages/lib/services/plugins/utils/getPluginSettingValue.js
packages/lib/services/plugins/utils/isCompatible/index.test.js
packages/lib/services/plugins/utils/isCompatible/index.js
packages/lib/services/plugins/utils/isCompatible/minVersionForPlatform.js
packages/lib/services/plugins/utils/isCompatible/types.js
packages/lib/services/plugins/utils/loadContentScripts.js
packages/lib/services/plugins/utils/makeListener.js
packages/lib/services/plugins/utils/manifestFromObject.js
@@ -888,6 +1034,8 @@ packages/lib/services/plugins/utils/mapEventHandlersToIds.js
packages/lib/services/plugins/utils/types.js
packages/lib/services/plugins/utils/validatePluginId.test.js
packages/lib/services/plugins/utils/validatePluginId.js
packages/lib/services/plugins/utils/validatePluginPlatforms.test.js
packages/lib/services/plugins/utils/validatePluginPlatforms.js
packages/lib/services/plugins/utils/validatePluginVersion.test.js
packages/lib/services/plugins/utils/validatePluginVersion.js
packages/lib/services/profileConfig/index.test.js
@@ -903,6 +1051,7 @@ packages/lib/services/rest/actionApi.desktop.js
packages/lib/services/rest/routes/auth.js
packages/lib/services/rest/routes/events.test.js
packages/lib/services/rest/routes/events.js
packages/lib/services/rest/routes/folders.test.js
packages/lib/services/rest/routes/folders.js
packages/lib/services/rest/routes/master_keys.js
packages/lib/services/rest/routes/notes.test.js
@@ -934,6 +1083,7 @@ packages/lib/services/search/gotoAnythingStyleQuery.js
packages/lib/services/search/queryBuilder.js
packages/lib/services/share/ShareService.test.js
packages/lib/services/share/ShareService.js
packages/lib/services/share/invitationRespond.js
packages/lib/services/share/reducer.js
packages/lib/services/spellChecker/SpellCheckerService.js
packages/lib/services/spellChecker/SpellCheckerServiceDriverBase.js
@@ -971,10 +1121,22 @@ packages/lib/services/synchronizer/utils/handleSyncStartupOperation.js
packages/lib/services/synchronizer/utils/resourceRemotePath.js
packages/lib/services/synchronizer/utils/syncDeleteStep.js
packages/lib/services/synchronizer/utils/types.js
packages/lib/services/trash/emptyTrash.test.js
packages/lib/services/trash/emptyTrash.js
packages/lib/services/trash/getTrashFolderId.js
packages/lib/services/trash/index.test.js
packages/lib/services/trash/index.js
packages/lib/services/trash/isTrashableItem.js
packages/lib/services/trash/permanentlyDeleteOldItems.test.js
packages/lib/services/trash/permanentlyDeleteOldItems.js
packages/lib/services/trash/restoreItems.test.js
packages/lib/services/trash/restoreItems.js
packages/lib/shim-init-node.js
packages/lib/shim.js
packages/lib/string-utils.test.js
packages/lib/string-utils.js
packages/lib/testing/share/makeMockShareInvitation.js
packages/lib/testing/share/mockShareService.js
packages/lib/testing/syncTargetUtils.js
packages/lib/testing/test-utils-synchronizer.js
packages/lib/testing/test-utils.js
@@ -990,7 +1152,20 @@ packages/lib/themes/solarizedLight.js
packages/lib/themes/type.js
packages/lib/time.js
packages/lib/types.js
packages/lib/utils/ActionLogger.test.js
packages/lib/utils/ActionLogger.js
packages/lib/utils/credentialFiles.js
packages/lib/utils/focusHandler.js
packages/lib/utils/ipc/RemoteMessenger.test.js
packages/lib/utils/ipc/RemoteMessenger.js
packages/lib/utils/ipc/TestMessenger.js
packages/lib/utils/ipc/WindowMessenger.js
packages/lib/utils/ipc/types.js
packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.test.js
packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.js
packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.test.js
packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.js
packages/lib/utils/ipc/utils/separateCallbacksFromSerializableArray.js
packages/lib/utils/joplinCloud.js
packages/lib/utils/processStartFlags.js
packages/lib/utils/replaceUnsupportedCharacters.test.js
@@ -1062,6 +1237,7 @@ packages/renderer/MdToHtml/rules/link_open.js
packages/renderer/MdToHtml/rules/mermaid.js
packages/renderer/MdToHtml/rules/sanitize_html.js
packages/renderer/MdToHtml/rules/source_map.js
packages/renderer/MdToHtml/rules/tableHorizontallyScrollable.js
packages/renderer/MdToHtml/setupLinkify.js
packages/renderer/MdToHtml/validateLinks.js
packages/renderer/assetsToHeaders.js
@@ -1093,6 +1269,7 @@ packages/tools/packageJsonLint.js
packages/tools/postPreReleasesToForum.js
packages/tools/release-android.js
packages/tools/release-cli.js
packages/tools/release-clipper.js
packages/tools/release-electron.js
packages/tools/release-ios.js
packages/tools/release-plugin-repo-cli.js

View File

@@ -0,0 +1,33 @@
diff --git a/lib/runner/index.js b/lib/runner/index.js
index 87e3b3957619728e3ed1ca61e2d83df1c49f928f..6d5ab905415da0577341c8f5b67d4806adcf7549 100644
--- a/lib/runner/index.js
+++ b/lib/runner/index.js
@@ -68,15 +68,19 @@ function run([, scriptPath, hookName = '', HUSKY_GIT_PARAMS], getStdinFn = get_s
return 0;
}
catch (err) {
- const noVerifyMessage = [
- 'commit-msg',
- 'pre-commit',
- 'pre-rebase',
- 'pre-push'
- ].includes(hookName)
- ? '(add --no-verify to bypass)'
- : '(cannot be bypassed with --no-verify due to Git specs)';
- console.log(`husky > ${hookName} hook failed ${noVerifyMessage}`);
+ // We do not want to print this "add --no-verify to bypass" message because that's
+ // literally what some developers do instead of trying to fix the errors.
+
+ // const noVerifyMessage = [
+ // 'commit-msg',
+ // 'pre-commit',
+ // 'pre-rebase',
+ // 'pre-push'
+ // ].includes(hookName)
+ // ? '(add --no-verify to bypass)'
+ // : '(cannot be bypassed with --no-verify due to Git specs)';
+
+ console.log(`husky > ${hookName} hook failed (Please fix the errors listed above and try again)`);
return err.code;
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,4 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Sat, 27 Jan 2024 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Sat, 27 Jan 2024 00:00:00 GMT</pubDate><item><title><![CDATA[Support for new plugin metadata]]></title><description><![CDATA[<p>The plugin manifest now supports new properties to better describe and present your plugins on Joplin Plugins website. Those are the <code>icons</code>, <code>categories</code>, <code>screenshots</code> and <code>promo_tile</code> properties.</p>
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Fri, 01 Mar 2024 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Fri, 01 Mar 2024 00:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 2.14]]></title><description><![CDATA[<h2>OCR<a name="ocr" href="#ocr" class="heading-anchor">🔗</a></h2>
<p>Optical Character Recognition (OCR) in Joplin enables the transformation of text-containing images into machine-readable text formats. From this version you can enable OCR in the Configuration screen under the &quot;General&quot; section. Once activated, Joplin scans images and PDFs, extracting text data for searchability.</p>
<p>While OCR search is available on both desktop and mobile apps, document scanning is limited to the desktop due to resource demands. For more information head to the <a href="https://joplinapp.org/help/apps/ocr">OCR official documentation</a>!</p>
<h2>Bundled plugins<a name="bundled-plugins" href="#bundled-plugins" class="heading-anchor">🔗</a></h2>
<p>Joplin will now bundle high quality plugins that we feel will benefit most users. With this version we include the great <a href="https://github.com/JackGruber/joplin-plugin-backup">Backup plugin</a> by JackGruber. This will provide another layer of safety when using Joplin as by default it will automatically backup your notes in a &quot;JoplinBackup&quot; folder in your home directory.</p>
<p>Note that, just like any other plugin, you can change the plugin configuration or even disable it from the settings.</p>
<h2>ENEX importer<a name="enex-importer" href="#enex-importer" class="heading-anchor">🔗</a></h2>
<p>As usual in recent version, there are plenty of improvements to the <a href="https://joplinapp.org/help/apps/import_export#importing-from-evernote">Joplin ENEX importer</a>. Besides the various fixes and enhancement to support this format, we've added a few useful features:</p>
<h3>Restore note links after importing an ENEX file<a name="restore-note-links-after-importing-an-enex-file" href="#restore-note-links-after-importing-an-enex-file" class="heading-anchor">🔗</a></h3>
<p>Evernote Export files do not include the necessary information to reliably restore the links between notes, so for a long time this feature was not supported by the importer.</p>
<p>Now however Joplin will try to guess what note is linked to what other note based on the note title, which in many cases will give the expected result. But not always - when that happens, and Joplin cannot detect the link target, the application leaves the original Evernote link. That way you can manually restore it yourself or at least find back what the note was linked to.</p>
<h3>Import a directory of ENEX files<a name="import-a-directory-of-enex-files" href="#import-a-directory-of-enex-files" class="heading-anchor">🔗</a></h3>
<p>It is notoriously difficult to export data from Evernote because, among other issues, you can only export one notebook at a time, which is an obvious problems when you have dozens of notebooks. Unfortunately we cannot improve this part of the process since this up to Evernote, however we now make it easier to import all these notebook files by adding support for importing a folder of ENEX files. To use this feature, go to File &gt; Import, and select one of the &quot;ENEX (Directory)&quot; options.</p>
<p>This will process all the ENEX files in that directory and create a notebook in Joplin for each of them.</p>
<h2>Beta Markdown editor<a name="beta-markdown-editor" href="#beta-markdown-editor" class="heading-anchor">🔗</a></h2>
<p>This version features further improvements to the new Markdown editor based on <a href="https://codemirror.net/">CodeMirror 6</a>. The goal eventually is to be able to use the same editor on both the desktop and mobile application (which already uses CodeMirror 6), which will allow a more consistent user experience across devices.</p>
<p>Plugin support has also been improved in this version - plugin authors can now write native CodeMirror 6 extensions using the plugin API. For more information check the documentation on <a href="https://joplinapp.org/help/api/tutorials/cm6_plugin/">how to create a Markdown plugin</a>!</p>
<p>Another benefit of this new editor is that, in a future version, it will allow us to support plugins on the mobile application since a plugin written for the desktop app will work on mobile too. There are several other advantages that Henry <a href="https://discourse.joplinapp.org/t/pre-release-v2-13-is-now-available-updated-18-11-2023/32697/12?u=laurent">listed in this forum post</a>.</p>
<h2>Rich text editor improvements<a name="rich-text-editor-improvements" href="#rich-text-editor-improvements" class="heading-anchor">🔗</a></h2>
<p>We continue making improvements to the Rich Text Editor (RTE) in particular to improve interoperability with other applications, such as LibreOffice, Office or web browsers, as well as better handling of copy and paste.</p>
<p>Another notable addition is support for setting colours, which was a frequently asked feature. To use the feature, select it from the &quot;...&quot; button in the toolbar. Note that once applied the colours will work in the Markdown editor too!</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20240301-rte-colors.png" alt=""></p>
<p>See below for the full list of RTE changes:</p>
<ul>
<li>Fixed: Rich text editor: Fix context menu not shown in some cases</li>
<li>Improved: Speed up pasting text and images in Rich Text Editor</li>
<li>Fixed: Fix drag-and-drop of images and text in the rich text editor</li>
<li>Fixed: Fix images with SVG data URLs corrupted in the rich text editor</li>
<li>Fixed: Pasting rich text in the RTE sometimes result in invalid markup</li>
<li>Fixed: Rich text editor: Fix newline behavior in new notes</li>
<li>Improved: Add support for changing text colors in rich text editor</li>
<li>Fixed: Fix HTML resource links lost when editing notes in the rich text editor</li>
<li>Fixed: Fix code blocks with blank lines break tables in the rich text editor</li>
<li>Fixed: Copied and pasted text from Firefox to RTE does not include images</li>
<li>Fixed: Pasting rich text in the RTE sometimes result in invalid markup</li>
<li>Fixed: Fixed copying and pasting an image from Chrome in RTE</li>
</ul>
<h1>Full changelog<a name="full-changelog" href="#full-changelog" class="heading-anchor">🔗</a></h1>
<p>This is just an overview of the main features. The full changelog is available there:</p>
<ul>
<li>Desktop: <a href="https://joplinapp.org/help/about/changelog/desktop">https://joplinapp.org/help/about/changelog/desktop</a></li>
<li>Android: <a href="https://joplinapp.org/help/about/changelog/android/">https://joplinapp.org/help/about/changelog/android/</a></li>
<li>iOS: <a href="https://joplinapp.org/help/about/changelog/ios/">https://joplinapp.org/help/about/changelog/ios/</a></li>
</ul>
]]></description><link>https://joplinapp.org/news/20240301-release-2-14</link><guid isPermaLink="false">20240301-release-2-14</guid><pubDate>Fri, 01 Mar 2024 00:00:00 GMT</pubDate><twitter-text>What&apos;s new in Joplin 2.14</twitter-text></item><item><title><![CDATA[Support for new plugin metadata]]></title><description><![CDATA[<p>The plugin manifest now supports new properties to better describe and present your plugins on Joplin Plugins website. Those are the <code>icons</code>, <code>categories</code>, <code>screenshots</code> and <code>promo_tile</code> properties.</p>
<h2>Icon<a name="icon" href="#icon" class="heading-anchor">🔗</a></h2>
<p>This is the icon that will be used in various plugin pages, including in your main plugin page. It will be shown on the main result page too. If not provided, a default icon will be displayed instead.</p>
<h2>Category<a name="category" href="#category" class="heading-anchor">🔗</a></h2>
@@ -353,15 +397,4 @@ sys 0m38.013s</p>
]]></description><link>https://joplinapp.org/news/20220522-gsoc-contributors</link><guid isPermaLink="false">20220522-gsoc-contributors</guid><pubDate>Sun, 22 May 2022 00:00:00 GMT</pubDate><twitter-text>Joplin received 6 Contributor Projects for GSoC 2022! Welcome to our new contributors who will be working on these projects over summer!</twitter-text></item><item><title><![CDATA[GSoC "Contributor Proposals" phase is starting now!]]></title><description><![CDATA[<p>The &quot;Contributor Proposals&quot; phase of GSoC 2022 is starting today! If you would like to be a contributor, now is the time to choose your project idea, write your proposal, and upload it to <a href="https://summerofcode.withgoogle.com/">https://summerofcode.withgoogle.com/</a></p>
<p>When it's done, please also let us know by posting an update on your forum introduction post.</p>
<p>If you haven't created a pull request yet, it's still time to create one. Doing so will greatly increase your chances of being selected!</p>
]]></description><link>https://joplinapp.org/news/20220405-gsoc-contributor-proposals</link><guid isPermaLink="false">20220405-gsoc-contributor-proposals</guid><pubDate>Tue, 05 Apr 2022 00:00:00 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Joplin participates in Google Summer of Code 2022!]]></title><description><![CDATA[<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20220308-gsoc-banner.png" alt=""></p>
<p>For the third year, Joplin has been selected as a <strong>Google Summer of Code</strong> mentor organisation! We look forward to start working with the contributors on some great new projects. This year's main themes are:</p>
<ul>
<li><strong>Mobile and tablet development</strong> - we want to improve the mobile/tablet application on iOS and Android.</li>
<li><strong>Plugin and external apps</strong> - leverage the Joplin API to create plugins and external apps.</li>
<li>And of course contributors are welcome to suggest their own ideas.</li>
</ul>
<p>Our full idea list is available here: <a href="https://joplinapp.org/help/dev/gsoc/gsoc2022/ideas">GSoC 2022 idea list</a></p>
<p>In the coming month (<strong>March 7 - April 3</strong>), contributors will start getting involved in the forum and start discussing project ideas with the mentors and community. It's also a good time to start looking at Joplin's source code, perhaps work on fixing bugs or implement small features to get familiar with the source code, and to show us your skills.</p>
<p>One difference with previous years is that anyone, not just students, are allowed to participate.</p>
<p>Additionally, last year Google only allowed smaller projects, while this year they allow again small and large projects, so we've indicated this in the idea list - the small ones are <strong>175 hours</strong>, and the large ones <strong>350 hours</strong>.</p>
]]></description><link>https://joplinapp.org/news/20220308-gsoc2022-start</link><guid isPermaLink="false">20220308-gsoc2022-start</guid><pubDate>Tue, 08 Mar 2022 00:00:00 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
]]></description><link>https://joplinapp.org/news/20220405-gsoc-contributor-proposals</link><guid isPermaLink="false">20220405-gsoc-contributor-proposals</guid><pubDate>Tue, 05 Apr 2022 00:00:00 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>

View File

@@ -39,12 +39,12 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
<!-- SPONSORS-GITHUB -->
| | | | |
| :---: | :---: | :---: | :---: |
| <img width="50" src="https://avatars2.githubusercontent.com/u/552452?s=96&v=4"/></br>[andypiper](https://github.com/andypiper) | <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars2.githubusercontent.com/u/67130?s=96&v=4"/></br>[chr15m](https://github.com/chr15m) | <img width="50" src="https://avatars2.githubusercontent.com/u/2793530?s=96&v=4"/></br>[CyberXZT](https://github.com/CyberXZT) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[dbrandonjohnson](https://github.com/dbrandonjohnson) | <img width="50" src="https://avatars2.githubusercontent.com/u/14873877?s=96&v=4"/></br>[dchecks](https://github.com/dchecks) | <img width="50" src="https://avatars2.githubusercontent.com/u/56287?s=96&v=4"/></br>[fats](https://github.com/fats) | <img width="50" src="https://avatars2.githubusercontent.com/u/8030470?s=96&v=4"/></br>[Galliver7](https://github.com/Galliver7) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/64712218?s=96&v=4"/></br>[Hegghammer](https://github.com/Hegghammer) | <img width="50" src="https://avatars2.githubusercontent.com/u/1310474?s=96&v=4"/></br>[jknowles](https://github.com/jknowles) | <img width="50" src="https://avatars2.githubusercontent.com/u/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) | <img width="50" src="https://avatars2.githubusercontent.com/u/126279083?s=96&v=4"/></br>[matmoly](https://github.com/matmoly) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/4560672?s=96&v=4"/></br>[mu88](https://github.com/mu88) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/31054972?s=96&v=4"/></br>[saarantras](https://github.com/saarantras) | <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) | <img width="50" src="https://avatars2.githubusercontent.com/u/333944?s=96&v=4"/></br>[tateisu](https://github.com/tateisu) |
| | | | |
| <img width="50" src="https://avatars2.githubusercontent.com/u/97193607?s=96&v=4"/></br>[Akhil-CM](https://github.com/Akhil-CM) | <img width="50" src="https://avatars2.githubusercontent.com/u/552452?s=96&v=4"/></br>[andypiper](https://github.com/andypiper) | <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars2.githubusercontent.com/u/67130?s=96&v=4"/></br>[chr15m](https://github.com/chr15m) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/2793530?s=96&v=4"/></br>[CyberXZT](https://github.com/CyberXZT) | <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[dbrandonjohnson](https://github.com/dbrandonjohnson) | <img width="50" src="https://avatars2.githubusercontent.com/u/14873877?s=96&v=4"/></br>[dchecks](https://github.com/dchecks) | <img width="50" src="https://avatars2.githubusercontent.com/u/56287?s=96&v=4"/></br>[fats](https://github.com/fats) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/8030470?s=96&v=4"/></br>[Galliver7](https://github.com/Galliver7) | <img width="50" src="https://avatars2.githubusercontent.com/u/64712218?s=96&v=4"/></br>[Hegghammer](https://github.com/Hegghammer) | <img width="50" src="https://avatars2.githubusercontent.com/u/2583421?s=96&v=4"/></br>[jamesandariese](https://github.com/jamesandariese) | <img width="50" src="https://avatars2.githubusercontent.com/u/1310474?s=96&v=4"/></br>[jknowles](https://github.com/jknowles) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) | <img width="50" src="https://avatars2.githubusercontent.com/u/126279083?s=96&v=4"/></br>[matmoly](https://github.com/matmoly) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/4560672?s=96&v=4"/></br>[mu88](https://github.com/mu88) | <img width="50" src="https://avatars2.githubusercontent.com/u/31054972?s=96&v=4"/></br>[saarantras](https://github.com/saarantras) | <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) | <img width="50" src="https://avatars2.githubusercontent.com/u/333944?s=96&v=4"/></br>[tateisu](https://github.com/tateisu) | | |
<!-- SPONSORS-GITHUB -->
# Community

View File

@@ -4,7 +4,7 @@
"ignoreRegExpList": [
"\\[.*?\\]\\(https:\\/\\/github.com\\/.*?\\)",
"by .*?\\)",
"\\| (.*?) \\| \\d\\d%"
"\\| (.*?) \\| \\d+%"
],
"ignorePaths": [
"**/*.d.ts",

View File

@@ -14,3 +14,22 @@ module.exports = () => {
global.console = jestConsole;
});
};
// jsdom extensions
if (typeof document !== 'undefined') {
// Prevents the CodeMirror error "getClientRects is undefined".
// See https://github.com/jsdom/jsdom/issues/3002#issue-652790925
document.createRange = () => {
const range = new Range();
range.getBoundingClientRect = jest.fn();
range.getClientRects = () => {
return {
length: 0,
item: () => null,
[Symbol.iterator]: jest.fn(),
};
};
return range;
};
}

View File

@@ -186,6 +186,8 @@
"packages/doc-builder/help": true,
"packages/doc-builder/news": true,
"packages/doc-builder/i18n": true,
"packages/doc-builder/.docusaurus": true,
"packages/doc-builder/static/images": true,
"readme/i18n": true,
"packages/app-cli/**/*.*~": true,
"packages/app-cli/**/*.mo": true,

View File

@@ -107,6 +107,8 @@
"react-native@0.71.10": "patch:react-native@npm%3A0.71.10#./.yarn/patches/react-native-animation-fix/react-native-npm-0.71.10-f9c32562d8.patch",
"nanoid": "patch:nanoid@npm%3A3.3.7#./.yarn/patches/nanoid-npm-3.3.7-98824ba130.patch",
"pdfjs-dist": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
"@react-native-community/slider": "patch:@react-native-community/slider@npm%3A4.4.4#./.yarn/patches/@react-native-community-slider-npm-4.4.4-d78e472f48.patch"
"@react-native-community/slider": "patch:@react-native-community/slider@npm%3A4.4.4#./.yarn/patches/@react-native-community-slider-npm-4.4.4-d78e472f48.patch",
"husky": "patch:husky@npm%3A3.1.0#./.yarn/patches/husky-npm-3.1.0-5cc13e4e34.patch",
"chokidar@^2.0.0": "3.5.3"
}
}

View File

@@ -41,6 +41,7 @@ class LinkSelector {
const newLinkStore: LinkStoreEntry[] = [];
const lines: string[] = renderedText.split('\n');
for (let i = 0; i < lines.length; i++) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const r = (lines[i] as any).matchAll(this.linkRegex_);
const matches = [...r];
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
@@ -63,12 +64,14 @@ class LinkSelector {
this.linkStore_ = this.findLinks(this.renderedText_);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public updateNote(textWidget: any): void {
this.noteId_ = textWidget.noteId;
this.scrollTop_ = textWidget.scrollTop_;
this.updateText(textWidget.renderedText_);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public scrollWidget(textWidget: any): void {
if (this.currentLinkIndex_ === null) return;
@@ -94,6 +97,7 @@ class LinkSelector {
return;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public changeLink(textWidget: any, offset: number): void | null {
if (textWidget.noteId !== this.noteId_) {
this.updateNote(textWidget);
@@ -124,6 +128,7 @@ class LinkSelector {
return;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public openLink(textWidget: any): void {
if (textWidget.noteId !== this.noteId_) return;
if (textWidget.renderedText_ !== this.renderedText_) return;

View File

@@ -31,7 +31,7 @@ const WindowWidget = require('tkwidgets/WindowWidget.js');
const NoteWidget = require('./gui/NoteWidget.js');
const ResourceServer = require('./ResourceServer.js');
const NoteMetadataWidget = require('./gui/NoteMetadataWidget.js');
const FolderListWidget = require('./gui/FolderListWidget.js');
const FolderListWidget = require('./gui/FolderListWidget').default;
const NoteListWidget = require('./gui/NoteListWidget.js');
const StatusBarWidget = require('./gui/StatusBarWidget').default;
const ConsoleWidget = require('./gui/ConsoleWidget.js');
@@ -441,6 +441,7 @@ class AppGui {
if (cmd === 'activate') {
const w = this.widget('mainWindow').focusedWidget;
if (w.name === 'folderList') {
// eslint-disable-next-line no-restricted-properties
this.widget('noteList').focus();
} else if (w.name === 'noteList' || w.name === 'noteText') {
this.processPromptCommand('edit $n');

View File

@@ -1,461 +0,0 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const BaseApplication_1 = require("@joplin/lib/BaseApplication");
const folders_screen_utils_js_1 = require("@joplin/lib/folders-screen-utils.js");
const ResourceService_1 = require("@joplin/lib/services/ResourceService");
const BaseModel_1 = require("@joplin/lib/BaseModel");
const Folder_1 = require("@joplin/lib/models/Folder");
const BaseItem_1 = require("@joplin/lib/models/BaseItem");
const Note_1 = require("@joplin/lib/models/Note");
const Tag_1 = require("@joplin/lib/models/Tag");
const Setting_1 = require("@joplin/lib/models/Setting");
const registry_js_1 = require("@joplin/lib/registry.js");
const path_utils_1 = require("@joplin/lib/path-utils");
const utils_1 = require("@joplin/utils");
const locale_1 = require("@joplin/lib/locale");
const fs_extra_1 = require("fs-extra");
const RevisionService_1 = require("@joplin/lib/services/RevisionService");
const shim_1 = require("@joplin/lib/shim");
const setupCommand_1 = require("./setupCommand");
const { cliUtils } = require('./cli-utils.js');
const Cache = require('@joplin/lib/Cache');
const { splitCommandBatch } = require('@joplin/lib/string-utils');
class Application extends BaseApplication_1.default {
constructor() {
super(...arguments);
this.commands_ = {};
this.commandMetadata_ = null;
this.activeCommand_ = null;
this.allCommandsLoaded_ = false;
this.gui_ = null;
this.cache_ = new Cache();
}
gui() {
return this.gui_;
}
commandStdoutMaxWidth() {
return this.gui().stdoutMaxWidth();
}
guessTypeAndLoadItem(pattern, options = null) {
return __awaiter(this, void 0, void 0, function* () {
let type = BaseModel_1.default.TYPE_NOTE;
if (pattern.indexOf('/') === 0) {
type = BaseModel_1.default.TYPE_FOLDER;
pattern = pattern.substr(1);
}
return this.loadItem(type, pattern, options);
});
}
loadItem(type, pattern, options = null) {
return __awaiter(this, void 0, void 0, function* () {
const output = yield this.loadItems(type, pattern, options);
if (output.length > 1) {
// output.sort((a, b) => { return a.user_updated_time < b.user_updated_time ? +1 : -1; });
// let answers = { 0: _('[Cancel]') };
// for (let i = 0; i < output.length; i++) {
// answers[i + 1] = output[i].title;
// }
// Not really useful with new UI?
throw new Error((0, locale_1._)('More than one item match "%s". Please narrow down your query.', pattern));
// let msg = _('More than one item match "%s". Please select one:', pattern);
// const response = await cliUtils.promptMcq(msg, answers);
// if (!response) return null;
// return output[response - 1];
}
else {
return output.length ? output[0] : null;
}
});
}
loadItems(type, pattern, options = null) {
return __awaiter(this, void 0, void 0, function* () {
if (type === 'folderOrNote') {
const folders = yield this.loadItems(BaseModel_1.default.TYPE_FOLDER, pattern, options);
if (folders.length)
return folders;
return yield this.loadItems(BaseModel_1.default.TYPE_NOTE, pattern, options);
}
pattern = pattern ? pattern.toString() : '';
if (type === BaseModel_1.default.TYPE_FOLDER && (pattern === Folder_1.default.conflictFolderTitle() || pattern === Folder_1.default.conflictFolderId()))
return [Folder_1.default.conflictFolder()];
if (!options)
options = {};
const parent = options.parent ? options.parent : app().currentFolder();
const ItemClass = BaseItem_1.default.itemClass(type);
if (type === BaseModel_1.default.TYPE_NOTE && pattern.indexOf('*') >= 0) {
// Handle it as pattern
if (!parent)
throw new Error((0, locale_1._)('No notebook selected.'));
return yield Note_1.default.previews(parent.id, { titlePattern: pattern });
}
else {
// Single item
let item = null;
if (type === BaseModel_1.default.TYPE_NOTE) {
if (!parent)
throw new Error((0, locale_1._)('No notebook has been specified.'));
item = yield ItemClass.loadFolderNoteByField(parent.id, 'title', pattern);
}
else {
item = yield ItemClass.loadByTitle(pattern);
}
if (item)
return [item];
item = yield ItemClass.load(pattern); // Load by id
if (item)
return [item];
if (pattern.length >= 2) {
return yield ItemClass.loadByPartialId(pattern);
}
}
return [];
});
}
setupCommand(cmd) {
return (0, setupCommand_1.default)(cmd, (t) => this.stdout(t), () => this.store(), () => this.gui());
}
stdout(text) {
return this.gui().stdout(text);
}
exit(code = 0) {
const _super = Object.create(null, {
exit: { get: () => super.exit }
});
return __awaiter(this, void 0, void 0, function* () {
const doExit = () => __awaiter(this, void 0, void 0, function* () {
this.gui().exit();
yield _super.exit.call(this, code);
});
// Give it a few seconds to cancel otherwise exit anyway
shim_1.default.setTimeout(() => __awaiter(this, void 0, void 0, function* () {
yield doExit();
}), 5000);
if (yield registry_js_1.reg.syncTarget().syncStarted()) {
this.stdout((0, locale_1._)('Cancelling background synchronisation... Please wait.'));
const sync = yield registry_js_1.reg.syncTarget().synchronizer();
yield sync.cancel();
}
yield doExit();
});
}
commands(uiType = null) {
if (!this.allCommandsLoaded_) {
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
(0, fs_extra_1.readdirSync)(__dirname).forEach(path => {
if (path.indexOf('command-') !== 0)
return;
if (path.endsWith('.test.js'))
return;
const ext = (0, path_utils_1.fileExtension)(path);
if (ext !== 'js')
return;
const CommandClass = require(`./${path}`);
let cmd = new CommandClass();
if (!cmd.enabled())
return;
cmd = this.setupCommand(cmd);
this.commands_[cmd.name()] = cmd;
});
this.allCommandsLoaded_ = true;
}
if (uiType !== null) {
const temp = {};
for (const n in this.commands_) {
if (!this.commands_.hasOwnProperty(n))
continue;
const c = this.commands_[n];
if (!c.supportsUi(uiType))
continue;
temp[n] = c;
}
return temp;
}
return this.commands_;
}
commandNames() {
return __awaiter(this, void 0, void 0, function* () {
const metadata = yield this.commandMetadata();
const output = [];
for (const n in metadata) {
if (!metadata.hasOwnProperty(n))
continue;
output.push(n);
}
return output;
});
}
commandMetadata() {
return __awaiter(this, void 0, void 0, function* () {
if (this.commandMetadata_)
return this.commandMetadata_;
let output = yield this.cache_.getItem('metadata');
if (output) {
this.commandMetadata_ = output;
return Object.assign({}, this.commandMetadata_);
}
const commands = this.commands();
output = {};
for (const n in commands) {
if (!commands.hasOwnProperty(n))
continue;
const cmd = commands[n];
output[n] = cmd.metadata();
}
yield this.cache_.setItem('metadata', output, 1000 * 60 * 60 * 24);
this.commandMetadata_ = output;
return Object.assign({}, this.commandMetadata_);
});
}
hasGui() {
return this.gui() && !this.gui().isDummy();
}
findCommandByName(name) {
if (this.commands_[name])
return this.commands_[name];
let CommandClass = null;
try {
CommandClass = require(`${__dirname}/command-${name}.js`);
}
catch (error) {
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
const e = new Error((0, locale_1._)('No such command: %s', name));
e.type = 'notFound';
throw e;
}
else {
throw error;
}
}
let cmd = new CommandClass();
cmd = this.setupCommand(cmd);
this.commands_[name] = cmd;
return this.commands_[name];
}
dummyGui() {
return {
isDummy: () => {
return true;
},
prompt: (initialText = '', promptString = '', options = null) => {
return cliUtils.prompt(initialText, promptString, options);
},
showConsole: () => { },
maximizeConsole: () => { },
stdout: (text) => {
// eslint-disable-next-line no-console
console.info(text);
},
fullScreen: () => { },
exit: () => { },
showModalOverlay: () => { },
hideModalOverlay: () => { },
stdoutMaxWidth: () => {
return 100;
},
forceRender: () => { },
termSaveState: () => { },
termRestoreState: () => { },
};
}
execCommand(argv) {
return __awaiter(this, void 0, void 0, function* () {
if (!argv.length)
return this.execCommand(['help']);
// reg.logger().debug('execCommand()', argv);
const commandName = argv[0];
this.activeCommand_ = this.findCommandByName(commandName);
let outException = null;
try {
if (this.gui().isDummy() && !this.activeCommand_.supportsUi('cli'))
throw new Error((0, locale_1._)('The command "%s" is only available in GUI mode', this.activeCommand_.name()));
const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv);
yield this.activeCommand_.action(cmdArgs);
}
catch (error) {
outException = error;
}
this.activeCommand_ = null;
if (outException)
throw outException;
});
}
currentCommand() {
return this.activeCommand_;
}
loadKeymaps() {
return __awaiter(this, void 0, void 0, function* () {
const defaultKeyMap = [
{ keys: [':'], type: 'function', command: 'enter_command_line_mode' },
{ keys: ['TAB'], type: 'function', command: 'focus_next' },
{ keys: ['SHIFT_TAB'], type: 'function', command: 'focus_previous' },
{ keys: ['UP'], type: 'function', command: 'move_up' },
{ keys: ['DOWN'], type: 'function', command: 'move_down' },
{ keys: ['PAGE_UP'], type: 'function', command: 'page_up' },
{ keys: ['PAGE_DOWN'], type: 'function', command: 'page_down' },
{ keys: ['ENTER'], type: 'function', command: 'activate' },
{ keys: ['DELETE', 'BACKSPACE'], type: 'function', command: 'delete' },
{ keys: ['n'], type: 'function', command: 'next_link' },
{ keys: ['b'], type: 'function', command: 'previous_link' },
{ keys: ['o'], type: 'function', command: 'open_link' },
{ keys: [' '], type: 'prompt', command: 'todo toggle $n' },
{ keys: ['tc'], type: 'function', command: 'toggle_console' },
{ keys: ['tm'], type: 'function', command: 'toggle_metadata' },
{ keys: ['ti'], type: 'function', command: 'toggle_ids' },
{ keys: ['/'], type: 'prompt', command: 'search ""', cursorPosition: -2 },
{ keys: ['mn'], type: 'prompt', command: 'mknote ""', cursorPosition: -2 },
{ keys: ['mt'], type: 'prompt', command: 'mktodo ""', cursorPosition: -2 },
{ keys: ['mb'], type: 'prompt', command: 'mkbook ""', cursorPosition: -2 },
{ keys: ['yn'], type: 'prompt', command: 'cp $n ""', cursorPosition: -2 },
{ keys: ['dn'], type: 'prompt', command: 'mv $n ""', cursorPosition: -2 },
];
// Filter the keymap item by command so that items in keymap.json can override
// the default ones.
const itemsByCommand = {};
for (let i = 0; i < defaultKeyMap.length; i++) {
itemsByCommand[defaultKeyMap[i].command] = defaultKeyMap[i];
}
const filePath = `${Setting_1.default.value('profileDir')}/keymap.json`;
if (yield (0, fs_extra_1.pathExists)(filePath)) {
try {
let configString = yield (0, fs_extra_1.readFile)(filePath, 'utf-8');
configString = configString.replace(/^\s*\/\/.*/, ''); // Strip off comments
const keymap = JSON.parse(configString);
for (let keymapIndex = 0; keymapIndex < keymap.length; keymapIndex++) {
const item = keymap[keymapIndex];
itemsByCommand[item.command] = item;
}
}
catch (error) {
let msg = error.message ? error.message : '';
msg = `Could not load keymap ${filePath}\n${msg}`;
error.message = msg;
throw error;
}
}
const output = [];
for (const n in itemsByCommand) {
if (!itemsByCommand.hasOwnProperty(n))
continue;
output.push(itemsByCommand[n]);
}
// Map reserved shortcuts to their equivalent key
// https://github.com/cronvel/terminal-kit/issues/101
for (let i = 0; i < output.length; i++) {
const newKeys = output[i].keys.map(k => {
k = k.replace(/CTRL_H/g, 'BACKSPACE');
k = k.replace(/CTRL_I/g, 'TAB');
k = k.replace(/CTRL_M/g, 'ENTER');
return k;
});
output[i].keys = newKeys;
}
return output;
});
}
commandList(argv) {
return __awaiter(this, void 0, void 0, function* () {
if (argv.length && argv[0] === 'batch') {
const commands = [];
const commandLines = splitCommandBatch(yield (0, fs_extra_1.readFile)(argv[1], 'utf-8'));
for (const commandLine of commandLines) {
if (!commandLine.trim())
continue;
const splitted = (0, utils_1.splitCommandString)(commandLine.trim());
commands.push(splitted);
}
return commands;
}
else {
return [argv];
}
});
}
// We need this special case here because by the time the `version` command
// runs, the keychain has already been setup.
checkIfKeychainEnabled(argv) {
return argv.indexOf('version') < 0;
}
start(argv) {
const _super = Object.create(null, {
start: { get: () => super.start }
});
return __awaiter(this, void 0, void 0, function* () {
const keychainEnabled = this.checkIfKeychainEnabled(argv);
argv = yield _super.start.call(this, argv, { keychainEnabled });
cliUtils.setStdout((object) => {
return this.stdout(object);
});
this.initRedux();
// If we have some arguments left at this point, it's a command
// so execute it.
if (argv.length) {
this.gui_ = this.dummyGui();
this.currentFolder_ = yield Folder_1.default.load(Setting_1.default.value('activeFolderId'));
yield this.applySettingsSideEffects();
try {
const commands = yield this.commandList(argv);
for (const command of commands) {
yield this.execCommand(command);
}
}
catch (error) {
if (this.showStackTraces_) {
console.error(error);
}
else {
// eslint-disable-next-line no-console
console.info(error.message);
}
process.exit(1);
}
yield Setting_1.default.saveAll();
// Need to call exit() explicitly, otherwise Node wait for any timeout to complete
// https://stackoverflow.com/questions/18050095
process.exit(0);
}
else {
// Otherwise open the GUI
const keymap = yield this.loadKeymaps();
const AppGui = require('./app-gui.js');
this.gui_ = new AppGui(this, this.store(), keymap);
this.gui_.setLogger(this.logger());
yield this.gui_.start();
// Since the settings need to be loaded before the store is created, it will never
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
// initialised. So we manually call dispatchUpdateAll() to force an update.
Setting_1.default.dispatchUpdateAll();
yield (0, folders_screen_utils_js_1.refreshFolders)((action) => this.store().dispatch(action));
const tags = yield Tag_1.default.allWithNotes();
ResourceService_1.default.runInBackground();
RevisionService_1.default.instance().runInBackground();
this.dispatch({
type: 'TAG_UPDATE_ALL',
items: tags,
});
this.store().dispatch({
type: 'FOLDER_SELECT',
id: Setting_1.default.value('activeFolderId'),
});
this.startRotatingLogMaintenance(Setting_1.default.value('profileDir'));
}
});
}
}
let application_ = null;
function app() {
if (application_)
return application_;
application_ = new Application();
return application_;
}
exports.default = app;
//# sourceMappingURL=app.js.map

View File

@@ -22,10 +22,14 @@ const { splitCommandBatch } = require('@joplin/lib/string-utils');
class Application extends BaseApplication {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private commands_: Record<string, any> = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private commandMetadata_: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private activeCommand_: any = null;
private allCommandsLoaded_ = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private gui_: any = null;
private cache_ = new Cache();
@@ -37,6 +41,7 @@ class Application extends BaseApplication {
return this.gui().stdoutMaxWidth();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async guessTypeAndLoadItem(pattern: string, options: any = null) {
let type = BaseModel.TYPE_NOTE;
if (pattern.indexOf('/') === 0) {
@@ -46,6 +51,7 @@ class Application extends BaseApplication {
return this.loadItem(type, pattern, options);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async loadItem(type: ModelType | 'folderOrNote', pattern: string, options: any = null) {
const output = await this.loadItems(type, pattern, options);
@@ -70,6 +76,7 @@ class Application extends BaseApplication {
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async loadItems(type: ModelType | 'folderOrNote', pattern: string, options: any = null): Promise<(FolderEntity | NoteEntity)[]> {
if (type === 'folderOrNote') {
const folders: FolderEntity[] = await this.loadItems(BaseModel.TYPE_FOLDER, pattern, options);
@@ -95,7 +102,7 @@ class Application extends BaseApplication {
let item = null;
if (type === BaseModel.TYPE_NOTE) {
if (!parent) throw new Error(_('No notebook has been specified.'));
item = await ItemClass.loadFolderNoteByField(parent.id, 'title', pattern);
item = await (ItemClass as typeof Note).loadFolderNoteByField(parent.id, 'title', pattern);
} else {
item = await ItemClass.loadByTitle(pattern);
}
@@ -160,6 +167,7 @@ class Application extends BaseApplication {
}
if (uiType !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const temp: Record<string, any> = {};
for (const n in this.commands_) {
if (!this.commands_.hasOwnProperty(n)) continue;
@@ -219,6 +227,7 @@ class Application extends BaseApplication {
CommandClass = require(`${__dirname}/command-${name}.js`);
} catch (error) {
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const e: any = new Error(_('No such command: %s', name));
e.type = 'notFound';
throw e;
@@ -238,6 +247,7 @@ class Application extends BaseApplication {
isDummy: () => {
return true;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
prompt: (initialText = '', promptString = '', options: any = null) => {
return cliUtils.prompt(initialText, promptString, options);
},
@@ -260,6 +270,7 @@ class Application extends BaseApplication {
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async execCommand(argv: string[]): Promise<any> {
if (!argv.length) return this.execCommand(['help']);
// reg.logger().debug('execCommand()', argv);
@@ -307,6 +318,7 @@ class Application extends BaseApplication {
{ keys: ['tc'], type: 'function', command: 'toggle_console' },
{ keys: ['tm'], type: 'function', command: 'toggle_metadata' },
{ keys: ['ti'], type: 'function', command: 'toggle_ids' },
{ keys: ['r'], type: 'prompt', command: 'restore $n' },
{ keys: ['/'], type: 'prompt', command: 'search ""', cursorPosition: -2 },
{ keys: ['mn'], type: 'prompt', command: 'mknote ""', cursorPosition: -2 },
{ keys: ['mt'], type: 'prompt', command: 'mktodo ""', cursorPosition: -2 },
@@ -389,6 +401,7 @@ class Application extends BaseApplication {
argv = await super.start(argv, { keychainEnabled });
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
cliUtils.setStdout((object: any) => {
return this.stdout(object);
});
@@ -438,6 +451,7 @@ class Application extends BaseApplication {
// initialised. So we manually call dispatchUpdateAll() to force an update.
Setting.dispatchUpdateAll();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
await refreshFolders((action: any) => this.store().dispatch(action));
const tags = await Tag.allWithNotes();

View File

@@ -3,14 +3,18 @@ import { reg } from '@joplin/lib/registry.js';
export default class BaseCommand {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
protected stdout_: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
protected prompt_: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
protected dispatcher_: any;
public usage(): string {
throw new Error('Usage not defined');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public encryptionCheck(item: any) {
if (item && item.encryption_applied) throw new Error(_('Cannot change encrypted item'));
}
@@ -19,6 +23,7 @@ export default class BaseCommand {
throw new Error('Description not defined');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async action(_args: any) {
throw new Error('Action not defined');
}
@@ -31,6 +36,7 @@ export default class BaseCommand {
return this.compatibleUis().indexOf(ui) >= 0;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public options(): any[] {
return [];
}
@@ -59,6 +65,7 @@ export default class BaseCommand {
this.dispatcher_ = fn;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public dispatch(action: any) {
if (!this.dispatcher_) throw new Error('Dispatcher not defined');
return this.dispatcher_(action);
@@ -78,6 +85,7 @@ export default class BaseCommand {
this.prompt_ = fn;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async prompt(message: string, options: any = null) {
if (!this.prompt_) throw new Error('Prompt is undefined');
return await this.prompt_(message, options);

View File

@@ -37,6 +37,7 @@ class Command extends BaseCommand {
return markdownUtils.createMarkdownTable(headers, tableFields);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const models = [
{
@@ -400,6 +401,11 @@ async function fetchAllNotes() {
lines.push('Remove the tag from the note.');
lines.push('');
}
if (model.type === BaseModel.TYPE_NOTE || model.type === BaseModel.TYPE_FOLDER) {
lines.push(`By default, the ${singular} will be moved **to the trash**. To permanently delete it, add the query parameter \`permanent=1\``);
lines.push('');
}
}
{

View File

@@ -13,6 +13,7 @@ class Command extends BaseCommand {
return _('Attaches the given file to the note.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const title = args['note'];

View File

@@ -18,6 +18,7 @@ class Command extends BaseCommand {
return [['-v, --verbose', _('Displays the complete information about note.')]];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const title = args['note'];

View File

@@ -27,6 +27,7 @@ class Command extends BaseCommand {
return new Promise<void>((resolve, reject) => {
// being defensive and not attempting to settle twice
let isSettled = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const chunks: any = [];
inputStream.on('readable', () => {
@@ -67,6 +68,7 @@ class Command extends BaseCommand {
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const verbose = args.options.verbose;
const isExport = args.options.export;
@@ -91,6 +93,7 @@ class Command extends BaseCommand {
keys.sort();
if (isExport) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const resultObj = keys.reduce<Record<string, any>>((acc, key) => {
const value = Setting.value(key);
if (!verbose && !value) return acc;

View File

@@ -13,6 +13,7 @@ class Command extends BaseCommand {
return _('Duplicates the notes matching <note> to [notebook]. If no notebook is specified the note is duplicated in the current notebook.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
let folder = null;
if (args['notebook']) {

View File

@@ -15,6 +15,7 @@ class Command extends BaseCommand {
return _('Marks a to-do as done.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public static async handleAction(commandInstance: BaseCommand, args: any, isCompleted: boolean) {
const note: NoteEntity = await app().loadItem(BaseModel.TYPE_NOTE, args.note);
commandInstance.encryptionCheck(note);
@@ -31,6 +32,7 @@ class Command extends BaseCommand {
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
await Command.handleAction(this, args, true);
}

View File

@@ -2,6 +2,7 @@ import BaseCommand from './base-command';
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import Tag from '@joplin/lib/models/Tag';
import { FolderEntity, NoteEntity } from '@joplin/lib/services/database/types';
class Command extends BaseCommand {
public override usage() {
@@ -17,7 +18,7 @@ class Command extends BaseCommand {
}
public override async action() {
let items = [];
let items: (NoteEntity | FolderEntity)[] = [];
const folders = await Folder.all();
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];

View File

@@ -30,9 +30,11 @@ class Command extends BaseCommand {
];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async action(args: any) {
const options = args.options;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const askForMasterKey = async (error: any) => {
const masterKeyId = error.masterKeyId;
const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true });

View File

@@ -17,6 +17,7 @@ class Command extends BaseCommand {
return _('Edit note.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
let tempFilePath: string|null = null;

View File

@@ -24,6 +24,7 @@ class Command extends BaseCommand {
return [['--format <format>', _('Destination format: %s', formats.join(', '))], ['--note <note>', _('Exports only the given note.')], ['--notebook <notebook>', _('Exports only the given notebook.')]];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const exportOptions: ExportOptions = {};
exportOptions.path = args.path;
@@ -35,10 +36,12 @@ class Command extends BaseCommand {
if (args.options.note) {
const notes = await app().loadItems(BaseModel.TYPE_NOTE, args.options.note, { parent: app().currentFolder() });
if (!notes.length) throw new Error(_('Cannot find "%s".', args.options.note));
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
exportOptions.sourceNoteIds = notes.map((n: any) => n.id);
} else if (args.options.notebook) {
const folders = await app().loadItems(BaseModel.TYPE_FOLDER, args.options.notebook);
if (!folders.length) throw new Error(_('Cannot find "%s".', args.options.notebook));
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
exportOptions.sourceFolderIds = folders.map((n: any) => n.id);
}

View File

@@ -13,6 +13,7 @@ class Command extends BaseCommand {
return _('Displays a geolocation URL for the note.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const title = args['note'];

View File

@@ -29,6 +29,7 @@ class Command extends BaseCommand {
return output;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const stdoutWidth = app().commandStdoutMaxWidth();

View File

@@ -30,6 +30,7 @@ class Command extends BaseCommand {
];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const folder = await app().loadItem(BaseModel.TYPE_FOLDER, args.notebook);

View File

@@ -7,6 +7,7 @@ import Setting from '@joplin/lib/models/Setting';
import Note from '@joplin/lib/models/Note';
const { sprintf } = require('sprintf-js');
import time from '@joplin/lib/time';
import { NoteEntity } from '@joplin/lib/services/database/types';
const { cliUtils } = require('./cli-utils.js');
class Command extends BaseCommand {
@@ -33,11 +34,13 @@ class Command extends BaseCommand {
];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const pattern = args['note-pattern'];
let items = [];
const options = args.options;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const queryOptions: any = {};
if (options.limit) queryOptions.limit = options.limit;
if (options.sort) {
@@ -71,7 +74,7 @@ class Command extends BaseCommand {
let hasTodos = false;
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.is_todo) {
if ((item as NoteEntity).is_todo) {
hasTodos = true;
break;
}
@@ -103,8 +106,8 @@ class Command extends BaseCommand {
}
if (hasTodos) {
if (item.is_todo) {
row.push(sprintf('[%s]', item.todo_completed ? 'X' : ' '));
if ((item as NoteEntity).is_todo) {
row.push(sprintf('[%s]', (item as NoteEntity).todo_completed ? 'X' : ' '));
} else {
row.push(' ');
}

View File

@@ -43,6 +43,7 @@ class Command extends BaseCommand {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async action(args: any) {
const targetFolder = args.options.parent;

View File

@@ -14,6 +14,7 @@ class Command extends BaseCommand {
return _('Moves the given <item> to [notebook]');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const pattern = args['item'];
const destination = args['notebook'];

View File

@@ -14,6 +14,7 @@ class Command extends BaseCommand {
return _('Renames the given <item> (note or notebook) to <name>.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const pattern = args['item'];
const name = args['name'];

View File

@@ -0,0 +1,27 @@
import BaseCommand from './base-command';
import app from './app';
import { _ } from '@joplin/lib/locale';
import restoreItems from '@joplin/lib/services/trash/restoreItems';
class Command extends BaseCommand {
public override usage() {
return 'restore <pattern>';
}
public override description() {
return _('Restore the items matching <pattern> from the trash.');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const pattern = args['pattern'];
const items = await app().loadItems('folderOrNote', pattern);
if (!items.length) throw new Error(_('Cannot find "%s".', pattern));
const ids = items.map(n => n.id);
await restoreItems(items[0].type_, ids, { useRestoreFolder: true });
}
}
module.exports = Command;

View File

@@ -0,0 +1,81 @@
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
import { setupCommandForTesting, setupApplication } from './utils/testUtils';
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
const Command = require('./command-rmbook');
const setUpCommand = () => {
const command = setupCommandForTesting(Command);
const promptMock = jest.fn(() => true);
command.setPrompt(promptMock);
return { command, promptMock };
};
describe('command-rmbook', () => {
beforeEach(async () => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
await setupApplication();
});
it('should ask before moving to the trash', async () => {
await Folder.save({ title: 'folder1' });
const { command, promptMock } = setUpCommand();
await command.action({ 'notebook': 'folder1', options: {} });
expect(promptMock).toHaveBeenCalledTimes(1);
const folder1 = await Folder.loadByTitle('folder1');
expect(folder1.deleted_time).not.toBeFalsy();
expect((await Note.allItemsInTrash()).folderIds).toHaveLength(1);
});
it('cancelling a prompt should prevent deletion', async () => {
await Folder.save({ title: 'folder1' });
const { command, promptMock } = setUpCommand();
promptMock.mockImplementation(() => false);
await command.action({ 'notebook': 'folder1', options: {} });
expect((await Note.allItemsInTrash()).folderIds).toHaveLength(0);
});
it('should not prompt when the force flag is given', async () => {
const { id: folder1Id } = await Folder.save({ title: 'folder1' });
const { id: folder2Id } = await Folder.save({ title: 'folder2', parent_id: folder1Id });
const { command, promptMock } = setUpCommand();
await command.action({ 'notebook': 'folder1', options: { force: true } });
expect(promptMock).toHaveBeenCalledTimes(0);
expect((await Note.allItemsInTrash()).folderIds.includes(folder1Id)).toBe(true);
expect((await Note.allItemsInTrash()).folderIds.includes(folder2Id)).toBe(true);
});
it('should support permanent deletion', async () => {
const { id: folder1Id } = await Folder.save({ title: 'folder1' });
const { id: folder2Id } = await Folder.save({ title: 'folder2' });
const { command, promptMock } = setUpCommand();
await command.action({ 'notebook': 'folder1', options: { permanent: true, force: true } });
expect(promptMock).not.toHaveBeenCalled();
// Should be permanently deleted.
expect((await Note.allItemsInTrash()).folderIds.includes(folder1Id)).toBe(false);
expect(await Folder.load(folder1Id, { includeDeleted: true })).toBe(undefined);
// folder2 should not be deleted
expect(await Folder.load(folder2Id, { includeDeleted: false })).toBeTruthy();
// Should prompt before deleting
await command.action({ 'notebook': 'folder2', options: { permanent: true } });
expect(promptMock).toHaveBeenCalled();
expect(await Folder.load(folder2Id, { includeDeleted: false })).toBeUndefined();
});
});

View File

@@ -3,6 +3,7 @@ import app from './app';
import { _ } from '@joplin/lib/locale';
import Folder from '@joplin/lib/models/Folder';
import BaseModel from '@joplin/lib/BaseModel';
import { substrWithEllipsis } from '@joplin/lib/string-utils';
class Command extends BaseCommand {
public override usage() {
@@ -14,19 +15,32 @@ class Command extends BaseCommand {
}
public override options() {
return [['-f, --force', _('Deletes the notebook without asking for confirmation.')]];
return [
['-f, --force', _('Deletes the notebook without asking for confirmation.')],
['-p, --permanent', _('Permanently deletes the notebook, skipping the trash.')],
];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const pattern = args['notebook'];
const force = args.options && args.options.force === true;
const folder = await app().loadItem(BaseModel.TYPE_FOLDER, pattern);
if (!folder) throw new Error(_('Cannot find "%s".', pattern));
const ok = force ? true : await this.prompt(_('Delete notebook? All notes and sub-notebooks within this notebook will also be deleted.'), { booleanAnswerDefault: 'n' });
const permanent = args.options?.permanent === true || !!folder.deleted_time;
const ellipsizedFolderTitle = substrWithEllipsis(folder.title, 0, 32);
let msg;
if (permanent) {
msg = _('Permanently delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will be permanently deleted.', ellipsizedFolderTitle);
} else {
msg = _('Move notebook "%s" to the trash?\n\nAll notes and sub-notebooks within this notebook will also be moved to the trash.', ellipsizedFolderTitle);
}
const ok = force ? true : await this.prompt(msg, { booleanAnswerDefault: 'n' });
if (!ok) return;
await Folder.delete(folder.id);
await Folder.delete(folder.id, { toTrash: !permanent, sourceDescription: 'rmbook command' });
}
}

View File

@@ -0,0 +1,57 @@
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
import { setupCommandForTesting, setupApplication } from './utils/testUtils';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import app from './app';
import { getTrashFolderId } from '@joplin/lib/services/trash';
const Command = require('./command-rmnote');
const setUpCommand = () => {
const command = setupCommandForTesting(Command);
const promptMock = jest.fn(() => true);
command.setPrompt(promptMock);
return { command, promptMock };
};
const createTestNote = async () => {
const folder = await Folder.save({ title: 'folder' });
app().switchCurrentFolder(folder);
return await Note.save({ title: 'note1', parent_id: folder.id });
};
describe('command-rmnote', () => {
beforeEach(async () => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
await setupApplication();
});
it('should move to the trash by default, without prompting', async () => {
const { id: noteId } = await createTestNote();
const { command, promptMock } = setUpCommand();
await command.action({ 'note-pattern': 'note1', options: {} });
expect(promptMock).not.toHaveBeenCalled();
expect((await Note.allItemsInTrash()).noteIds.includes(noteId)).toBe(true);
});
it('should permanently delete trashed items by default, with prompting', async () => {
const { id: noteId } = await createTestNote();
const { command, promptMock } = setUpCommand();
// Should not prompt when deleting from a folder
await command.action({ 'note-pattern': 'note1', options: {} });
expect(promptMock).toHaveBeenCalledTimes(0);
// Should prompt when deleting from trash
app().switchCurrentFolder(await Folder.load(getTrashFolderId()));
await command.action({ 'note-pattern': 'note1', options: {} });
expect(promptMock).toHaveBeenCalledTimes(1);
expect(await Note.load(noteId, { includeDeleted: true })).toBe(undefined);
});
});

View File

@@ -2,7 +2,8 @@ import BaseCommand from './base-command';
import app from './app';
import { _ } from '@joplin/lib/locale';
import Note from '@joplin/lib/models/Note';
import BaseModel from '@joplin/lib/BaseModel';
import BaseModel, { DeleteOptions } from '@joplin/lib/BaseModel';
import { NoteEntity } from '@joplin/lib/services/database/types';
class Command extends BaseCommand {
public override usage() {
@@ -14,20 +15,41 @@ class Command extends BaseCommand {
}
public override options() {
return [['-f, --force', _('Deletes the notes without asking for confirmation.')]];
return [
['-f, --force', _('Deletes the notes without asking for confirmation.')],
['-p, --permanent', _('Deletes notes permanently, skipping the trash.')],
];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const pattern = args['note-pattern'];
const force = args.options && args.options.force === true;
const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
const notes: NoteEntity[] = await app().loadItems(BaseModel.TYPE_NOTE, pattern);
if (!notes.length) throw new Error(_('Cannot find "%s".', pattern));
const ok = force ? true : await this.prompt(notes.length > 1 ? _('%d notes match this pattern. Delete them?', notes.length) : _('Delete note?'), { booleanAnswerDefault: 'n' });
let ok = true;
if (!force && notes.length > 1) {
ok = await this.prompt(_('%d notes match this pattern. Delete them?', notes.length), { booleanAnswerDefault: 'n' });
}
const permanent = (args.options?.permanent === true) || notes.every(n => !!n.deleted_time);
if (!force && permanent) {
const message = (
notes.length === 1 ? _('This will permanently delete the note "%s". Continue?', notes[0].title) : _('%d notes will be permanently deleted. Continue?', notes.length)
);
ok = await this.prompt(message, { booleanAnswerDefault: 'n' });
}
if (!ok) return;
const ids = notes.map((n: any) => n.id);
await Note.batchDelete(ids);
const ids = notes.map(n => n.id);
const options: DeleteOptions = {
toTrash: !permanent,
sourceDescription: 'rmnote',
};
await Note.batchDelete(ids, options);
}
}

View File

@@ -22,6 +22,7 @@ class Command extends BaseCommand {
return _('Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s', s.join(', '));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const title = args['note'];
const propName = args['name'];
@@ -36,6 +37,7 @@ class Command extends BaseCommand {
const timestamp = Date.now();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const newNote: any = {
id: notes[i].id,
type_: notes[i].type_,

View File

@@ -35,7 +35,9 @@ class Command extends BaseCommand {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async action(args: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const schema: Record<string, any> = {
title: 'JSON schema for Joplin setting files',
'$id': Setting.schemaUrl,
@@ -52,6 +54,7 @@ class Command extends BaseCommand {
const type = settingTypeToSchemaType(md.type);
if (!type) continue;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const props: Record<string, any> = {};
props.type = type;
props.default = md.value;
@@ -61,6 +64,7 @@ class Command extends BaseCommand {
if (md.description && md.description('desktop')) description.push(md.description('desktop'));
if (description.length) props.description = description.join('. ');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
if (md.isEnum) props.enum = Object.keys(md.options()).map((v: any) => Setting.formatValue(key, v));
if ('minimum' in md) props.minimum = md.minimum;
if ('maximum' in md) props.maximum = md.maximum;

View File

@@ -14,12 +14,18 @@ const { cliUtils } = require('./cli-utils.js');
const md5 = require('md5');
import * as locker from 'proper-lockfile';
import { pathExists, writeFile } from 'fs-extra';
import { checkIfLoginWasSuccessful, generateApplicationConfirmUrl } from '@joplin/lib/services/joplinCloudUtils';
import Logger from '@joplin/utils/Logger';
import { uuidgen } from '@joplin/lib/uuid';
const logger = Logger.create('command-sync');
class Command extends BaseCommand {
private syncTargetId_: number = null;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
private releaseLockFn_: Function = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private oneDriveApiUtils_: any = null;
public usage() {
@@ -54,6 +60,7 @@ class Command extends BaseCommand {
// OneDrive
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
const auth = await this.oneDriveApiUtils_.oauthDance({
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
log: (...s: any[]) => {
return this.stdout(...s);
},
@@ -84,6 +91,33 @@ class Command extends BaseCommand {
Setting.setValue(`sync.${this.syncTargetId_}.auth`, response.access_token);
api.setAuthToken(response.access_token);
return true;
} else if (syncTargetMd.name === 'joplinCloud') {
const applicationAuthId = uuidgen();
const checkForCredentials = async () => {
try {
const applicationAuthUrl = `${Setting.value('sync.10.path')}/api/application_auth/${applicationAuthId}`;
const response = await checkIfLoginWasSuccessful(applicationAuthUrl);
if (response && response.success) {
return response;
}
return null;
} catch (error) {
logger.error(error);
throw error;
}
};
this.stdout(_('To allow Joplin to synchronise with Joplin Cloud, please login using this URL:'));
const confirmUrl = `${Setting.value('sync.10.website')}/applications/${applicationAuthId}/confirm`;
const urlWithClient = await generateApplicationConfirmUrl(confirmUrl);
this.stdout(urlWithClient);
const authorized = await this.prompt(_('Have you authorised the application login in the above URL?'), { booleanAnswerDefault: 'y' });
if (!authorized) return false;
const result = await checkForCredentials();
if (!result) return false;
return true;
}
this.stdout(_('Not authenticated with %s. Please provide any missing credentials.', syncTargetMd.label));
@@ -101,6 +135,7 @@ class Command extends BaseCommand {
return !!this.oneDriveApiUtils_;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async action(args: any) {
this.releaseLockFn_ = null;
@@ -149,7 +184,9 @@ class Command extends BaseCommand {
const sync = await syncTarget.synchronizer();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const options: any = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
onProgress: (report: any) => {
const lines = Synchronizer.reportToLines(report);
if (lines.length) cliUtils.redraw(lines.join(' '));

View File

@@ -6,11 +6,13 @@ import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
import { readCredentialFile } from '@joplin/lib/utils/credentialFiles';
import JoplinServerApi from '@joplin/lib/JoplinServerApi';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function randomElement(array: any[]): any {
if (!array.length) return null;
return array[Math.floor(Math.random() * array.length)];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function itemCount(args: any) {
const count = Number(args.arg0);
if (!count || isNaN(count)) throw new Error('Note count must be specified');
@@ -30,6 +32,7 @@ class Command extends BaseCommand {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public options(): any[] {
return [
['--folder-count <count>', 'Folders to create'],
@@ -40,6 +43,7 @@ class Command extends BaseCommand {
];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async action(args: any) {
const { command, options } = args;
@@ -53,6 +57,7 @@ class Command extends BaseCommand {
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const promises: any[] = [];
if (command === 'createRandomNotes') {
@@ -85,7 +90,7 @@ class Command extends BaseCommand {
for (let i = 0; i < noteCount; i++) {
const noteId = randomElement(noteIds);
promises.push(Note.delete(noteId));
promises.push(Note.delete(noteId, { sourceDescription: 'command-testing' }));
}
}

View File

@@ -16,6 +16,7 @@ class Command extends BaseCommand {
return ['cli'];
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public override async action(args: any) {
const folder = await app().loadItem(BaseModel.TYPE_FOLDER, args['notebook']);
if (!folder) throw new Error(_('Cannot find "%s".', args['notebook']));

View File

@@ -1,16 +1,20 @@
const Folder = require('@joplin/lib/models/Folder').default;
const Tag = require('@joplin/lib/models/Tag').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
import Folder from '@joplin/lib/models/Folder';
import Tag from '@joplin/lib/models/Tag';
import BaseModel from '@joplin/lib/BaseModel';
import Setting from '@joplin/lib/models/Setting';
import { _ } from '@joplin/lib/locale';
import { FolderEntity } from '@joplin/lib/services/database/types';
import { getDisplayParentId, getTrashFolderId } from '@joplin/lib/services/trash';
const ListWidget = require('tkwidgets/ListWidget.js');
const Setting = require('@joplin/lib/models/Setting').default;
const _ = require('@joplin/lib/locale')._;
class FolderListWidget extends ListWidget {
constructor() {
export default class FolderListWidget extends ListWidget {
private folders_: FolderEntity[] = [];
public constructor() {
super();
this.tags_ = [];
this.folders_ = [];
this.searches_ = [];
this.selectedFolderId_ = null;
this.selectedTagId_ = null;
@@ -21,7 +25,8 @@ class FolderListWidget extends ListWidget {
this.trimItemTitle = false;
this.showIds = false;
this.itemRenderer = item => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
this.itemRenderer = (item: any) => {
const output = [];
if (item === '-') {
output.push('-'.repeat(this.innerWidth));
@@ -33,13 +38,13 @@ class FolderListWidget extends ListWidget {
}
output.push(Folder.displayTitle(item));
if (Setting.value('showNoteCounts')) {
if (Setting.value('showNoteCounts') && !item.deleted_time && item.id !== getTrashFolderId()) {
let noteCount = item.note_count;
// Subtract children note_count from parent folder.
if (this.folderHasChildren_(this.folders, item.id)) {
for (let i = 0; i < this.folders.length; i++) {
if (this.folders[i].parent_id === item.id) {
noteCount -= this.folders[i].note_count;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
noteCount -= (this.folders[i] as any).note_count;
}
}
}
@@ -56,113 +61,122 @@ class FolderListWidget extends ListWidget {
};
}
folderDepth(folders, folderId) {
public folderDepth(folders: FolderEntity[], folderId: string) {
let output = 0;
while (true) {
const folder = BaseModel.byId(folders, folderId);
if (!folder || !folder.parent_id) return output;
const folderParentId = getDisplayParentId(folder, folders.find(f => f.id === folder.parent_id));
if (!folder || !folderParentId) return output;
output++;
folderId = folder.parent_id;
folderId = folderParentId;
}
}
get selectedFolderId() {
public get selectedFolderId() {
return this.selectedFolderId_;
}
set selectedFolderId(v) {
public set selectedFolderId(v) {
this.selectedFolderId_ = v;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
get selectedSearchId() {
public get selectedSearchId() {
return this.selectedSearchId_;
}
set selectedSearchId(v) {
public set selectedSearchId(v) {
this.selectedSearchId_ = v;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
get selectedTagId() {
public get selectedTagId() {
return this.selectedTagId_;
}
set selectedTagId(v) {
public set selectedTagId(v) {
this.selectedTagId_ = v;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
get notesParentType() {
public get notesParentType() {
return this.notesParentType_;
}
set notesParentType(v) {
public set notesParentType(v) {
this.notesParentType_ = v;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
get searches() {
public get searches() {
return this.searches_;
}
set searches(v) {
public set searches(v) {
this.searches_ = v;
this.updateItems_ = true;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
get tags() {
public get tags() {
return this.tags_;
}
set tags(v) {
public set tags(v) {
this.tags_ = v;
this.updateItems_ = true;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
get folders() {
public get folders() {
return this.folders_;
}
set folders(v) {
public set folders(v) {
this.folders_ = v;
this.updateItems_ = true;
this.updateIndexFromSelectedItemId();
this.invalidate();
}
toggleShowIds() {
public toggleShowIds() {
this.showIds = !this.showIds;
this.invalidate();
}
folderHasChildren_(folders, folderId) {
public folderHasChildren_(folders: FolderEntity[], folderId: string) {
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];
if (folder.parent_id === folderId) return true;
const folderParentId = getDisplayParentId(folder, folders.find(f => f.id === folder.parent_id));
if (folderParentId === folderId) return true;
}
return false;
}
render() {
public render() {
if (this.updateItems_) {
this.logger().debug('Rebuilding items...', this.notesParentType, this.selectedJoplinItemId, this.selectedSearchId);
const wasSelectedItemId = this.selectedJoplinItemId;
const previousParentType = this.notesParentType;
let newItems = [];
const orderFolders = parentId => {
this.logger().info('FFFFFFFFFFFFF', JSON.stringify(this.folders, null, 4));
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
let newItems: any[] = [];
const orderFolders = (parentId: string) => {
this.logger().info('PARENT', parentId);
for (let i = 0; i < this.folders.length; i++) {
const f = this.folders[i];
const folderParentId = f.parent_id ? f.parent_id : '';
const originalParent = this.folders_.find(f => f.id === f.parent_id);
const folderParentId = getDisplayParentId(f, originalParent); // f.parent_id ? f.parent_id : '';
this.logger().info('FFF', f.title, folderParentId);
if (folderParentId === parentId) {
newItems.push(f);
if (this.folderHasChildren_(this.folders, f.id)) orderFolders(f.id);
@@ -192,7 +206,7 @@ class FolderListWidget extends ListWidget {
super.render();
}
get selectedJoplinItemId() {
public get selectedJoplinItemId() {
if (!this.notesParentType) return '';
if (this.notesParentType === 'Folder') return this.selectedFolderId;
if (this.notesParentType === 'Tag') return this.selectedTagId;
@@ -200,17 +214,15 @@ class FolderListWidget extends ListWidget {
throw new Error(`Unknown parent type: ${this.notesParentType}`);
}
get selectedJoplinItem() {
public get selectedJoplinItem() {
const id = this.selectedJoplinItemId;
const index = this.itemIndexByKey('id', id);
return this.itemAt(index);
}
updateIndexFromSelectedItemId(itemId = null) {
public updateIndexFromSelectedItemId(itemId: string = null) {
if (itemId === null) itemId = this.selectedJoplinItemId;
const index = this.itemIndexByKey('id', itemId);
this.currentIndex = index >= 0 ? index : 0;
}
}
module.exports = FolderListWidget;

View File

@@ -27,6 +27,7 @@ export default class StatusBarWidget extends BaseWidget {
this.invalidate();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async prompt(initialText = '', promptString: any = null, options: any = null) {
if (this.promptState_) throw new Error('Another prompt already active');
if (promptString === null) promptString = ':';
@@ -86,6 +87,7 @@ export default class StatusBarWidget extends BaseWidget {
// const textStyle = this.promptActive ? (s) => s : chalk.bgBlueBright.white;
// const textStyle = (s) => s;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const textStyle = this.promptActive ? (s: any) => s : chalk.gray;
this.term.drawHLine(this.absoluteInnerX, this.absoluteInnerY, this.innerWidth, textStyle(' '));
@@ -106,6 +108,7 @@ export default class StatusBarWidget extends BaseWidget {
const isSecurePrompt = !!this.promptState_.secure;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const options: any = {
cancelable: true,
history: this.history,
@@ -118,6 +121,7 @@ export default class StatusBarWidget extends BaseWidget {
if ('cursorPosition' in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition;
if (isSecurePrompt) options.echoChar = true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
this.inputEventEmitter_ = this.term.inputField(options, (error: any, input: any) => {
let resolveResult = null;
const resolveFn = this.promptState_.resolve;

View File

@@ -8,14 +8,17 @@ import uuid from '@joplin/lib/uuid';
const sandboxProxy = require('@joplin/lib/services/plugins/sandboxProxy');
function createConsoleWrapper(pluginId: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const wrapper: any = {};
for (const n in console) {
// eslint-disable-next-line no-console
if (!console.hasOwnProperty(n)) continue;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
wrapper[n] = (...args: any[]) => {
const newArgs = args.slice();
newArgs.splice(0, 0, `Plugin "${pluginId}":`);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
return (console as any)[n](...newArgs);
};
}
@@ -33,6 +36,7 @@ function createConsoleWrapper(pluginId: string) {
export default class PluginRunner extends BasePluginRunner {
private eventHandlers_: EventHandlers = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private activeSandboxCalls_: any = {};
public constructor() {
@@ -41,12 +45,14 @@ export default class PluginRunner extends BasePluginRunner {
this.eventHandler = this.eventHandler.bind(this);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private async eventHandler(eventHandlerId: string, args: any[]) {
const cb = this.eventHandlers_[eventHandlerId];
return cb(...args);
}
private newSandboxProxy(pluginId: string, sandbox: Global) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const target = async (path: string, args: any[]) => {
const callId = `${pluginId}::${path}::${uuid.createNano()}`;
this.activeSandboxCalls_[callId] = true;

View File

@@ -1,11 +1,12 @@
import { _ } from '@joplin/lib/locale';
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
export default (cmd: any, stdout: Function, store: Function, gui: Function) => {
cmd.setStdout((text: string) => {
return stdout(text);
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
cmd.setDispatcher((action: any) => {
if (store()) {
return store().dispatch(action);
@@ -14,6 +15,7 @@ export default (cmd: any, stdout: Function, store: Function, gui: Function) => {
}
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
cmd.setPrompt(async (message: string, options: any) => {
if (!options) options = {};
if (!options.type) options.type = 'boolean';

View File

@@ -3,7 +3,7 @@ import Folder from '@joplin/lib/models/Folder';
import BaseCommand from '../base-command';
import setupCommand from '../setupCommand';
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
export const setupCommandForTesting = (CommandClass: any, stdout: Function = null): BaseCommand => {
const command = new CommandClass();
setupCommand(command, stdout, null, null);

View File

@@ -35,15 +35,15 @@
],
"owner": "Laurent Cozic"
},
"version": "2.14.0",
"version": "3.0.0",
"bin": "./main.js",
"engines": {
"node": ">=10.0.0"
},
"dependencies": {
"@joplin/lib": "~2.14",
"@joplin/renderer": "~2.14",
"@joplin/utils": "~2.14",
"@joplin/lib": "~3.0",
"@joplin/renderer": "~3.0",
"@joplin/utils": "~3.0",
"aws-sdk": "2.1340.0",
"chalk": "4.1.2",
"compare-version": "0.1.2",
@@ -70,7 +70,7 @@
"yargs-parser": "21.1.1"
},
"devDependencies": {
"@joplin/tools": "~2.14",
"@joplin/tools": "~3.0",
"@types/fs-extra": "11.0.4",
"@types/jest": "29.5.8",
"@types/node": "18.19.8",

View File

@@ -22,6 +22,7 @@ describe('HtmlToMd', () => {
// if (htmlFilename.indexOf('image_preserve_size') !== 0) continue;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const htmlToMdOptions: any = {};
if (htmlFilename === 'anchor_local.html') {

View File

@@ -4,6 +4,7 @@ import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/
import shim from '@joplin/lib/shim';
const { themeStyle } = require('@joplin/lib/theme');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function newTestMdToHtml(options: any = null) {
options = {
ResourceModel: {
@@ -37,6 +38,7 @@ describe('MdToHtml', () => {
// if (mdFilename !== 'sanitize_9.md') continue;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const mdToHtmlOptions: any = {
bodyOnly: true,
};
@@ -86,6 +88,7 @@ describe('MdToHtml', () => {
}));
it('should return enabled plugin assets', (async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const pluginOptions: any = {};
const pluginNames = MdToHtml.pluginNames();

View File

@@ -7,9 +7,10 @@
<updated>20231224T151443Z</updated>
<content>
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div><a href="evernote:///view/5223870/s49/9cd5e810-fa03-429a-8194-ab847f2f1ab2/c99d9e01-ca35-4c75-ba63-f0c0ef97787d/" rel="noopener noreferrer" rev="en_rl_none">Note 2</a><a href="evernote:///view/5223870/s49/9cd5e810-fa03-429a-8194-ab847f2f1ab2/c99d9e01-ca35-4c75-ba63-f0c0ef97787d/" rel="noopener noreferrer" rev="en_rl_none">Note 3</a></div></en-note> ]]>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div><a href="evernote:///view/5223870/s49/9cd5e810-fa03-429a-8194-ab847f2f1ab2/c99d9e01-ca35-4c75-ba63-f0c0ef97787d/">Note 2</a><a href="evernote:///view/5223870/s49/9cd5e810-fa03-429a-8194-ab847f2f1ab2/c99d9e01-ca35-4c75-ba63-f0c0ef97787d/">Note 3</a></div></en-note> ]]>
</content>
</note>
<note>
<title>Note 2</title>
<created>20160730T111759Z</created>
@@ -19,15 +20,37 @@
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div>Testing</div></en-note> ]]>
</content>
</note>
<note>
<title>Note 3</title>
<created>20160730T111759Z</created>
<updated>20160730T111807Z</updated>
<content>
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div><a href="evernote:///view/5223870/s49/9cd5e810-fa03-429a-8194-ab847f2f1ab2/c99d9e01-ca35-4c75-ba63-f0c0ef97787d/" rel="noopener noreferrer" rev="en_rl_none">Ambiguous note</a></div></en-note> ]]>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div><a href="evernote:///view/5223870/s49/9cd5e810-fa03-429a-8194-ab847f2f1ab2/c99d9e01-ca35-4c75-ba63-f0c0ef97787d/">Ambiguous note</a></div></en-note> ]]>
</content>
</note>
<note>
<title>Note 4</title>
<created>20160730T111759Z</created>
<updated>20160730T111807Z</updated>
<content>
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div><a href="https://joplinapp.org">Note 5</a></div></en-note> ]]>
</content>
</note>
<note>
<title>Note 5</title>
<created>20160730T111759Z</created>
<updated>20160730T111807Z</updated>
<content>
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div></div></en-note> ]]>
</content>
</note>
<note>
<title>Ambiguous note</title>
<created>20160730T111759Z</created>
@@ -37,6 +60,7 @@
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div>Testing</div></en-note> ]]>
</content>
</note>
<note>
<title>Ambiguous note</title>
<created>20160730T111759Z</created>

View File

@@ -0,0 +1,20 @@
<div class="joplin-table-wrapper">
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>No</td>
<td>header</td>
</tr>
<tr>
<td>And no</td>
<td>surprises</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,4 @@
| | |
| --- | --- |
| No | header |
| And no | surprises |

View File

@@ -0,0 +1,16 @@
<div class="joplin-table-wrapper">
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Some paragraph<br class="jop-noMdConv"/><br class="jop-noMdConv"/>inside a table cell</td>
<td>Second column</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,3 @@
| | |
| --- | --- |
| Some paragraph<br><br>inside a table cell | Second column |

View File

@@ -5,6 +5,7 @@ import shim from '@joplin/lib/shim';
import Setting from '@joplin/lib/models/Setting';
import { db, setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function describeIfCompatible(name: string, fn: any, elseFn: any) {
if (['win32', 'darwin'].includes(shim.platformName())) {
return describe(name, fn);

View File

@@ -7,7 +7,7 @@ import Setting from '@joplin/lib/models/Setting';
import * as fs from 'fs-extra';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir, supportDir } from '@joplin/lib/testing/test-utils';
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir, supportDir, mockMobilePlatform } from '@joplin/lib/testing/test-utils';
import { newPluginScript } from '../../testUtils';
const testPluginDir = `${supportDir}/plugins`;
@@ -82,6 +82,7 @@ describe('services_PluginService', () => {
const allFolders = await Folder.all();
expect(allFolders.length).toBe(2);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
expect(allFolders.map((f: any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
}));
@@ -262,6 +263,68 @@ describe('services_PluginService', () => {
}
}));
it.each([
{
manifestPlatforms: ['desktop'],
isDesktop: true,
appVersion: '3.0.0',
shouldRun: true,
},
{
manifestPlatforms: ['desktop'],
isDesktop: false,
appVersion: '3.0.6',
shouldRun: false,
},
{
manifestPlatforms: ['desktop', 'mobile'],
isDesktop: false,
appVersion: '3.0.6',
shouldRun: true,
},
{
manifestPlatforms: [],
isDesktop: false,
appVersion: '3.0.8',
shouldRun: true,
},
])('should enable and disable plugins depending on what platform(s) they support (case %#: %j)', async ({ manifestPlatforms, isDesktop, appVersion, shouldRun }) => {
const pluginScript = `
/* joplin-manifest:
{
"id": "org.joplinapp.plugins.PluginTest",
"manifest_version": 1,
"app_min_version": "1.0.0",
"platforms": ${JSON.stringify(manifestPlatforms)},
"name": "JS Bundle test",
"version": "1.0.0"
}
*/
joplin.plugins.register({
onStart: async function() { },
});
`;
let resetPlatformMock = () => {};
if (!isDesktop) {
resetPlatformMock = mockMobilePlatform('android').reset;
}
try {
const service = newPluginService(appVersion);
const plugin = await service.loadPluginFromJsBundle('', pluginScript);
if (shouldRun) {
await expect(service.runPlugin(plugin)).resolves.toBeUndefined();
} else {
await expect(service.runPlugin(plugin)).rejects.toThrow(/disabled/);
}
} finally {
resetPlatformMock();
}
});
it('should install a plugin', (async () => {
const service = newPluginService();
const pluginPath = `${testPluginDir}/jpl_test/org.joplinapp.FirstJplPlugin.jpl`;

View File

@@ -1,9 +1,14 @@
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import { AppType } from '@joplin/lib/models/Setting';
import RepositoryApi, { AppInfo, InstallMode } from '@joplin/lib/services/plugins/RepositoryApi';
import shim from '@joplin/lib/shim';
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '@joplin/lib/testing/test-utils';
import { remove } from 'fs-extra';
async function newRepoApi(): Promise<RepositoryApi> {
const repo = new RepositoryApi(`${supportDir}/pluginRepo`, await createTempDir());
let tempDirs: string[] = [];
async function newRepoApi(appInfo: AppInfo = { type: AppType.Desktop, version: '3.0.0' }): Promise<RepositoryApi> {
const tempDir = await createTempDir();
tempDirs.push(tempDir);
const repo = new RepositoryApi(`${supportDir}/pluginRepo`, tempDir, appInfo, InstallMode.Default);
await repo.initialize();
return repo;
}
@@ -14,6 +19,12 @@ describe('services_plugins_RepositoryApi', () => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
});
afterEach(async () => {
for (const tempDir of tempDirs) {
await remove(tempDir);
}
tempDirs = [];
});
it('should get the manifests', (async () => {
const api = await newRepoApi();
@@ -27,9 +38,10 @@ describe('services_plugins_RepositoryApi', () => {
{
const results = await api.search('to');
expect(results.length).toBe(2);
expect(results.length).toBe(3);
expect(!!results.find(m => m.id === 'joplin.plugin.ambrt.backlinksToNote')).toBe(true);
expect(!!results.find(m => m.id === 'org.joplinapp.plugins.ToggleSidebars')).toBe(true);
expect(!!results.find(m => m.id === 'org.joplinapp.plugins.AbcSheetMusic')).toBe(true);
}
{
@@ -45,13 +57,14 @@ describe('services_plugins_RepositoryApi', () => {
expect(await shim.fsDriver().exists(pluginPath)).toBe(true);
}));
it('should tell if a plugin can be updated', (async () => {
const api = await newRepoApi();
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0', '3.0.0')).toBe(true);
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0', '1.0.0')).toBe(false);
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.2', '3.0.0')).toBe(false);
expect(await api.pluginCanBeUpdated('does.not.exist', '1.0.0', '3.0.0')).toBe(false);
it.each([
{ id: 'org.joplinapp.plugins.ToggleSidebars', installedVersion: '1.0.0', appVersion: '3.0.0', shouldBeUpdatable: true },
{ id: 'org.joplinapp.plugins.ToggleSidebars', installedVersion: '1.0.0', appVersion: '1.0.0', shouldBeUpdatable: false },
{ id: 'org.joplinapp.plugins.ToggleSidebars', installedVersion: '1.0.2', appVersion: '3.0.0', shouldBeUpdatable: false },
{ id: 'does.not.exist', installedVersion: '1.0.0', appVersion: '3.0.0', shouldBeUpdatable: false },
])('should tell if a plugin can be updated (case %#)', (async ({ id, installedVersion, appVersion, shouldBeUpdatable }) => {
const api = await newRepoApi({ version: appVersion, type: AppType.Desktop });
expect(await api.pluginCanBeUpdated(id, installedVersion)).toBe(shouldBeUpdatable);
}));
});

View File

@@ -2,6 +2,7 @@ import Setting from '@joplin/lib/models/Setting';
import { waitForFolderCount, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '@joplin/lib/testing/test-utils';
import Folder from '@joplin/lib/models/Folder';
import { newPluginScript, newPluginService } from '../../../testUtils';
import eventManager, { EventName } from '@joplin/lib/eventManager';
describe('JoplinSettings', () => {
@@ -66,6 +67,38 @@ describe('JoplinSettings', () => {
await service.destroy();
});
test('should de-register settings change listeners when a plugin is unloaded', async () => {
const service = newPluginService();
const pluginScript = newPluginScript(`
joplin.plugins.register({
onStart: async function() {
await joplin.settings.registerSettings({
'test-setting': {
value: 1234,
type: 1,
public: false,
label: 'Test',
}
});
// Register 8 listeners to improve test reliability in the case
// where listeners are added/removed from other sources.
for (let i = 0; i < 8; i++) {
await joplin.settings.onChange((event) => { });
}
},
});
`);
const plugin = await service.loadPluginFromJsBundle('', pluginScript);
await service.runPlugin(plugin);
const listenerCounter = eventManager.listenerCounter_(EventName.SettingsChange);
plugin.onUnload();
expect(listenerCounter.getCountRemoved()).toBeGreaterThanOrEqual(8);
});
test('should allow registering multiple settings', async () => {
const service = newPluginService();

View File

@@ -4,6 +4,7 @@ import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import ItemChange from '@joplin/lib/models/ItemChange';
import { newPluginScript, newPluginService } from '../../../testUtils';
import eventManager, { EventName } from '@joplin/lib/eventManager';
describe('JoplinWorkspace', () => {
@@ -17,6 +18,7 @@ describe('JoplinWorkspace', () => {
});
test('should listen to noteChange events', async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const appState: Record<string, any> = {
selectedNoteIds: [],
};
@@ -49,6 +51,7 @@ describe('JoplinWorkspace', () => {
const folder = (await Folder.all())[0];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const result: any = JSON.parse(folder.title);
expect(result.id).toBe(note.id);
@@ -79,4 +82,33 @@ describe('JoplinWorkspace', () => {
expect(modFolder.title).toBe('changedtitle');
});
test('should remove event listeners when plugins are unloaded', async () => {
const service = newPluginService();
const pluginScript = newPluginScript(`
joplin.plugins.register({
onStart: async () => {
// Register each listener 8 times to improve test reliability -- it's possible
// for listeners for the same events to be added/removed by other sources.
for (let i = 0; i < 8; i++) {
await joplin.workspace.onNoteChange(async (event) => { });
await joplin.workspace.onResourceChange(async (event) => { });
await joplin.workspace.filterEditorContextMenu(async (event) => { });
}
}
})
`);
const plugin = await service.loadPluginFromJsBundle('', pluginScript);
await service.runPlugin(plugin);
const itemChangeListenerCounter = eventManager.listenerCounter_(EventName.ItemChange);
const resourceChangeListenerCounter = eventManager.listenerCounter_(EventName.ResourceChange);
plugin.onUnload();
expect(itemChangeListenerCounter.getCountRemoved()).toBeGreaterThanOrEqual(8);
expect(resourceChangeListenerCounter.getCountRemoved()).toBeGreaterThanOrEqual(8);
await service.destroy();
});
});

View File

@@ -11,11 +11,13 @@ describe('services_plugins_sandboxProxy', () => {
it('should create a new sandbox proxy', (async () => {
interface Result {
path: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
args: any[];
}
const results: Result[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const target: any = (path: string, args: any[]) => {
results.push({ path, args });
};
@@ -34,11 +36,13 @@ describe('services_plugins_sandboxProxy', () => {
it('should allow importing a namespace', (async () => {
interface Result {
path: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
args: any[];
}
const results: Result[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const target: any = (path: string, args: any[]) => {
results.push({ path, args });
};

View File

@@ -16,6 +16,7 @@
"manifest_version": 1,
"id": "org.joplinapp.plugins.ToggleSidebars",
"app_min_version": "1.6",
"platforms": ["desktop"],
"version": "1.0.2",
"name": "Note list and side bar toggle buttons",
"description": "Adds buttons to toggle note list and sidebar",
@@ -25,5 +26,43 @@
"_publish_hash": "sha256:e0d833b7ef1bb8f02ee4cb861ef1989621358c45a5614911071302dc0527a3b4",
"_publish_commit": "dev:1b5b2342fc25717b77ad9f1627c1a334e5bbae54",
"_npm_package_name": "@joplin/joplin-plugin-toggle-sidebars"
},
"org.joplinapp.plugins.AbcSheetMusic": {
"manifest_version": 1,
"id": "org.joplinapp.plugins.AbcSheetMusic",
"app_min_version": "2.13.5",
"version": "1.0.5",
"name": "ABC Sheet Music Plugin",
"description": "Turns ABC text notation into sheet music",
"author": "Laurent Cozic",
"homepage_url": "https://github.com/joplin/plugin-abc-sheet-music",
"repository_url": "https://github.com/joplin/plugin-abc-sheet-music",
"category": "viewer",
"keywords": [
"sheet music",
"abc",
"notation"
],
"screenshots": [
{
"src": "images/PeacherineRag.png"
},
{
"src": "images/Tablature.png"
}
],
"icons": {
"16": "images/icon-16.png",
"32": "images/icon-32.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png"
},
"promo_tile": {
"src": "images/promo_tile.png"
},
"_publish_hash": "sha256:94a13d4833affbc949ba60174ab77419aa02b0777563a796f7480206b73a864b",
"_publish_commit": "master:2b128c7a21f010e9937a112e0cf1fb5c03f9ca75",
"_npm_package_name": "joplin-plugin-abc-sheet-music",
"_recommended": true
}
}

View File

@@ -0,0 +1,37 @@
{
"manifest_version": 1,
"id": "org.joplinapp.plugins.AbcSheetMusic",
"app_min_version": "2.13.5",
"version": "1.0.5",
"name": "ABC Sheet Music Plugin",
"description": "Turns ABC text notation into sheet music",
"author": "Laurent Cozic",
"homepage_url": "https://github.com/joplin/plugin-abc-sheet-music",
"repository_url": "https://github.com/joplin/plugin-abc-sheet-music",
"category": "viewer",
"keywords": [
"sheet music",
"abc",
"notation"
],
"screenshots": [
{
"src": "images/PeacherineRag.png"
},
{
"src": "images/Tablature.png"
}
],
"icons": {
"16": "images/icon-16.png",
"32": "images/icon-32.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png"
},
"promo_tile": {
"src": "images/promo_tile.png"
},
"_publish_hash": "sha256:94a13d4833affbc949ba60174ab77419aa02b0777563a796f7480206b73a864b",
"_publish_commit": "master:2b128c7a21f010e9937a112e0cf1fb5c03f9ca75",
"_npm_package_name": "joplin-plugin-abc-sheet-music"
}

View File

@@ -2,6 +2,7 @@
"manifest_version": 1,
"id": "org.joplinapp.plugins.ToggleSidebars",
"app_min_version": "1.6",
"platforms": ["desktop"],
"version": "1.0.2",
"name": "Note list and side bar toggle buttons",
"description": "Adds buttons to toggle note list and sidebar",

View File

@@ -566,8 +566,8 @@ export interface CodeMirrorControl {
};
}
export interface CodeMirrorContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
plugin: (codeMirrorControl: CodeMirrorControl)=> void;
export interface MarkdownEditorContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
plugin: (editorControl: CodeMirrorControl)=> void;
}
export enum ContentScriptType {

View File

@@ -6,7 +6,7 @@
//
import { lineNumbers, highlightActiveLineGutter, EditorView } from '@codemirror/view';
import { completeFromList } from '@codemirror/autocomplete';
import { CodeMirrorContentScriptModule, ContentScriptContext } from 'api/types';
import { MarkdownEditorContentScriptModule, ContentScriptContext } from 'api/types';
//
// For the above import to work, you may also need to add @codemirror/view as a dev dependency
// to package.json. (For the type information only).
@@ -15,7 +15,7 @@ import { CodeMirrorContentScriptModule, ContentScriptContext } from 'api/types';
// const { lineNumbers } = joplin.require('@codemirror/view');
export default (_context: ContentScriptContext): CodeMirrorContentScriptModule => {
export default (_context: ContentScriptContext): MarkdownEditorContentScriptModule => {
return {
// - codeMirrorWrapper: A thin wrapper around CodeMirror 6, designed to be similar to the
// CodeMirror 5 API. If running in CodeMirror 5, a CodeMirror object is provided instead.

View File

@@ -3,7 +3,7 @@ const leftPad = require('left-pad');
export default function(context) {
return {
plugin: function(markdownIt, _options) {
const pluginId = context.pluginId;
const contentScriptId = context.contentScriptId;
const defaultRender = markdownIt.renderer.rules.fence || function(tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options, env, self);
@@ -14,15 +14,30 @@ export default function(context) {
if (token.info !== 'justtesting') return defaultRender(tokens, idx, options, env, self);
const postMessageWithResponseTest = `
webviewApi.postMessage('${pluginId}', 'justtesting').then(function(response) {
webviewApi.postMessage('${contentScriptId}', 'justtesting').then(function(response) {
console.info('Got response in content script: ' + response);
});
return false;
`;
// Rich text editor support:
// The joplin-editable and joplin-source CSS classes mark the generated div
// as a region that needs special processing when converting back to markdown.
// This element helps Joplin reconstruct the original markdown.
const richTextEditorMetadata = `
<pre
class="joplin-source"
data-joplin-language="justtesting"
data-joplin-source-open="\`\`\`justtesting\n"
data-joplin-source-close="\`\`\`"
>${markdownIt.utils.escapeHtml(token.content)}</pre>
`;
return `
<div class="just-testing">
<p>JUST TESTING: <pre>${leftPad(token.content.trim(), 10, 'x')}</pre></p>
<div class="just-testing joplin-editable">
${richTextEditorMetadata}
<p>JUST TESTING: <pre>${markdownIt.utils.escapeHtml(leftPad(token.content.trim(), 10, 'x'))}</pre></p>
<p><a href="#" onclick="${postMessageWithResponseTest.replace(/\n/g, ' ')}">Click to post a message "justtesting" to plugin and check the response in the console</a></p>
</div>
`;

View File

@@ -1,12 +1,35 @@
import { Rectangle } from './types';
export interface Implementation {
nativeImage: any;
}
export interface CreateFromBufferOptions {
width?: number;
height?: number;
scaleFactor?: number;
}
export interface CreateFromPdfOptions {
/**
* The first page to export. Defaults to `1`, the first page in
* the document.
*/
minPage?: number;
/**
* The number of the last page to convert. Defaults to the last page
* if not given.
*
* If `maxPage` is greater than the number of pages in the PDF, all pages
* in the PDF will be converted to images.
*/
maxPage?: number;
scaleFactor?: number;
}
export interface PdfInfo {
pageCount: number;
}
export interface Implementation {
nativeImage: {
createFromPath: (path: string) => Promise<any>;
createFromPdf: (path: string, options: CreateFromPdfOptions) => Promise<any[]>;
};
getPdfInfo: (path: string) => Promise<PdfInfo>;
}
export interface ResizeOptions {
width?: number;
height?: number;
@@ -34,9 +57,13 @@ export default class JoplinImaging {
private cacheImage;
createFromPath(filePath: string): Promise<Handle>;
createFromResource(resourceId: string): Promise<Handle>;
createFromPdfPath(path: string, options?: CreateFromPdfOptions): Promise<Handle[]>;
createFromPdfResource(resourceId: string, options?: CreateFromPdfOptions): Promise<Handle[]>;
getPdfInfoFromPath(path: string): Promise<PdfInfo>;
getPdfInfoFromResource(resourceId: string): Promise<PdfInfo>;
getSize(handle: Handle): Promise<any>;
resize(handle: Handle, options?: ResizeOptions): Promise<string>;
crop(handle: Handle, rectange: Rectangle): Promise<string>;
crop(handle: Handle, rectangle: Rectangle): Promise<string>;
toPngFile(handle: Handle, filePath: string): Promise<void>;
/**
* Quality is between 0 and 100
@@ -57,5 +84,5 @@ export default class JoplinImaging {
* Image data is not automatically deleted by Joplin so make sure you call
* this method on the handle once you are done.
*/
free(handle: Handle): Promise<void>;
free(handles: Handle[] | Handle): Promise<void>;
}

View File

@@ -1,50 +1,110 @@
import joplin from 'api';
import { ToolbarButtonLocation } from 'api/types';
const registerMakeThumbnailCommand = async () => {
await joplin.commands.register({
name: 'makeThumbnail',
execute: async () => {
// ---------------------------------------------------------------
// Get the current note
// ---------------------------------------------------------------
const noteIds = await joplin.workspace.selectedNoteIds();
if (noteIds.length !== 1) return;
const noteId = noteIds[0];
// ---------------------------------------------------------------
// Get the top resource in that note (if any)
// ---------------------------------------------------------------
const result = await joplin.data.get(['notes', noteId, 'resources']);
if (result.items.length <= 0) return;
const resource = result.items[0];
// ---------------------------------------------------------------
// Create an image object and resize it
// ---------------------------------------------------------------
const imageHandle = await joplin.imaging.createFromResource(resource.id);
const resizedImageHandle = await joplin.imaging.resize(imageHandle, { width: 100 });
// ---------------------------------------------------------------
// Convert the image to a resource and add it to the note
// ---------------------------------------------------------------
const newResource = await joplin.imaging.toJpgResource(resizedImageHandle, { title: "Thumbnail" });
await joplin.commands.execute('insertText', '\n![](:/' + newResource.id + ')');
// ---------------------------------------------------------------
// Free up the image objects at the end
// ---------------------------------------------------------------
await joplin.imaging.free(imageHandle);
await joplin.imaging.free(resizedImageHandle);
},
});
await joplin.views.toolbarButtons.create('makeThumbnailButton', 'makeThumbnail', ToolbarButtonLocation.EditorToolbar);
};
const registerInlinePdfCommand = async () => {
await joplin.commands.register({
name: 'inlinePdfs',
execute: async () => {
// ---------------------------------------------------------------
// Get the current selection & extract a resource link
// ---------------------------------------------------------------
const selection: string = await joplin.commands.execute('selectedText');
// Matches content of the form
// [text here](:/32-letter-or-num-characters-here)
// Where ([a-z0-9]{32}) matches the resource ID.
const resourceLinkRegex = /\[.*\]\(:\/([a-z0-9]{32})\)/;
const resourceLinkMatch = selection.match(resourceLinkRegex);
if (!resourceLinkMatch) return;
const resourceId = resourceLinkMatch[1]; // The text of the region matching ([a-z0-9]{32})
const resource = await joplin.data.get(['resources', resourceId], { fields: ['mime'] });
const isPdf = resource.mime === 'application/pdf';
if (!isPdf) return;
// Clear the selection
await joplin.commands.execute('replaceSelection', '');
await joplin.commands.execute('insertText', selection);
// ---------------------------------------------------------------
// Convert the PDF to images
// ---------------------------------------------------------------
const pdfInfo = await joplin.imaging.getPdfInfoFromResource(resourceId);
const images = await joplin.imaging.createFromPdfResource(
resourceId,
// Convert at most 10 pages
{ minPage: 1, maxPage: 10, scaleFactor: 0.5 },
);
let pageNumber = 0;
for (const image of images) {
pageNumber++;
const pageResource = await joplin.imaging.toJpgResource(
image, { title: `Page ${pageNumber} of ${pdfInfo.pageCount}` }
);
await joplin.commands.execute('insertText', `\n- ![${pageResource.title}](:/${pageResource.id})`);
}
await joplin.imaging.free(images);
},
});
await joplin.views.toolbarButtons.create('inlineSelectedPdfsButton', 'inlinePdfs', ToolbarButtonLocation.EditorToolbar);
};
joplin.plugins.register({
onStart: async function() {
await joplin.commands.register({
name: 'makeThumbnail',
execute: async () => {
// ---------------------------------------------------------------
// Get the current note
// ---------------------------------------------------------------
const noteIds = await joplin.workspace.selectedNoteIds();
if (noteIds.length !== 1) return;
const noteId = noteIds[0];
// ---------------------------------------------------------------
// Get the top resource in that note (if any)
// ---------------------------------------------------------------
const result = await joplin.data.get(['notes', noteId, 'resources']);
if (result.items.length <= 0) return;
const resource = result.items[0];
// ---------------------------------------------------------------
// Create an image object and resize it
// ---------------------------------------------------------------
const imageHandle = await joplin.imaging.createFromResource(resource.id);
const resizedImageHandle = await joplin.imaging.resize(imageHandle, { width: 100 });
// ---------------------------------------------------------------
// Convert the image to a resource and add it to the note
// ---------------------------------------------------------------
const newResource = await joplin.imaging.toJpgResource(resizedImageHandle, { title: "Thumbnail" });
await joplin.commands.execute('insertText', '\n![](:/' + newResource.id + ')');
// ---------------------------------------------------------------
// Free up the image objects at the end
// ---------------------------------------------------------------
await joplin.imaging.free(imageHandle);
await joplin.imaging.free(resizedImageHandle);
},
});
await joplin.views.toolbarButtons.create('makeThumbnailButton', 'makeThumbnail', ToolbarButtonLocation.EditorToolbar);
await registerMakeThumbnailCommand();
await registerInlinePdfCommand();
},
});

View File

@@ -22,7 +22,7 @@ const registerSimpleTopToBottomRenderer = async () => {
dependencies: [
'item.selected',
'note.titleHtml',
'note.title',
'note.body',
'note.user_updated_time',
],
@@ -55,8 +55,8 @@ const registerSimpleTopToBottomRenderer = async () => {
itemTemplate: // html
`
<div class="content {{#item.selected}}-selected{{/item.selected}}">
<p class="title">{{{note.titleHtml}}}</p>
<p class="date">{{{updatedTime}}}</p>
<p class="title">{{note.title}}</p>
<p class="date">{{updatedTime}}</p>
<p class="body">{{noteBody}}</p>
</div>
`,
@@ -90,7 +90,7 @@ const registerSimpleLeftToRightRenderer = async() => {
dependencies: [
'note.id',
'item.selected',
'note.titleHtml',
'note.title',
'note.body',
],
@@ -124,7 +124,7 @@ const registerSimpleLeftToRightRenderer = async() => {
<img class="thumbnail" src="file://{{thumbnailFilePath}}"/>
{{/thumbnailFilePath}}
{{^thumbnailFilePath}}
{{{note.titleHtml}}}
{{{note.title}}}
{{/thumbnailFilePath}}
</div>
`,

View File

@@ -2,6 +2,7 @@ import PluginService from '@joplin/lib/services/plugins/PluginService';
import PluginRunner from '../app/services/plugins/PluginRunner';
export interface PluginServiceOptions {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
getState?(): Record<string, any>;
}

View File

@@ -5,7 +5,7 @@
//
// To get it working:
//
// - Run the Postgres database -- `sudo docker-compose --file docker-compose.db-dev.yml up`
// - Run the Postgres database -- `sudo docker compose --file docker-compose.db-dev.yml up`
// - Update the DB parameters in ~/joplin-credentials/server.env to use the dev
// database
// - Run the server - `JOPLIN_IS_TESTING=1 yarn start-dev`

View File

@@ -11,13 +11,9 @@
if (typeof browser !== 'undefined') {
// eslint-disable-next-line no-undef
browser_ = browser;
// eslint-disable-next-line no-undef
browserSupportsPromises_ = true;
} else if (typeof chrome !== 'undefined') {
// eslint-disable-next-line no-undef
browser_ = chrome;
// eslint-disable-next-line no-undef
browserSupportsPromises_ = false;
}
function escapeHtml(s) {
@@ -461,6 +457,7 @@
tags: command.tags,
windowInnerWidth: window.innerWidth,
windowInnerHeight: window.innerHeight,
devicePixelRatio: window.devicePixelRatio,
};
browser_.runtime.sendMessage({

View File

@@ -1,10 +1,12 @@
{
"manifest_version": 2,
"manifest_version": 3,
"name": "Joplin Web Clipper [DEV]",
"version": "2.14.0",
"version": "3.0.0",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplinapp.org",
"content_security_policy": "script-src 'self'; object-src 'self'",
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
},
"icons": {
"32": "icons/32.png",
"48": "icons/48.png",
@@ -13,18 +15,21 @@
"permissions": [
"activeTab",
"tabs",
"http://*/",
"https://*/",
"<all_urls>",
"scripting",
"storage"
],
"browser_action": {
"host_permissions": [
"http://*/",
"https://*/",
"<all_urls>"
],
"action": {
"default_icon": "icons/32.png",
"default_title": "Joplin Web Clipper",
"default_popup": "popup/build/index.html"
},
"commands": {
"_execute_browser_action": {
"_execute_action": {
"suggested_key": {
"default": "Alt+Shift+J"
}
@@ -49,12 +54,12 @@
}
},
"background": {
"scripts": [
"background.js"
],
"persistent": false
"scripts": ["service_worker.mjs"],
"service_worker": "service_worker.mjs",
"type": "module"
},
"applications": {
"browser_specific_settings": {
"gecko": {
"id": "{8419486a-54e9-11e8-9401-ac9e17909436}"
}

View File

@@ -115,6 +115,13 @@ class AppComponent extends Component {
this.clipScreenshot_click = async () => {
try {
// Firefox requires the <all_urls> host permission to take a
// screenshot of the current page, however, this may change
// in the future. Note that Firefox also forces this permission
// to be optional.
// See https://discourse.mozilla.org/t/browser-tabs-capturevisibletab-not-working-in-firefox-for-mv3/122965/3
await bridge().browser().permissions.request({ origins: ['<all_urls>'] });
const baseUrl = await bridge().clipperServerBaseUrl();
await bridge().sendCommandToActiveTab({
@@ -179,12 +186,14 @@ class AppComponent extends Component {
}
async loadContentScripts() {
await bridge().tabsExecuteScript({ file: '/content_scripts/setUpEnvironment.js' });
await bridge().tabsExecuteScript({ file: '/content_scripts/JSDOMParser.js' });
await bridge().tabsExecuteScript({ file: '/content_scripts/Readability.js' });
await bridge().tabsExecuteScript({ file: '/content_scripts/Readability-readerable.js' });
await bridge().tabsExecuteScript({ file: '/content_scripts/clipperUtils.js' });
await bridge().tabsExecuteScript({ file: '/content_scripts/index.js' });
await bridge().tabsExecuteScript([
'/content_scripts/setUpEnvironment.js',
'/content_scripts/JSDOMParser.js',
'/content_scripts/Readability.js',
'/content_scripts/Readability-readerable.js',
'/content_scripts/clipperUtils.js',
'/content_scripts/index.js',
]);
}
async componentDidMount() {
@@ -234,6 +243,7 @@ class AppComponent extends Component {
if (!ref) break;
lastRef = ref;
}
// eslint-disable-next-line no-restricted-properties
if (lastRef) lastRef.focus();
}
}

View File

@@ -1,5 +1,7 @@
/* eslint-disable no-console */
import getActiveTabs from '../../util/getActiveTabs.mjs';
import joplinEnv from '../../util/joplinEnv.mjs';
const { randomClipperPort } = require('./randomClipperPort');
function msleep(ms) {
@@ -17,13 +19,12 @@ class Bridge {
this.token_ = null;
}
async init(browser, browserSupportsPromises, store) {
async init(browser, store) {
console.info('Popup: Init bridge');
this.browser_ = browser;
this.dispatch_ = store.dispatch;
this.store_ = store;
this.browserSupportsPromises_ = browserSupportsPromises;
this.clipperServerPort_ = null;
this.clipperServerPortStatus_ = 'searching';
@@ -74,12 +75,7 @@ class Bridge {
}
};
this.browser_.runtime.onMessage.addListener(this.browser_notify);
const backgroundPage = await this.backgroundPage(this.browser_);
// Not sure why the getBackgroundPage() sometimes returns null, so
// in that case default to "prod" environment, which means the live
// extension won't be affected by this bug.
this.env_ = backgroundPage ? backgroundPage.joplinEnv() : 'prod';
this.env_ = joplinEnv();
console.info('Popup: Env:', this.env());
@@ -197,17 +193,6 @@ class Bridge {
}
}
async backgroundPage(browser) {
const bgp = browser.extension.getBackgroundPage();
if (bgp) return bgp;
return new Promise((resolve) => {
browser.runtime.getBackgroundPage((bgp) => {
resolve(bgp);
});
});
}
env() {
return this.env_;
}
@@ -305,50 +290,26 @@ class Bridge {
return `http://127.0.0.1:${port}`;
}
async tabsExecuteScript(options) {
if (this.browserSupportsPromises_) return this.browser().tabs.executeScript(options);
return new Promise((resolve, reject) => {
this.browser().tabs.executeScript(options, () => {
const e = this.browser().runtime.lastError;
if (e) {
const msg = [`tabsExecuteScript: Cannot load ${JSON.stringify(options)}`];
if (e.message) msg.push(e.message);
reject(new Error(msg.join(': ')));
}
resolve();
});
async tabsExecuteScript(files) {
const activeTabs = await getActiveTabs(this.browser());
await this.browser().scripting.executeScript({
target: {
tabId: activeTabs[0].id,
},
files,
});
}
async tabsQuery(options) {
if (this.browserSupportsPromises_) return this.browser().tabs.query(options);
return new Promise((resolve) => {
this.browser().tabs.query(options, (tabs) => {
resolve(tabs);
});
});
return this.browser().tabs.query(options);
}
async tabsSendMessage(tabId, command) {
if (this.browserSupportsPromises_) return this.browser().tabs.sendMessage(tabId, command);
return new Promise((resolve) => {
this.browser().tabs.sendMessage(tabId, command, (result) => {
resolve(result);
});
});
return this.browser().tabs.sendMessage(tabId, command);
}
async tabsCreate(options) {
if (this.browserSupportsPromises_) return this.browser().tabs.create(options);
return new Promise((resolve) => {
this.browser().tabs.create(options, () => {
resolve();
});
});
return this.browser().tabs.create(options);
}
async folderTree() {
@@ -356,29 +317,15 @@ class Bridge {
}
async storageSet(keys) {
if (this.browserSupportsPromises_) return this.browser().storage.local.set(keys);
return new Promise((resolve) => {
this.browser().storage.local.set(keys, () => {
resolve();
});
});
return this.browser().storage.local.set(keys);
}
async storageGet(keys, defaultValue = null) {
if (this.browserSupportsPromises_) {
try {
const r = await this.browser().storage.local.get(keys);
return r;
} catch (error) {
return defaultValue;
}
} else {
return new Promise((resolve) => {
this.browser().storage.local.get(keys, (result) => {
resolve(result);
});
});
try {
const r = await this.browser().storage.local.get(keys);
return r;
} catch (error) {
return defaultValue;
}
}

View File

@@ -112,7 +112,7 @@ async function main() {
console.info('Popup: Init bridge and restore state...');
await bridge().init(window.browser ? window.browser : window.chrome, !!window.browser, store);
await bridge().init(window.browser ? window.browser : window.chrome, store);
console.info('Popup: Creating React app...');

View File

@@ -1,25 +1,13 @@
import joplinEnv from './util/joplinEnv.mjs';
import getActiveTabs from './util/getActiveTabs.mjs';
let browser_ = null;
if (typeof browser !== 'undefined') {
browser_ = browser;
browserSupportsPromises_ = true;
} else if (typeof chrome !== 'undefined') {
browser_ = chrome;
browserSupportsPromises_ = false;
}
let env_ = null;
// Make this function global so that it can be accessed
// from the popup too.
// https://stackoverflow.com/questions/6323184/communication-between-background-page-and-popup-page-in-a-chrome-extension
window.joplinEnv = function() {
if (env_) return env_;
const manifest = browser_.runtime.getManifest();
env_ = manifest.name.indexOf('[DEV]') >= 0 ? 'dev' : 'prod';
return env_;
};
async function browserCaptureVisibleTabs(windowId) {
const options = {
format: 'jpeg',
@@ -31,53 +19,19 @@ async function browserCaptureVisibleTabs(windowId) {
// https://discourse.joplinapp.org/t/clip-screenshot-image-quality/12302/4
quality: 92,
};
if (browserSupportsPromises_) return browser_.tabs.captureVisibleTab(windowId, options);
return new Promise((resolve) => {
browser_.tabs.captureVisibleTab(windowId, options, (image) => {
resolve(image);
});
});
}
async function browserGetZoom(tabId) {
if (browserSupportsPromises_) return browser_.tabs.getZoom(tabId);
return new Promise((resolve) => {
browser_.tabs.getZoom(tabId, (zoom) => {
resolve(zoom);
});
});
return browser_.tabs.captureVisibleTab(windowId, options);
}
browser_.runtime.onInstalled.addListener(() => {
if (window.joplinEnv() === 'dev') {
browser_.browserAction.setIcon({
if (joplinEnv() === 'dev') {
browser_.action.setIcon({
path: 'icons/32-dev.png',
});
}
});
async function getImageSize(dataUrl) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve({ width: image.width, height: image.height });
};
image.onerror = function(event) {
reject(event);
};
image.src = dataUrl;
});
}
browser_.runtime.onMessage.addListener(async (command) => {
if (command.name === 'screenshotArea') {
const browserZoom = await browserGetZoom();
// The dimensions of the image returned by Firefox are the regular ones,
// while the one returned by Chrome depend on the screen pixel ratio. So
// it would return a 600*400 image if the window dimensions are 300x200
@@ -90,15 +44,18 @@ browser_.runtime.onMessage.addListener(async (command) => {
//
// The crop rectangle is always in real pixels, so we need to multiply
// it by the ratio we've calculated.
//
// 8/3/2024: With manifest v3, we don't have access to DOM APIs in Chrome.
// As a result, we can't easily calculate the size of the captured image.
// We instead base the crop region exclusively on window.devicePixelRatio,
// which seems to work in modern Firefox and Chrome.
const imageDataUrl = await browserCaptureVisibleTabs(null);
const imageSize = await getImageSize(imageDataUrl);
const imagePixelRatio = imageSize.width / command.content.windowInnerWidth;
const content = { ...command.content };
content.image_data_url = imageDataUrl;
if ('url' in content) content.source_url = content.url;
const ratio = browserZoom * imagePixelRatio;
const ratio = content.devicePixelRatio;
const newArea = { ...command.content.crop_rect };
newArea.x *= ratio;
newArea.y *= ratio;
@@ -117,19 +74,8 @@ browser_.runtime.onMessage.addListener(async (command) => {
}
});
async function getActiveTabs() {
const options = { active: true, currentWindow: true };
if (browserSupportsPromises_) return browser_.tabs.query(options);
return new Promise((resolve) => {
browser_.tabs.query(options, (tabs) => {
resolve(tabs);
});
});
}
async function sendClipMessage(clipType) {
const tabs = await getActiveTabs();
const tabs = await getActiveTabs(browser_);
if (!tabs || !tabs.length) {
console.error('No active tabs');
return;

View File

@@ -0,0 +1,7 @@
const getActiveTabs = async (browser) => {
const options = { active: true, currentWindow: true };
return await browser.tabs.query(options);
}
export default getActiveTabs;

View File

@@ -0,0 +1,3 @@
// AUTOGENERATED by release-clipper
export default () => 'dev';

View File

@@ -20,24 +20,28 @@ interface RendererProcessQuitReply {
}
interface PluginWindows {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
[key: string]: any;
}
export default class ElectronAppWrapper {
private logger_: Logger = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private electronApp_: any;
private env_: string;
private isDebugMode_: boolean;
private profilePath_: string;
private win_: BrowserWindow = null;
private willQuitApp_ = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private tray_: any = null;
private buildDir_: string = null;
private rendererProcessQuitReply_: RendererProcessQuitReply = null;
private pluginWindows_: PluginWindows = {};
private initialCallbackUrl_: string = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public constructor(electronApp: any, env: string, profilePath: string|null, isDebugMode: boolean, initialCallbackUrl: string) {
this.electronApp_ = electronApp;
this.env_ = env;
@@ -115,6 +119,7 @@ export default class ElectronAppWrapper {
const windowStateKeeper = require('electron-window-state');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const stateOptions: any = {
defaultWidth: Math.round(0.8 * screen.getPrimaryDisplay().workArea.width),
defaultHeight: Math.round(0.8 * screen.getPrimaryDisplay().workArea.height),
@@ -126,6 +131,7 @@ export default class ElectronAppWrapper {
// Load the previous state with fallback to defaults
const windowState = windowStateKeeper(stateOptions);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const windowOptions: any = {
x: windowState.x,
y: windowState.y,
@@ -192,6 +198,7 @@ export default class ElectronAppWrapper {
});
this.win_.webContents.on('did-fail-load', async event => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
if ((event as any).isMainFrame) {
await this.handleAppFailure('Renderer process failed to load', false);
}
@@ -228,6 +235,7 @@ export default class ElectronAppWrapper {
}
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
this.win_.on('close', (event: any) => {
// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
// otherwise the window is simply hidden, and will be re-open once the app is "activated" (which happens when the
@@ -276,6 +284,7 @@ export default class ElectronAppWrapper {
}
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
ipcMain.on('asynchronous-message', (_event: any, message: string, args: any) => {
if (message === 'appCloseReply') {
// We got the response from the renderer process:
@@ -287,6 +296,7 @@ export default class ElectronAppWrapper {
// This handler receives IPC messages from a plugin or from the main window,
// and forwards it to the main window or the plugin window.
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
ipcMain.on('pluginMessage', (_event: any, message: PluginMessage) => {
try {
if (message.target === 'mainWindow') {
@@ -325,6 +335,7 @@ export default class ElectronAppWrapper {
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public registerPluginWindow(pluginId: string, window: any) {
this.pluginWindows_[pluginId] = window;
}
@@ -387,6 +398,7 @@ export default class ElectronAppWrapper {
}
// Note: this must be called only after the "ready" event of the app has been dispatched
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public createTray(contextMenu: any) {
try {
this.tray_ = new Tray(`${this.buildDir()}/icons/${this.trayIconFilename_()}`);
@@ -423,11 +435,13 @@ export default class ElectronAppWrapper {
}
// Someone tried to open a second instance - focus our window instead
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
this.electronApp_.on('second-instance', (_e: any, argv: string[]) => {
const win = this.window();
if (!win) return;
if (win.isMinimized()) win.restore();
win.show();
// eslint-disable-next-line no-restricted-properties
win.focus();
if (process.platform !== 'darwin') {
const url = argv.find((arg) => isCallbackUrl(arg));
@@ -462,6 +476,7 @@ export default class ElectronAppWrapper {
this.win_.show();
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
this.electronApp_.on('open-url', (event: any, url: string) => {
event.preventDefault();
void this.openCallbackUrl(url);

View File

@@ -68,6 +68,7 @@ export default class InteropServiceHelper {
win = bridge().newBrowserWindow(windowOptions);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
return new Promise<any>((resolve, reject) => {
win.webContents.on('did-finish-load', () => {
@@ -85,6 +86,7 @@ export default class InteropServiceHelper {
// pdfs.
// https://github.com/laurent22/joplin/issues/6254.
await win.webContents.executeJavaScript('document.querySelectorAll(\'details\').forEach(el=>el.setAttribute(\'open\',\'\'))');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const data = await win.webContents.printToPDF(options as any);
resolve(data);
} catch (error) {
@@ -126,6 +128,7 @@ export default class InteropServiceHelper {
resolve(null);
}, 1000);
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
win.webContents.print(options as any, (success: boolean, reason: string) => {
cleanup();
if (!success && reason !== 'cancelled') reject(new Error(`Could not print: ${reason}`));

View File

@@ -11,6 +11,7 @@ const logger = Logger.create('app.reducer');
export interface AppStateRoute {
type: string;
routeName: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
props: any;
}
@@ -21,29 +22,36 @@ export enum AppStateDialogName {
export interface AppStateDialog {
name: AppStateDialogName;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
props: Record<string, any>;
}
export interface AppState extends State {
route: AppStateRoute;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
navHistory: any[];
noteVisiblePanes: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
windowContentSize: any;
watchedNoteFiles: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
lastEditorScrollPercents: any;
devToolsVisible: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: string;
layoutMoveMode: boolean;
startupPluginsLoaded: boolean;
// Extra reducer keys go here
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
watchedResources: any;
mainLayout: LayoutItem;
dialogs: AppStateDialog[];
isResettingLayout: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export function createAppDefaultState(windowContentSize: any, resourceEditWatcherDefaultState: any): AppState {
return {
...defaultState,
@@ -69,6 +77,7 @@ export function createAppDefaultState(windowContentSize: any, resourceEditWatche
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export default function(state: AppState, action: any) {
let newState = state;
@@ -129,6 +138,7 @@ export default function(state: AppState, action: any) {
case 'NOTE_VISIBLE_PANES_TOGGLE':
{
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const getNextLayout = (currentLayout: any) => {
currentLayout = panes.length === 2 ? 'both' : currentLayout[0];
@@ -183,6 +193,7 @@ export default function(state: AppState, action: any) {
logger.warn('MAIN_LAYOUT_SET_ITEM_PROP: Found an empty item in layout: ', JSON.stringify(state.mainLayout));
} else {
if (item.key === action.itemKey) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
(item as any)[action.propName] = action.propValue;
return false;
}

View File

@@ -84,6 +84,7 @@ const appDefaultState = createAppDefaultState(
class Application extends BaseApplication {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private checkAllPluginStartedIID_: any = null;
private initPluginServiceDone_ = false;
private ocrService_: OcrService;
@@ -98,6 +99,7 @@ class Application extends BaseApplication {
return true;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public reducer(state: AppState = appDefaultState, action: any) {
let newState = appReducer(state, action);
newState = resourceEditWatcherReducer(newState, action);
@@ -113,6 +115,7 @@ class Application extends BaseApplication {
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
protected async generalMiddleware(store: any, next: any, action: any) {
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'locale' || action.type === 'SETTING_UPDATE_ALL') {
setLocale(Setting.value('locale'));
@@ -137,6 +140,10 @@ class Application extends BaseApplication {
webFrame.setZoomFactor(Setting.value('windowContentZoomFactor') / 100);
}
if (action.type === 'SETTING_UPDATE_ONE' && action.key === 'linking.extraAllowedExtensions' || action.type === 'SETTING_UPDATE_ALL') {
bridge().extraAllowedOpenExtensions = Setting.value('linking.extraAllowedExtensions');
}
if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) {
await AlarmService.updateNoteNotification(action.id, action.type === 'NOTE_DELETE');
}
@@ -201,8 +208,10 @@ class Application extends BaseApplication {
// The '*' and '!important' parts are necessary to make sure Russian text is displayed properly
// https://github.com/laurent22/joplin/issues/155
//
// Note: Be careful about the specificity here. Incorrect specificity can break monospaced fonts in tables.
const css = `.CodeMirror *, .cm-editor .cm-content { font-family: ${fontFamilies.join(', ')} !important; }`;
const css = `.CodeMirror5 *, .cm-editor .cm-content { font-family: ${fontFamilies.join(', ')} !important; }`;
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.appendChild(document.createTextNode(css));
@@ -227,13 +236,16 @@ class Application extends BaseApplication {
// The context menu must be setup in renderer process because that's where
// the spell checker service lives.
electronContextMenu({
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
shouldShowMenu: (_event: any, params: any) => {
// params.inputFieldType === 'none' when right-clicking the text editor. This is a bit of a hack to detect it because in this
// case we don't want to use the built-in context menu but a custom one.
return params.isEditable && params.inputFieldType !== 'none';
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
menu: (actions: any, props: any) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(props.misspelledWord, props.dictionarySuggestions).map((item: any) => new MenuItem(item));
const output = [
@@ -277,17 +289,7 @@ class Application extends BaseApplication {
}
try {
const devPluginOptions = { devMode: true, builtIn: false };
if (Setting.value('plugins.devPluginPaths')) {
const paths = Setting.value('plugins.devPluginPaths').split(',').map((p: string) => p.trim());
await service.loadAndRunPlugins(paths, pluginSettings, devPluginOptions);
}
// Also load dev plugins that have passed via command line arguments
if (Setting.value('startupDevPlugins')) {
await service.loadAndRunPlugins(Setting.value('startupDevPlugins'), pluginSettings, devPluginOptions);
}
await service.loadAndRunDevPlugins(pluginSettings);
} catch (error) {
this.logger().error(`There was an error loading plugins from ${Setting.value('plugins.devPluginPaths')}:`, error);
}
@@ -354,6 +356,7 @@ class Application extends BaseApplication {
private setupOcrService() {
if (Setting.value('ocr.enabled')) {
if (!this.ocrService_) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const Tesseract = (window as any).Tesseract;
const driver = new OcrDriverTesseract(
@@ -379,6 +382,7 @@ class Application extends BaseApplication {
eventManager.on(EventName.ResourceChange, handleResourceChange);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async start(argv: string[], startOptions: StartOptions = null): Promise<any> {
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
// insert an extra argument so that they can be processed in a consistent way everywhere.
@@ -460,6 +464,7 @@ class Application extends BaseApplication {
// manually call dispatchUpdateAll() to force an update.
Setting.dispatchUpdateAll();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
await refreshFolders((action: any) => this.dispatch(action));
const tags = await Tag.allWithNotes();
@@ -588,12 +593,14 @@ class Application extends BaseApplication {
ResourceEditWatcher.instance().initialize(
reg.logger(),
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
(action: any) => { this.store().dispatch(action); },
(path: string) => bridge().openItem(path),
);
// Forwards the local event to the global event manager, so that it can
// be picked up by the plugin manager.
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
ResourceEditWatcher.instance().on('resourceChange', (event: any) => {
eventManager.emit(EventName.ResourceChange, event);
});
@@ -602,6 +609,7 @@ class Application extends BaseApplication {
// Make it available to the console window - useful to call revisionService.collectRevisions()
if (Setting.value('env') === 'dev') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
(window as any).joplin = {
revisionService: RevisionService.instance(),
migrationService: MigrationService.instance(),
@@ -617,6 +625,9 @@ class Application extends BaseApplication {
}
bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated);
bridge().setOnAllowedExtensionsChangeListener((newExtensions) => {
Setting.setValue('linking.extraAllowedExtensions', newExtensions);
});
await this.initPluginService();
@@ -636,12 +647,17 @@ class Application extends BaseApplication {
SearchEngine.instance().scheduleSyncTables();
});
// await populateDatabase(reg.db(), {
// clearDatabase: true,
// folderCount: 1000,
// rootFolderCount: 1,
// subFolderDepth: 1,
// });
// setTimeout(() => {
// void populateDatabase(reg.db(), {
// clearDatabase: true,
// folderCount: 200,
// noteCount: 3000,
// tagCount: 2000,
// tagsPerNote: 10,
// rootFolderCount: 20,
// subFolderDepth: 3,
// });
// }, 5000);
// setTimeout(() => {
// console.info(CommandService.instance().commandsToMarkdownTable(this.store().getState()));

View File

@@ -1,15 +1,16 @@
import ElectronAppWrapper from './ElectronAppWrapper';
import shim from '@joplin/lib/shim';
import { _, setLocale } from '@joplin/lib/locale';
import { BrowserWindow, nativeTheme, nativeImage, shell } from 'electron';
import { dirname, isUncPath, toSystemSlashes } from '@joplin/lib/path-utils';
import { BrowserWindow, nativeTheme, nativeImage, shell, dialog, MessageBoxSyncOptions } from 'electron';
import { dirname, toSystemSlashes } from '@joplin/lib/path-utils';
import { fileUriToPath } from '@joplin/utils/url';
import { urlDecode } from '@joplin/lib/string-utils';
import * as Sentry from '@sentry/electron/main';
import { homedir } from 'os';
import { msleep } from '@joplin/utils/time';
import { pathExists, writeFileSync } from 'fs-extra';
import { normalize } from 'path';
import { extname, normalize } from 'path';
import isSafeToOpen from './utils/isSafeToOpen';
interface LastSelectedPath {
file: string;
@@ -20,9 +21,15 @@ interface OpenDialogOptions {
properties?: string[];
defaultPath?: string;
createDirectory?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
filters?: any[];
}
type OnAllowedExtensionsChange = (newExtensions: string[])=> void;
interface MessageDialogOptions extends Omit<MessageBoxSyncOptions, 'message'> {
message?: string;
}
export class Bridge {
private electronWrapper_: ElectronAppWrapper;
@@ -32,6 +39,9 @@ export class Bridge {
private appName_: string;
private appId_: string;
private extraAllowedExtensions_: string[] = [];
private onAllowedExtensionsChangeListener_: OnAllowedExtensionsChange = ()=>{};
public constructor(electronWrapper: ElectronAppWrapper, appId: string, appName: string, rootProfileDir: string, autoUploadCrashDumps: boolean) {
this.electronWrapper_ = electronWrapper;
this.appId_ = appId;
@@ -84,11 +94,6 @@ export class Bridge {
return this.rootProfileDir_;
}
private logWarning(...message: string[]) {
// eslint-disable-next-line no-console
console.warn('bridge:', ...message);
}
public electronApp() {
return this.electronWrapper_;
}
@@ -105,6 +110,24 @@ export class Bridge {
this.autoUploadCrashDumps_ = v;
}
public get extraAllowedOpenExtensions() {
return this.extraAllowedExtensions_;
}
public set extraAllowedOpenExtensions(newValue: string[]) {
const oldValue = this.extraAllowedExtensions_;
const changed = newValue.length !== oldValue.length || newValue.some((v, idx) => v !== oldValue[idx]);
if (changed) {
this.extraAllowedExtensions_ = newValue;
this.onAllowedExtensionsChangeListener_?.(this.extraAllowedExtensions_);
}
}
public setOnAllowedExtensionsChangeListener(listener: OnAllowedExtensionsChange) {
this.onAllowedExtensionsChangeListener_ = listener;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async captureException(error: any) {
Sentry.captureException(error);
// We wait to give the "beforeSend" event handler time to process the crash dump and write
@@ -170,6 +193,7 @@ export class Bridge {
electronApp: this.electronApp(),
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
shouldShowMenu: (_event: any, params: any) => {
return params.isEditable;
},
@@ -198,6 +222,7 @@ export class Bridge {
return require('electron').shell.showItemInFolder(toSystemSlashes(fullPath));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public newBrowserWindow(options: any) {
return new BrowserWindow(options);
}
@@ -227,8 +252,8 @@ export class Bridge {
return this.window().webContents.closeDevTools();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public async showSaveDialog(options: any) {
const { dialog } = require('electron');
if (!options) options = {};
if (!('defaultPath' in options) && this.lastSelectedPaths_.file) options.defaultPath = this.lastSelectedPaths_.file;
const { filePath } = await dialog.showSaveDialog(this.window(), options);
@@ -239,27 +264,29 @@ export class Bridge {
}
public async showOpenDialog(options: OpenDialogOptions = null) {
const { dialog } = require('electron');
if (!options) options = {};
let fileType = 'file';
if (options.properties && options.properties.includes('openDirectory')) fileType = 'directory';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
if (!('defaultPath' in options) && (this.lastSelectedPaths_ as any)[fileType]) options.defaultPath = (this.lastSelectedPaths_ as any)[fileType];
if (!('createDirectory' in options)) options.createDirectory = true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const { filePaths } = await dialog.showOpenDialog(this.window(), options as any);
if (filePaths && filePaths.length) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
(this.lastSelectedPaths_ as any)[fileType] = dirname(filePaths[0]);
}
return filePaths;
}
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead
private showMessageBox_(window: any, options: any): number {
const { dialog } = require('electron');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private showMessageBox_(window: any, options: MessageDialogOptions): number {
if (!window) window = this.window();
return dialog.showMessageBoxSync(window, options);
return dialog.showMessageBoxSync(window, { message: '', ...options });
}
public showErrorMessageBox(message: string, options: any = null) {
public showErrorMessageBox(message: string, options: MessageDialogOptions = null) {
options = {
buttons: [_('OK')],
...options,
@@ -272,7 +299,7 @@ export class Bridge {
});
}
public showConfirmMessageBox(message: string, options: any = null) {
public showConfirmMessageBox(message: string, options: MessageDialogOptions = null) {
options = {
buttons: [_('OK'), _('Cancel')],
...options,
@@ -287,9 +314,7 @@ export class Bridge {
}
/* returns the index of the clicked button */
public showMessageBox(message: string, options: any = null) {
if (options === null) options = {};
public showMessageBox(message: string, options: MessageDialogOptions = {}) {
const result = this.showMessageBox_(this.window(), { type: 'question',
message: message,
buttons: [_('OK'), _('Cancel')], ...options });
@@ -297,6 +322,7 @@ export class Bridge {
return result;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public showInfoMessageBox(message: string, options: any = {}) {
const result = this.showMessageBox_(this.window(), { type: 'info',
message: message,
@@ -331,14 +357,43 @@ export class Bridge {
fullPath = fileUriToPath(urlDecode(fullPath), shim.platformName());
}
fullPath = normalize(fullPath);
// On Windows, \\example.com\... links can map to network drives. Opening files on these
// drives can lead to arbitrary remote code execution.
const isUntrustedUncPath = isUncPath(fullPath);
if (isUntrustedUncPath) {
this.logWarning(`Not opening external file link: ${fullPath} -- it starts with two \\s, so could be to a network drive.`);
return 'Refusing to open file on a network drive.';
} else if (await pathExists(fullPath)) {
return shell.openPath(fullPath);
// Note: pathExists is intended to mitigate a security issue related to network drives
// on Windows.
if (await pathExists(fullPath)) {
const fileExtension = extname(fullPath);
const userAllowedExtension = this.extraAllowedOpenExtensions.includes(fileExtension);
if (userAllowedExtension || await isSafeToOpen(fullPath)) {
return shell.openPath(fullPath);
} else {
const allowOpenId = 2;
const learnMoreId = 1;
const fileExtensionDescription = JSON.stringify(fileExtension);
const result = await dialog.showMessageBox(this.window(), {
title: _('Unknown file type'),
message:
_('Joplin doesn\'t recognise the %s extension. Opening this file could be dangerous. What would you like to do?', fileExtensionDescription),
type: 'warning',
checkboxLabel: _('Always open %s files without asking.', fileExtensionDescription),
buttons: [
_('Cancel'),
_('Learn more'),
_('Open it'),
],
});
if (result.response === learnMoreId) {
void this.openExternal('https://joplinapp.org/help/apps/attachments#unknown-filetype-warning');
return 'Learn more shown';
} else if (result.response !== allowOpenId) {
return 'Cancelled by user';
}
if (result.checkboxChecked) {
this.extraAllowedOpenExtensions = this.extraAllowedOpenExtensions.concat(fileExtension);
}
return shell.openPath(fullPath);
}
} else {
return 'Path does not exist.';
}

View File

@@ -64,6 +64,7 @@ async function addSkippedVersion(s: string) {
await KvStore.instance().setValue('updateCheck::skippedVersions', JSON.stringify(versions));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export default async function checkForUpdates(inBackground: boolean, parentWindow: any, options: CheckForUpdateOptions) {
if (isCheckingForUpdate_) {
logger.info('Skipping check because it is already running');

View File

@@ -0,0 +1,24 @@
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import emptyTrash from '@joplin/lib/services/trash/emptyTrash';
import bridge from '../services/bridge';
export const declaration: CommandDeclaration = {
name: 'emptyTrash',
label: () => _('Empty trash'),
};
export const runtime = (): CommandRuntime => {
return {
execute: async () => {
const ok = await bridge().showConfirmMessageBox(_('This will permanently delete all items in the trash. Continue?'), {
buttons: [
_('Empty trash'),
_('Cancel'),
],
});
if (ok) await emptyTrash();
},
};
};

View File

@@ -11,6 +11,7 @@ export const runtime = (): CommandRuntime => {
return {
// "targetPath" should be a file for JEX export (because the format can
// contain multiple folders) or a directory otherwise.
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
execute: async (_context: any, folderIds: string[], format: ExportModuleOutputFormat, targetPath: string) => {
const exportOptions: ExportOptions = {
sourceFolderIds: folderIds,

View File

@@ -8,6 +8,7 @@ export const declaration: CommandDeclaration = {
export const runtime = (): CommandRuntime => {
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
execute: async (_context: any, noteIds: string[], format: ExportModuleOutputFormat, targetDirectoryPath: string) => {
const exportOptions: ExportOptions = {
path: targetDirectoryPath,

View File

@@ -6,6 +6,7 @@ export const declaration: CommandDeclaration = {
export const runtime = (): CommandRuntime => {
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
execute: async (_context: any, target: string) => {
if (target === 'noteBody') return CommandService.instance().execute('focusElementNoteBody');
if (target === 'noteList') return CommandService.instance().execute('focusElementNoteList');

View File

@@ -1,6 +1,7 @@
// AUTO-GENERATED using `gulp buildScriptIndexes`
import * as copyDevCommand from './copyDevCommand';
import * as editProfileConfig from './editProfileConfig';
import * as emptyTrash from './emptyTrash';
import * as exportFolders from './exportFolders';
import * as exportNotes from './exportNotes';
import * as focusElement from './focusElement';
@@ -19,6 +20,7 @@ import * as toggleSafeMode from './toggleSafeMode';
const index: any[] = [
copyDevCommand,
editProfileConfig,
emptyTrash,
exportFolders,
exportNotes,
focusElement,

View File

@@ -23,6 +23,7 @@ export const runtime = (): CommandRuntime => {
}
},
enabledCondition: 'oneNoteSelected && !noteIsReadOnly && (!modalDialogVisible || gotoAnythingVisible)',
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
mapStateToTitle: (state: any) => {
const noteId = stateUtils.selectedNoteId(state);
return state.watchedNoteFiles.includes(noteId) ? _('Stop') : '';

View File

@@ -2,6 +2,9 @@ import * as React from 'react';
const styled = require('styled-components').default;
const { space } = require('styled-system');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied;
type StyleProps = any;
export enum ButtonLevel {
Primary = 'primary',
Secondary = 'secondary',
@@ -26,6 +29,7 @@ interface Props {
iconAnimation?: string;
tooltip?: string;
disabled?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied;
style?: any;
size?: ButtonSize;
isSquare?: boolean;
@@ -71,131 +75,131 @@ const StyledButtonBase = styled.button`
`;
const StyledIcon = styled(styled.span(space))`
font-size: ${(props: any) => props.theme.toolbarIconSize}px;
${(props: any) => props.animation ? `animation: ${props.animation}` : ''};
font-size: ${(props: StyleProps) => props.theme.toolbarIconSize}px;
${(props: StyleProps) => props.animation ? `animation: ${props.animation}` : ''};
`;
const StyledButtonPrimary = styled(StyledButtonBase)`
border: none;
background-color: ${(props: any) => props.theme.backgroundColor5};
background-color: ${(props: StyleProps) => props.theme.backgroundColor5};
${(props: any) => props.disabled} {
${(props: StyleProps) => props.disabled} {
&:hover {
background-color: ${(props: any) => props.theme.backgroundColorHover5};
background-color: ${(props: StyleProps) => props.theme.backgroundColorHover5};
}
&:active {
background-color: ${(props: any) => props.theme.backgroundColorActive5};
background-color: ${(props: StyleProps) => props.theme.backgroundColorActive5};
}
}
${StyledIcon} {
color: ${(props: any) => props.theme.color5};
color: ${(props: StyleProps) => props.theme.color5};
}
${StyledTitle} {
color: ${(props: any) => props.theme.color5};
color: ${(props: StyleProps) => props.theme.color5};
}
`;
const StyledButtonSecondary = styled(StyledButtonBase)`
border: 1px solid ${(props: any) => props.theme.borderColor4};
background-color: ${(props: any) => props.theme.backgroundColor4};
border: 1px solid ${(props: StyleProps) => props.theme.borderColor4};
background-color: ${(props: StyleProps) => props.theme.backgroundColor4};
${(props: any) => props.disabled} {
${(props: StyleProps) => props.disabled} {
&:hover {
background-color: ${(props: any) => props.theme.backgroundColorHover4};
background-color: ${(props: StyleProps) => props.theme.backgroundColorHover4};
}
&:active {
background-color: ${(props: any) => props.theme.backgroundColorActive4};
background-color: ${(props: StyleProps) => props.theme.backgroundColorActive4};
}
}
${StyledIcon} {
color: ${(props: any) => props.theme.color4};
color: ${(props: StyleProps) => props.theme.color4};
}
${StyledTitle} {
color: ${(props: any) => props.theme.color4};
color: ${(props: StyleProps) => props.theme.color4};
}
`;
const StyledButtonTertiary = styled(StyledButtonBase)`
border: 1px solid ${(props: any) => props.theme.color3};
background-color: ${(props: any) => props.theme.backgroundColor3};
border: 1px solid ${(props: StyleProps) => props.theme.color3};
background-color: ${(props: StyleProps) => props.theme.backgroundColor3};
&:hover {
background-color: ${(props: any) => props.theme.backgroundColorHoverDim3};
background-color: ${(props: StyleProps) => props.theme.backgroundColorHoverDim3};
}
&:active {
background-color: ${(props: any) => props.theme.backgroundColorActive3};
background-color: ${(props: StyleProps) => props.theme.backgroundColorActive3};
}
${StyledIcon} {
color: ${(props: any) => props.theme.color};
color: ${(props: StyleProps) => props.theme.color};
}
${StyledTitle} {
color: ${(props: any) => props.theme.color};
color: ${(props: StyleProps) => props.theme.color};
opacity: 0.9;
}
`;
const StyledButtonRecommended = styled(StyledButtonBase)`
border: 1px solid ${(props: any) => props.theme.borderColor4};
background-color: ${(props: any) => props.theme.warningBackgroundColor};
border: 1px solid ${(props: StyleProps) => props.theme.borderColor4};
background-color: ${(props: StyleProps) => props.theme.warningBackgroundColor};
${StyledIcon} {
color: ${(props: any) => props.theme.color};
color: ${(props: StyleProps) => props.theme.color};
}
${StyledTitle} {
color: ${(props: any) => props.theme.color};
color: ${(props: StyleProps) => props.theme.color};
opacity: 0.9;
}
`;
const StyledButtonSidebarSecondary = styled(StyledButtonBase)`
background: none;
border-color: ${(props: any) => props.theme.color2};
color: ${(props: any) => props.theme.color2};
border-color: ${(props: StyleProps) => props.theme.color2};
color: ${(props: StyleProps) => props.theme.color2};
&:hover {
color: ${(props: any) => props.theme.colorHover2};
border-color: ${(props: any) => props.theme.colorHover2};
color: ${(props: StyleProps) => props.theme.colorHover2};
border-color: ${(props: StyleProps) => props.theme.colorHover2};
background: none;
${StyledTitle} {
color: ${(props: any) => props.theme.colorHover2};
color: ${(props: StyleProps) => props.theme.colorHover2};
}
${StyledIcon} {
color: ${(props: any) => props.theme.colorHover2};
color: ${(props: StyleProps) => props.theme.colorHover2};
}
}
&:active {
color: ${(props: any) => props.theme.colorActive2};
border-color: ${(props: any) => props.theme.colorActive2};
color: ${(props: StyleProps) => props.theme.colorActive2};
border-color: ${(props: StyleProps) => props.theme.colorActive2};
background: none;
${StyledTitle} {
color: ${(props: any) => props.theme.colorActive2};
color: ${(props: StyleProps) => props.theme.colorActive2};
}
${StyledIcon} {
color: ${(props: any) => props.theme.colorActive2};
color: ${(props: StyleProps) => props.theme.colorActive2};
}
}
${StyledTitle} {
color: ${(props: any) => props.theme.color2};
color: ${(props: StyleProps) => props.theme.color2};
}
${StyledIcon} {
color: ${(props: any) => props.theme.color2};
color: ${(props: StyleProps) => props.theme.color2};
}
`;
@@ -207,6 +211,7 @@ function buttonClass(level: ButtonLevel) {
return StyledButtonSecondary;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied;
const Button = React.forwardRef((props: Props, ref: any) => {
const iconOnly = props.iconName && !props.title;

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