1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-27 20:29:45 +02:00

Compare commits

...

247 Commits

Author SHA1 Message Date
Laurent Cozic
5c35569b5b Android 3.3.2 2025-03-03 22:36:37 +00:00
Laurent Cozic
5f02af9724 Server v3.3.4 2025-03-03 22:29:46 +00:00
Henry Heino
975f16d21c Server: Security: Improve request validation in default route (#11916) 2025-03-03 22:29:05 +00:00
Laurent Cozic
06359834d6 Desktop: Add a button to collapse or expand all folders (#11905) 2025-03-02 22:20:47 +00:00
Laurent Cozic
0cc0fec8c3 lock file 2025-03-01 13:04:37 +00:00
Joplin Bot
68ab5dcda5 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-03-01 02:08:23 +00:00
Joplin Bot
65544123e6 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-02-28 18:43:59 +00:00
Laurent Cozic
cfbded00e2 Merge branch 'release-3.2' into dev 2025-02-28 14:14:21 +00:00
Laurent Cozic
a898e17b4c Desktop release v3.2.13 2025-02-28 14:13:20 +00:00
pedr
d12e2d9a81 Desktop: Fixes #11759: Preserve attachment file extensions regardless of the mime type (#11852)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-02-28 14:13:08 +00:00
Henry Heino
7025321d76 Mobile: Accessibility: Fix "new note" and "new to-do" buttons are focusable even while invisible (#11899) 2025-02-28 10:30:50 +00:00
Laurent Cozic
6c890121b9 Doc: Update release cycle 2025-02-27 18:47:02 +00:00
Josh Scheitler
9c4be00745 Desktop: Resolves #11663: Improve Rich Text Editor toolbar structure (#11869)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-02-27 18:32:47 +00:00
Henry Heino
7f51712311 Android: Switch default library used for Whisper voice typing (#11881) 2025-02-27 18:31:13 +00:00
Anmol Garg
502c929c88 Chore: Update Docker Compose POSTGRES_HOST for proper service-to-service communication (#11886) 2025-02-27 18:26:21 +00:00
Kev Bittner
1abf9e9602 Doc: Update S3 synchronization documentation (#11890) 2025-02-27 18:24:45 +00:00
Laurent Cozic
8bdb6c5d72 Desktop: Add dialog to select a note and link to it (#11891) 2025-02-27 18:24:02 +00:00
Henry Heino
9cbd1b855c Desktop: Accessibility: Add more standard keyboard shortcuts for the notebook sidebar (#11892) 2025-02-27 18:23:28 +00:00
Laurent Cozic
ae8658554f Desktop: Fix issue with GotoAnything that would prevent it from highlighting search results in note titles (#11888) 2025-02-27 16:29:33 +00:00
renovate[bot]
bc385d59e9 Update dependency @types/node to v18.19.67 (#11880)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 16:13:24 +00:00
renovate[bot]
00ccd994e3 Update dependency @types/adm-zip to v0.5.7 (#11895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 16:11:10 +00:00
Laurent Cozic
9251299289 Deskop: Attempt to capture more debug info when the app crashes 2025-02-26 10:51:18 +00:00
Laurent Cozic
fe67a44285 Plugins: Add support for joplin.shouldUseDarkColors API 2025-02-25 15:33:44 +00:00
Laurent Cozic
50a1b184fd Chore: Desktop: Ensure dev tools are open on startup in dev mode 2025-02-24 16:55:52 +00:00
Laurent Cozic
3caa718132 Chore: Improve error message when renderMarkup command cannot render some text 2025-02-24 16:54:57 +00:00
Laurent Cozic
d0e16c0878 Chore: Improve error message when data API cannot parse a note 2025-02-24 16:54:56 +00:00
renovate[bot]
4fcb250c27 Update dependency @bam.tech/react-native-image-resizer to v3.0.11 (#11879)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 00:01:48 +00:00
Laurent Cozic
86e59ad621 Server v3.3.3 2025-02-23 19:07:47 +00:00
Laurent Cozic
12baa9827d Server: Fixed patching user properties 2025-02-23 18:40:12 +00:00
pedr
95c50ada7c Mobile: Fixes #11858: Fix disabled encryption keys list showing enabled keys (#11861) 2025-02-23 14:08:55 +00:00
Henry Heino
55a57f7baf Mobile: Resolves #11846: Improve encryption config screen accessibility (#11874) 2025-02-23 14:08:09 +00:00
summoner001
69b24b4437 Update hu-HU.po (#11877) 2025-02-23 13:56:11 +00:00
Henry Heino
5143fae0f6 Mobile: Fixes #11864: Fix voice recorder crash (#11876) 2025-02-23 13:53:28 +00:00
Henry Heino
01a62acfdf Chore: Fix yarn tsc fails when run from packages/utils (#11873) 2025-02-23 13:52:24 +00:00
renovate[bot]
c663742689 Update dependency @types/node to v18.19.65 (#11875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-23 11:42:29 +00:00
Laurent Cozic
0c405951ed Server v3.3.2 2025-02-19 23:16:30 +00:00
Laurent Cozic
4b411e600c Chore: Add iOS entitlements for push notifications 2025-02-19 21:58:43 +00:00
Laurent Cozic
bf58a52394 Chore: Server: Exclude db migration from test 2025-02-19 21:57:13 +00:00
Laurent Cozic
36d3736bff Server v3.3.1 2025-02-19 19:19:02 +00:00
Laurent Cozic
4df0b9f851 Server: Optimise delta sync queries by optimising the underlying SQL query 2025-02-19 19:01:09 +00:00
Joplin Bot
914b5e230d Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-02-19 18:42:49 +00:00
pedr
9278fd7910 Desktop: Accessibility: Add error indication on Note properties (#11784) 2025-02-19 18:32:29 +00:00
Laurent Cozic
2180ad1d9b iOS 13.3.1 2025-02-19 16:04:53 +00:00
Laurent Cozic
d301cdf992 Android 3.3.1 2025-02-19 16:03:59 +00:00
Laurent Cozic
200d3c84e0 Desktop release v3.3.2 2025-02-19 15:37:37 +00:00
Laurent Cozic
6cadaa2137 lock file 2025-02-19 15:37:20 +00:00
Henry Heino
8221081514 Mobile: Support attaching audio recordings (#11836) 2025-02-19 15:23:20 +00:00
Laurent Cozic
dd06b1e680 Desktop: Improve usability of note list when ticking to-dos using the Space key (#11855) 2025-02-19 15:19:20 +00:00
pedr
70e0ae0c2c Desktop: Fixes #11844: Fix OneNote importer not being able to handle corrupted attachments (#11859) 2025-02-19 15:18:53 +00:00
Laurent Cozic
7aeec923e3 Doc: Update OCR documentation 2025-02-19 14:33:01 +00:00
pedr
70d64225c8 Desktop: Accessibility: Make click outside of dialog content be cancellable (#11765) 2025-02-18 18:25:49 +00:00
Henry Heino
ad0ecc2320 Desktop: Fixes #11847: Hide extra clear button in search field (#11851) 2025-02-18 18:17:40 +00:00
pedr
8a28edcda8 Desktop: Fixes #11759: Preserve attachment file extensions regardless of the mime type (#11852)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-02-18 18:17:23 +00:00
Henry Heino
c8640aa7f8 Desktop: Fix Rich Text right-click and paste regressions (#11850) 2025-02-18 18:15:46 +00:00
Jozef Gaal
ddf75d6c52 New strings translated to Slovak (#11856) 2025-02-18 18:14:43 +00:00
Kamila Łopuszańska
0a42317e07 Doc: Resolves #11842: Updated faq.md (#11853) 2025-02-18 18:14:10 +00:00
Henry Heino
51ce1b06fe Chore: Docs: Document creating new editor commands (#11829) 2025-02-18 18:13:32 +00:00
Laurent Cozic
44c735afac Desktop: Improve behaviour of note list to-dos when ticking a checkbox using the keyboard 2025-02-17 22:38:43 +00:00
Laurent Cozic
c6154cfb4e Mobile: Add support for plugin editor views (#11831)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2025-02-17 13:47:56 +00:00
Joplin Bot
d2aad1d6c7 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-02-17 12:59:15 +00:00
Dan Dascalescu
3e81cc8585 Docs: update note tagging instructions in 1_welcome_to_joplin.md (#11834) 2025-02-17 12:11:45 +00:00
Henry Heino
abc5c062c3 iOS: Fix "attach file" doesn't work the first time after startup (#11839) 2025-02-17 12:09:10 +00:00
Henry Heino
316ef9d960 Desktop,Mobile: Plugins: Simplify getting the ID of the note open in an editor (#11841) 2025-02-17 12:08:48 +00:00
Henry Heino
b870f8344c iOS: Fixes #11835: Allow attaching videos to notes (#11840) 2025-02-17 12:07:15 +00:00
Joplin Bot
6f6683d15d Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-02-16 18:40:23 +00:00
Henry Heino
da59aef95b Desktop release v3.3.1 2025-02-16 08:36:01 -08:00
Laurent Cozic
c55979cd03 Desktop: Enable OCR processing by default 2025-02-16 16:15:51 +00:00
Laurent Cozic
07f4217f17 Chore: Fixed spelling issue 2025-02-13 10:30:20 +00:00
Laurent Cozic
8a7071179d Doc: Add "Area outside of Joplin's Threat Model" to Security.md 2025-02-13 09:55:10 +00:00
Joplin Bot
2c9a12307e Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-02-13 01:59:29 +00:00
renovate[bot]
dd3864fa47 Update dependency @adobe/css-tools to v4.4.1 (#11830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 00:00:10 +00:00
Laurent Cozic
43c1c5849b Doc: Update sponsors 2025-02-12 22:57:30 +00:00
Sahil Rathore
5e08ff0621 Mobile: Fixes #11827: Canceling dev plugin path setup shows error (#11828)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-02-12 20:01:44 +00:00
balaraz
45838c0223 Update Ukrainian translation (#11824) 2025-02-12 08:55:29 +00:00
Henry Heino
91ac4f8526 Chore: Resolves #11800: Web: Improve reload behavior in dev mode (#11803) 2025-02-11 21:15:01 +00:00
Laurent Cozic
3603350287 lock file 2025-02-11 18:12:11 +00:00
Henry Heino
bcde346ebe Web: Fixes #11800: Fix "Only one client can be open at a time" shown in dev mode (#11819) 2025-02-11 15:31:39 +00:00
renovate[bot]
9803d7985d Update dependency @types/node-fetch to v2.6.12 (#11817)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 15:52:17 +00:00
renovate[bot]
30f6b3ecb2 Update dependency @axe-core/playwright to v4.10.1 (#11816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 15:52:07 +00:00
Self Not Found
0b287d1113 Website: Fix compatibility issues of Mermaid graphs (#11809) 2025-02-09 09:39:42 +00:00
renovate[bot]
be18655ceb Update Rust crate thiserror to v1.0.69 (#11808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 09:37:04 +00:00
Henry Heino
be43ff42c9 Web: Upgrade react-native-web to v0.19.13 (#11804) 2025-02-08 10:02:35 +00:00
pedr
1230e1b30c Desktop: Accessibility: Add a new shortcut to set focus to editor toolbar (#11764) 2025-02-07 20:41:22 +00:00
pedr
f5d168b16a Desktop: Accessibility: Fixes focus going to start of document when Note History is open (#11769) 2025-02-07 20:41:03 +00:00
Henry Heino
7055d3db18 Windows: Re-enable the beta "auto-update" feature flag (#11802) 2025-02-07 20:18:40 +00:00
mrjo118
18a9c3f841 Desktop, Mobile: Harden failsafe logic to check for the presence of info.json, rather than just the item count (#11750) 2025-02-07 19:08:01 +00:00
pedr
a4ab197c42 Desktop: Accessibility: Add label to the delete buttons of the Note Attachments (#11749) 2025-02-07 19:04:37 +00:00
Henry Heino
2b5881a103 Mobile: Accessibility: Make it possible to create and edit profiles with a screen reader (#11797) 2025-02-07 19:00:24 +00:00
Henry Heino
e9dee4cd99 Desktop,Mobile: Resolves #11790: Render strikethrough text in the editor (#11795) 2025-02-07 19:00:02 +00:00
Henry Heino
dd948f5c95 Desktop: Fixes #11783: Plugins: Fix toast notifications don't reappear unless parameters are changed (#11786) 2025-02-07 18:59:54 +00:00
Laurent Cozic
798e1b8f4f Desktop, Mobile, Cli: Move S3 sync target out of beta 2025-02-06 22:22:28 +00:00
Self Not Found
b3f69794b1 All: Add specification document for new encryption methods (#11754) 2025-02-06 18:14:51 +00:00
Henry Heino
f25e1a5e80 Desktop,Mobile: Plugins: Legacy editor API: Fix delayed crash caused by out-of-bounds inputs (#11714) 2025-02-06 18:12:43 +00:00
Henry Heino
17e463b6bc Desktop: Resolves #11710: Plugins: Mark the LanguageTool Integration plugin as incompatible (#11715) 2025-02-06 18:12:30 +00:00
Henry Heino
786e55c972 Desktop: Performance: Improve performance when changing window state (#11720) 2025-02-06 18:12:16 +00:00
mrjo118
cd9155514c Mobile: Fixes #11571: Use alternative fix to set the sqlite CursorWindow size to 50mb (#11726) 2025-02-06 18:11:51 +00:00
Henry Heino
3e9e669642 Desktop: Built-in plugins: Update Freehand Drawing to v2.15.0 (#11735) 2025-02-06 18:11:10 +00:00
pedr
e36a30eb1a Desktop: Fix datetime values not appearing on note properties when the picker is open (#11748) 2025-02-06 18:10:59 +00:00
Henry Heino
1975ebd438 Desktop,Mobile: Highlight ==marked== text in the Markdown editor (#11794) 2025-02-06 18:04:15 +00:00
Henry Heino
94bff77313 Mobile: Update js-draw to version 1.27.2 (#11788) 2025-02-06 18:03:58 +00:00
Henry Heino
6e3258a5d8 Mobile: Plugins: Support the showToast API (#11787) 2025-02-06 18:03:50 +00:00
Henry Heino
c55c8d62ec Desktop: Fixes #11662: Prevent the default note title from being "&nbsp;" (#11785) 2025-02-06 18:00:40 +00:00
Henry Heino
c7031568a8 Mobile: Fix homescreen new-note shortcuts are re-applied after switching notes (#11779) 2025-02-06 17:57:27 +00:00
Henry Heino
c2c72215b9 Desktop: Accessibility: Add accessibility information to the warning banner (#11775) 2025-02-06 17:57:12 +00:00
Henry Heino
cc09f92d3b Mobile: Toolbar: Show only half of last button to indicate scroll (#11772) 2025-02-06 17:56:56 +00:00
Henry Heino
8312196faa Mobile,Desktop,Cli: Logging: Log less information at level warn when a decryption error occurs (#11771) 2025-02-06 17:56:16 +00:00
Henry Heino
a16a66c37b Desktop,Mobile: Sync: Fix share not marked as readonly if the recipient has an outgoing share (#11770) 2025-02-06 17:55:54 +00:00
Henry Heino
a8210225a0 Desktop: Improve font picker accessibility (#11763) 2025-02-06 17:40:01 +00:00
Henry Heino
cd50454664 Android: Fix error logged when opening the note editor (#11761) 2025-02-06 17:39:48 +00:00
Henry Heino
986163721d Chore: Slightly stronger types in ResourceService.ts (#11756) 2025-02-06 17:39:33 +00:00
Laurent Cozic
e41dcb9bc9 Doc: Archived GSoC 2024 documents 2025-02-05 16:45:07 +00:00
Henry Heino
f90e642f43 Desktop: Fix crash when closing a secondary window with the Rich Text Editor open (#11737) 2025-02-04 00:09:12 +00:00
renovate[bot]
67d1dd36be Update Rust crate thiserror to v1.0.68 (#11768)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-02-03 11:30:54 +00:00
renovate[bot]
2cba693905 Update dependency @types/node to v18.19.64 (#11747)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 00:44:16 +00:00
renovate[bot]
a226ede5d7 Update Rust crate thiserror to v1.0.67 (#11766)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 00:41:50 +00:00
Laurent Cozic
7994c0bc79 Desktop: Disable featureFlag.autoUpdaterServiceEnabled for now 2025-02-03 00:14:35 +00:00
Joplin Bot
d589891a86 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-02-01 00:54:39 +00:00
renovate[bot]
fe6c949cc1 Update Rust crate thiserror to v1.0.66 (#11755)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 08:21:55 +00:00
Henry Heino
4e677d2baf Desktop: Accessibility: Fix incorrect note viewer accessibility label (#11744) 2025-01-30 06:58:53 -08:00
Henry Heino
25aab57af5 Desktop: Fix secondary windows not removed from state if closed while focused (#11740) 2025-01-30 06:58:12 -08:00
pedr
db81064c98 Desktop: Fixes #11738: Context menu was empty for notes on Trash folder (#11743) 2025-01-30 06:57:55 -08:00
renovate[bot]
9b82578253 Update dependency @types/node to v18.19.61 (#11736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 20:07:22 +00:00
Henry Heino
bb513c83ac Desktop: Accessibility: Rich Text Editor: Make it possible to edit code blocks with a keyboard or touchscreen (#11727) 2025-01-27 12:05:29 -08:00
Henry Heino
662185816d Desktop: Accessibility: Allow toggling between tab navigation and indentation (#11717) 2025-01-27 10:34:58 -08:00
Henry Heino
cc1582d535 Desktop,Mobile,Cli: Fixes #11630: Adjust how items are queried by ID (#11734) 2025-01-27 10:34:46 -08:00
Henry Heino
aa6348a127 Setup new release 3.3 2025-01-27 09:35:44 -08:00
pedr
68f4b8ed0c Desktop: Accessibility: Fix input fields not associated with labels (#11700) 2025-01-27 08:52:11 -08:00
Henry Heino
98540493e0 Desktop: Accessibility: Improve scrollbar contrast (#11708) 2025-01-27 08:23:00 -08:00
Henry Heino
762daa5a68 Desktop: Accessibility: Improve "change application layout" screen keyboard accessibility (#11718) 2025-01-27 08:18:37 -08:00
renovate[bot]
827233605e Update dependency @types/node to v18.19.60 (#11731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 15:53:13 +00:00
renovate[bot]
31b13defb6 Update dependency @types/adm-zip to v0.5.6 (#11730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 15:52:06 +00:00
pedr
8611391d01 Desktop: Accessibility: Replacing library used for datetime with native input element (#11725) 2025-01-27 07:50:38 -08:00
renovate[bot]
5a3d57e39a Update dependency @react-native-clipboard/clipboard to v1.14.3 (#11724)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-26 02:30:00 +00:00
Joplin Bot
e22ccd6edf Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-24 00:49:56 +00:00
renovate[bot]
8aec0ae445 Update dependency @react-native-community/slider to v4.5.5 (#11719)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 23:55:20 +00:00
Henry Heino
24a2f5452c Merge branch 'release-3.2' into dev 2025-01-23 15:42:51 -08:00
Henry Heino
d6f1ca4ba4 Desktop release v3.2.12 2025-01-23 14:57:15 -08:00
Henry Heino
2a058ed809 Desktop,Mobile: Security: Improve comment escaping (#11706) 2025-01-23 14:38:52 -08:00
renovate[bot]
877123bda7 Update Rust crate encoding_rs to v0.8.35 (#11704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 14:35:01 +00:00
Henry Heino
d621e631f7 Desktop: Fix error when a note has no history (#11702) 2025-01-22 13:39:21 +00:00
Laurent Cozic
64d1da9773 Doc: Removed Twitter links 2025-01-21 22:12:25 +00:00
Joplin Bot
2643bb9b32 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-21 18:23:39 +00:00
Laurent Cozic
5c737b3ccd Doc: Update sponsors 2025-01-21 17:08:34 +00:00
Laurent Cozic
23f75f8784 Desktop: Added shortcut Cmd+Option+N to open note in new window, and added command to menu bar 2025-01-21 15:08:20 +00:00
Joplin Bot
60b2f69620 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-21 12:26:51 +00:00
renovate[bot]
1d00b7a68e Update dependency @types/node to v18.19.59 (#11693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 12:08:11 +00:00
pedr
d0b783c595 Desktop: Resolves #11642: Add alt text to welcome notes (#11643) 2025-01-21 10:29:21 +00:00
pedr
9c446b03da Desktop: Resolves #11654: Add scrollbar to Note Revision to allow usage on zoomed interface (#11689) 2025-01-21 10:28:38 +00:00
renovate[bot]
0603c56446 Update Rust crate thiserror to v1.0.65 (#11688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 10:27:33 +00:00
Laurent Cozic
4223864302 Desktop, Mobile: Do not add double newlines around attached files (#11690) 2025-01-21 10:25:28 +00:00
summoner001
0ddf5732a8 Update hu_HU.po (#11682) 2025-01-20 17:45:25 +00:00
renovate[bot]
a5ffc11831 Update dependency @types/node to v18.19.58 (#11685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 17:36:49 +00:00
pedr
157ad2c0cd Desktop: Resolves #11621: Accessibility: Add status update after update (#11634) 2025-01-20 17:34:36 +00:00
Laurent Cozic
0ac710ecf9 Revert "Tools: Make settings in settings.json recommended only"
This reverts commit d3046582e1.

Reason: not how it works
2025-01-20 08:55:52 +00:00
Laurent Cozic
d3046582e1 Tools: Make settings in settings.json recommended only
Ref: https://discourse.joplinapp.org/t/vscode-settings-json-shouldnt-be-under-version-control/43184/6?u=laurent
2025-01-20 08:52:41 +00:00
qx100
9642640cda Translation: Update Simplified Chinese Translation (#11679) 2025-01-18 22:47:13 +00:00
Henry Heino
dab2438df0 Mobile: Resolves #10824: Allow adjusting the default note viewer font size (#11633) 2025-01-18 12:43:07 +00:00
Henry Heino
dc7871b655 Desktop,Mobile: Accessibility: Improve contrast of faded URLs in Markdown editor (#11635) 2025-01-18 12:42:19 +00:00
Henry Heino
ff465767ab Desktop: Accessibility: Make Markdown toolbar scrollable when low on space (#11636) 2025-01-18 12:41:56 +00:00
Henry Heino
c58aac9387 Desktop: Accessibility: Improve sidebar content contrast (#11638) 2025-01-18 12:41:45 +00:00
pedr
29e55b8231 Desktop: Fixes #11640: Pressing Shift+Tab when focus is on notebook list would jump straight to editor (#11641) 2025-01-18 12:41:18 +00:00
pedr
dc10ff6215 Desktop: Resolves #11644: Add proper type to search input (#11645) 2025-01-18 12:38:45 +00:00
Henry Heino
e8e3ef36ed Desktop: Accessibility: Improve sync wizard accessibility (#11649) 2025-01-18 12:38:23 +00:00
Henry Heino
e1b41cff5f Desktop: Accessibility: Mark secondary screen navigation bars as navigation regions (#11650) 2025-01-18 12:38:11 +00:00
Henry Heino
5782ee6ba1 Desktop: Upgrade to TinyMCE v6 (#11652) 2025-01-18 12:37:46 +00:00
cedecode
cbf81d1257 Translation: Update de_DE.po (#11653) 2025-01-18 12:37:11 +00:00
Henry Heino
c357b77a48 Desktop: Accessibility: Fix unlabelled toolbar button in the Rich Text Editor (#11655) 2025-01-18 12:37:03 +00:00
Arda Kılıçdağı
64c14fe76f Translation: Turkish translations updated (#11659) 2025-01-18 12:36:56 +00:00
Henry Heino
b2c1d7a2ba Desktop: Upgrade to Electron 34 (#11665) 2025-01-18 12:36:39 +00:00
ERYpTION
dbd4cffef3 Update da_DK.po (#11668) 2025-01-18 12:36:25 +00:00
renovate[bot]
d190463325 Update dependency @types/node to v18.19.57 (#11669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 12:36:15 +00:00
Henry Heino
2c1aa5d620 Desktop,Mobile: Allow internal links to target elements using the name attribute (#11671) 2025-01-18 12:36:09 +00:00
Henry Heino
52d255352a Chore: Upgrade Webpack to v5.97.1 (#11672) 2025-01-18 12:35:30 +00:00
Henry Heino
76274033db Android: Fixes #11292: Fix unable to sync with multiple Nextcloud accounts in different profiles (#11674) 2025-01-18 12:35:22 +00:00
Liffindra Angga Zaaldian
92abfac3af update Indonesian translation (#11677) 2025-01-18 12:35:04 +00:00
Laurent Cozic
c6956df1c9 Desktop: Resolves #11664: Double click to open a note in a new window 2025-01-17 12:16:00 +00:00
Joplin Bot
0bd1e202a2 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-16 18:22:47 +00:00
Laurent Cozic
f602ad8a63 Desktop: Resolves #11664: Reorganised note list context menu 2025-01-16 17:43:10 +00:00
Laurent Cozic
6a1b498e96 Merge branch 'release-3.2' into dev 2025-01-16 15:24:19 +00:00
Laurent Cozic
ca64451503 CLI v3.2.3 2025-01-16 15:23:18 +00:00
pedr
519f3f5898 Desktop, Mobile: Resolves #11647: Improve Welcome Notes with clearer instructions (#11656)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2025-01-16 15:22:46 +00:00
renovate[bot]
907b1e969e Update dependency @types/node to v18.19.56 (#11658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 01:12:59 +00:00
Laurent Cozic
216b750a90 Lock file 2025-01-16 01:10:48 +00:00
Laurent Cozic
219d5bcae3 Releasing sub-packages 2025-01-16 01:09:52 +00:00
Joplin Bot
b59774a763 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-15 18:22:33 +00:00
Laurent Cozic
0494719e4f Doc: Update sponsors 2025-01-15 13:28:19 +00:00
Joplin Bot
0e1b81685a Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-15 12:26:36 +00:00
Laurent Cozic
c157cd0cb3 Doc: Update sponsors 2025-01-15 11:57:41 +00:00
Joplin Bot
5c711df2e4 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-14 18:23:59 +00:00
Laurent Cozic
e520a695a6 Doc: Add news item for release 3.2 2025-01-14 15:45:04 +00:00
renovate[bot]
5ee8a9a454 Update dependency @react-native-community/slider to v4.5.4 (#11632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:35:49 +00:00
Joplin Bot
6b73879512 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-14 00:51:08 +00:00
Joplin Bot
f08235f05c Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-13 18:23:39 +00:00
Laurent Cozic
a4b1b9a2bf iOS 13.2.5 2025-01-13 17:19:33 +00:00
Laurent Cozic
fc8ea6df0b Android 3.2.7 2025-01-13 17:12:08 +00:00
Laurent Cozic
2fba101333 Desktop release v3.2.11 2025-01-13 16:34:20 +00:00
Henry Heino
35a0b22df2 Desktop: Accessibility: Add setting to increase scrollbar and other small control sizes (#11627) 2025-01-13 16:33:42 +00:00
Laurent Cozic
e177bffb1c Doc: Update sponsor ALT tag 2025-01-13 16:10:31 +00:00
Laurent Cozic
f95ca578c2 Doc: Trying to fix sponsor image 2025-01-13 16:05:35 +00:00
Laurent Cozic
4bed47a1af Update translations 2025-01-13 12:16:32 +00:00
Joplin Bot
5a0b0e6314 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-12 00:55:11 +00:00
Laurent Cozic
f119212068 Android 3.2.6 2025-01-11 22:03:42 +00:00
Liffindra Angga Zaaldian
cd12de78d6 update Indonesian translation (#11628) 2025-01-11 21:52:46 +00:00
Laurent Cozic
6aa2c5f116 Tools: Run yarn build when releasing Android APK 2025-01-11 21:48:48 +00:00
Joplin Bot
e287e5cbab Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-11 18:21:48 +00:00
Laurent Cozic
d70a5b25a0 Doc: Updated sponsors 2025-01-11 17:08:39 +00:00
Henry Heino
d2df7e6feb Desktop: Fixes #11624: Fix double-click to collapse notebooks (#11625) 2025-01-11 12:16:07 +00:00
Joplin Bot
e9ee8c8419 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-10 12:27:02 +00:00
Laurent Cozic
8d2ae7e20e Tools: Better handling of different platforms in git-changelog 2025-01-10 10:25:11 +00:00
Laurent Cozic
50d5843344 Desktop release v3.2.10 2025-01-10 00:54:33 +00:00
Laurent Cozic
1fdc327977 Merge branch 'release-3.2' into dev 2025-01-10 00:54:05 +00:00
Joplin Bot
c18ab5a7fb Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-10 00:52:39 +00:00
Laurent Cozic
11216902d0 Desktop release v3.2.9 2025-01-09 20:58:24 +00:00
Henry Heino
950ffef84d Windows: Resolves #11508: Allow installer to skip uninstallation step after repeated failures (#11612) 2025-01-09 20:57:47 +00:00
pedr
86e6445526 Desktop: Fixes #11617: Links from imported notes from OneNote were being wrongly rendered (#11618) 2025-01-09 20:57:23 +00:00
pedr
ab286b6da3 Desktop: Fixes #11600: OneNote imported notes have broken links when there are chineses characters on link (#11602) 2025-01-09 19:24:10 +00:00
ERYpTION
8c24928cf4 Update da_DK.po (#11601) 2025-01-09 15:29:03 +00:00
Henry Heino
3952060dac Chore: Retry flaky tests in Note.test.tsx (#11615) 2025-01-09 15:28:51 +00:00
Henry Heino
877f39bb0e Deskotp: Legacy Markdown Editor: Fix styles in seconary windows and remove red focus-visible border (#11614) 2025-01-09 15:28:45 +00:00
Henry Heino
652812a15c Desktop: Drawing: Fix "insert drawing" button is not disabled in read-only notes (Upgrade Freehand Drawing to v2.14.0) (#11613) 2025-01-09 15:28:40 +00:00
Henry Heino
597f3188bd Desktop: Fixes #11594: Fix syncLockGoneError on sync with certain share configs (#11611) 2025-01-09 15:28:24 +00:00
Henry Heino
d7d50f4373 Chore: Plugin repo CLI: Only match packages with the joplin-plugin keyword (#11599) 2025-01-09 15:27:41 +00:00
Self Not Found
83db585c0b Desktop: Resolves #11575: Remove "URI malformed" alert (#11576) 2025-01-09 15:26:20 +00:00
Maxim Medvedev
d817ddd5c6 Server: use node:18 (bookworm) instead node:18-bullseye (#11554) 2025-01-09 15:25:56 +00:00
Henry Heino
98fce34fe9 Web: Add support for auto-reloading dev plugins on change (#11545) 2025-01-09 15:25:06 +00:00
pedr
a81af0711c Desktop: Fixes #11597: OneNote Importer should only use text on fallback title (#11598) 2025-01-09 15:22:12 +00:00
Henry Heino
72575e3c6f Mobile: Fixes #11455: Clicking on an external note link from within a note logs an error (#11619) 2025-01-09 15:21:06 +00:00
pedr
e8f305dea5 CLI: Fixes #11577: Revert deprecation warning suppression (#11620) 2025-01-09 15:20:43 +00:00
Henry Heino
e1e2ba8888 Desktop: Fix keyboard can't add text after certain error/info dialogs are shown (#11603) 2025-01-08 12:30:16 +00:00
Henry Heino
633d87ebfe Android: Fix clicking "Draw picture" results in blank screen with very old WebView versions (#11604) 2025-01-08 12:29:47 +00:00
Joplin Bot
a9e1be944f Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-08 12:27:30 +00:00
Joplin Bot
6048f9613c Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-08 00:53:08 +00:00
Laurent Cozic
0a76494555 iOS 13.2.4 2025-01-08 00:12:25 +00:00
Laurent Cozic
edbb6137ea Revert "iOS: Resolves #11119: Add iOS Dark Icon (#11460)"
This reverts commit dc445579da.

Reason: Trying to fix error "Asset validation failed"
2025-01-08 00:11:06 +00:00
Laurent Cozic
4d216ef907 iOS 13.2.3 2025-01-07 23:52:48 +00:00
Laurent Cozic
2f71c40ceb lock file 2025-01-07 23:51:44 +00:00
Laurent Cozic
d3ea6fbe1d Android 3.2.5 2025-01-07 23:41:48 +00:00
Laurent Cozic
d45864888a Desktop release v3.2.8 2025-01-07 23:18:54 +00:00
Jozef Gaal
0e92ab654a All: Translation: Update sk_SK.po (#11593) 2025-01-06 18:46:19 -05:00
Joplin Bot
9e5c0ef3ce Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-01-06 18:22:50 +00:00
Nilakh(s)hya
431cc15a51 Doc: Update s3.md for provider Tebi (#11572)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-01-06 17:35:36 +00:00
Vladimir Fedorchuk
82118810d9 Documentation: Fixed two broken links in command-apidoc.ts (rest_api.md) (#11549) 2025-01-06 17:34:11 +00:00
Henry Heino
bacaf800f2 Android: Allow re-downloading voice typing models on URL change and error (#11557) 2025-01-06 17:33:44 +00:00
Henry Heino
4d827afccb Desktop: Fixes #11226: Fix reordering notes in custom sort order when some notes are deleted (#11592) 2025-01-06 17:33:31 +00:00
Henry Heino
e70efcbd60 Desktop: Security: Remove the name attribute when rendering to HTML (#11591) 2025-01-06 17:33:19 +00:00
Henry Heino
ac154ee1e8 Desktop: Fixes #11445: Link watched files to the current window (#11590) 2025-01-06 17:33:02 +00:00
Henry Heino
6220267abb Mobile: Upgrade js-draw to 1.26.0 (#11589) 2025-01-06 17:32:19 +00:00
1039 changed files with 200520 additions and 19166 deletions

View File

@@ -170,6 +170,7 @@ packages/app-desktop/commands/switchProfile2.js
packages/app-desktop/commands/switchProfile3.js
packages/app-desktop/commands/toggleExternalEditing.js
packages/app-desktop/commands/toggleSafeMode.js
packages/app-desktop/commands/toggleTabMovesFocus.js
packages/app-desktop/gui/Button/Button.js
packages/app-desktop/gui/ClipperConfigScreen.js
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
@@ -201,6 +202,7 @@ packages/app-desktop/gui/FolderIconBox.js
packages/app-desktop/gui/HelpButton.js
packages/app-desktop/gui/IconButton.js
packages/app-desktop/gui/ImportScreen.js
packages/app-desktop/gui/InlineCombobox.js
packages/app-desktop/gui/ItemList.js
packages/app-desktop/gui/JoplinCloudConfigScreen.js
packages/app-desktop/gui/JoplinCloudLoginScreen.js
@@ -249,13 +251,15 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useRefocusOnVis
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
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/enableTextAreaTab.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.js
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/useEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useEditDialogEventListeners.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useKeyboardRefocusHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useLinkTooltips.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
@@ -263,10 +267,12 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useTabIndenter.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/StatusBar.js
packages/app-desktop/gui/NoteEditor/WarningBanner/BannerContent.js
packages/app-desktop/gui/NoteEditor/WarningBanner/WarningBanner.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
packages/app-desktop/gui/NoteEditor/commands/focusElementToolbar.js
packages/app-desktop/gui/NoteEditor/commands/index.js
packages/app-desktop/gui/NoteEditor/commands/pasteAsText.js
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
@@ -289,12 +295,12 @@ packages/app-desktop/gui/NoteEditor/utils/useEffectiveNoteId.js
packages/app-desktop/gui/NoteEditor/utils/useFolder.js
packages/app-desktop/gui/NoteEditor/utils/useFormNote.test.js
packages/app-desktop/gui/NoteEditor/utils/useFormNote.js
packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.test.js
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.js
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
packages/app-desktop/gui/NoteEditor/utils/useResourceUnwatcher.js
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
@@ -312,6 +318,7 @@ packages/app-desktop/gui/NoteList/utils/useItemCss.js
packages/app-desktop/gui/NoteList/utils/useMoveNote.js
packages/app-desktop/gui/NoteList/utils/useOnKeyDown.js
packages/app-desktop/gui/NoteList/utils/useOnNoteClick.js
packages/app-desktop/gui/NoteList/utils/useOnNoteDoubleClick.js
packages/app-desktop/gui/NoteList/utils/useScroll.js
packages/app-desktop/gui/NoteList/utils/useVisibleRange.test.js
packages/app-desktop/gui/NoteList/utils/useVisibleRange.js
@@ -354,6 +361,7 @@ packages/app-desktop/gui/PasswordInput/types.js
packages/app-desktop/gui/PdfViewer.js
packages/app-desktop/gui/PluginNotification/PluginNotification.js
packages/app-desktop/gui/PromptDialog.js
packages/app-desktop/gui/ResizableLayout/LayoutItemContainer.js
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
@@ -428,6 +436,7 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/gotoAnything.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/hideModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/linkToNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/moveToFolder.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newFolder.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newNote.js
@@ -448,7 +457,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
@@ -457,7 +465,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareFolderDialog
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.test.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditors.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleMenuBar.js
@@ -479,6 +486,7 @@ packages/app-desktop/gui/hooks/useDocument.js
packages/app-desktop/gui/hooks/useEffectDebugger.js
packages/app-desktop/gui/hooks/useElementHeight.js
packages/app-desktop/gui/hooks/useImperativeHandlerDebugger.js
packages/app-desktop/gui/hooks/useMarkupToHtml.js
packages/app-desktop/gui/hooks/usePrevious.js
packages/app-desktop/gui/hooks/usePropsDebugger.js
packages/app-desktop/gui/lib/SearchInput/SearchInput.js
@@ -489,6 +497,7 @@ packages/app-desktop/gui/style/StyledInput.js
packages/app-desktop/gui/style/StyledLink.js
packages/app-desktop/gui/style/StyledMessage.js
packages/app-desktop/gui/style/StyledTextInput.js
packages/app-desktop/gui/utils/NoteListUtils.test.js
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/announceForAccessibility.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
@@ -498,6 +507,8 @@ packages/app-desktop/gulpfile.js
packages/app-desktop/integration-tests/goToAnything.spec.js
packages/app-desktop/integration-tests/main.spec.js
packages/app-desktop/integration-tests/markdownEditor.spec.js
packages/app-desktop/integration-tests/models/ChangeAppLayoutScreen.js
packages/app-desktop/integration-tests/models/EditorCodeDialog.js
packages/app-desktop/integration-tests/models/GoToAnything.js
packages/app-desktop/integration-tests/models/MainScreen.js
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
@@ -506,6 +517,7 @@ packages/app-desktop/integration-tests/models/SettingsScreen.js
packages/app-desktop/integration-tests/models/Sidebar.js
packages/app-desktop/integration-tests/noteList.spec.js
packages/app-desktop/integration-tests/pluginApi.spec.js
packages/app-desktop/integration-tests/resizableLayout.spec.js
packages/app-desktop/integration-tests/richTextEditor.spec.js
packages/app-desktop/integration-tests/settings.spec.js
packages/app-desktop/integration-tests/sidebar.spec.js
@@ -562,6 +574,7 @@ packages/app-desktop/utils/customProtocols/constants.js
packages/app-desktop/utils/customProtocols/handleCustomProtocols.test.js
packages/app-desktop/utils/customProtocols/handleCustomProtocols.js
packages/app-desktop/utils/customProtocols/registerCustomProtocols.js
packages/app-desktop/utils/initializeCommandService.js
packages/app-desktop/utils/isSafeToOpen.test.js
packages/app-desktop/utils/isSafeToOpen.js
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
@@ -606,6 +619,7 @@ packages/app-mobile/components/EditorToolbar/utils/allToolbarCommandNamesFromSta
packages/app-mobile/components/EditorToolbar/utils/isSelected.js
packages/app-mobile/components/EditorToolbar/utils/selectedCommandNamesFromState.js
packages/app-mobile/components/EditorToolbar/utils/toolbarButtonsFromState.js
packages/app-mobile/components/EditorToolbar/utils/useButtonSize.js
packages/app-mobile/components/ExtendedWebView/index.jest.js
packages/app-mobile/components/ExtendedWebView/index.js
packages/app-mobile/components/ExtendedWebView/index.web.js
@@ -640,10 +654,12 @@ packages/app-mobile/components/NoteEditor/ImageEditor/isEditableResource.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/applyTemplateToEditor.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.test.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/polyfills.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/startAutosaveLoop.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/types.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/watchEditorForTemplateChanges.js
packages/app-mobile/components/NoteEditor/ImageEditor/promptRestoreAutosave.js
packages/app-mobile/components/NoteEditor/ImageEditor/utils/useEditorMessenger.js
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
packages/app-mobile/components/NoteEditor/NoteEditor.js
packages/app-mobile/components/NoteEditor/SearchPanel.js
@@ -681,6 +697,7 @@ packages/app-mobile/components/buttons/index.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/plugins/PluginNotification.js
packages/app-mobile/components/plugins/PluginRunner.js
packages/app-mobile/components/plugins/PluginRunnerWebView.js
packages/app-mobile/components/plugins/backgroundPage/initializeDialogWebView.js
@@ -701,6 +718,7 @@ packages/app-mobile/components/plugins/dialogs/hooks/useViewInfos.js
packages/app-mobile/components/plugins/dialogs/hooks/useWebViewSetup.js
packages/app-mobile/components/plugins/types.js
packages/app-mobile/components/plugins/utils/createOnLogHandler.js
packages/app-mobile/components/plugins/utils/useOnDevPluginsUpdated.js
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
packages/app-mobile/components/screens/ConfigScreen/FileSystemPathSelector.js
packages/app-mobile/components/screens/ConfigScreen/JoplinCloudConfig.js
@@ -770,12 +788,16 @@ 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/dropbox-login.js
packages/app-mobile/components/screens/encryption-config.test.js
packages/app-mobile/components/screens/encryption-config.js
packages/app-mobile/components/screens/status.js
packages/app-mobile/components/screens/tags.js
packages/app-mobile/components/side-menu-content.js
packages/app-mobile/components/testing/TestProviderStack.js
packages/app-mobile/components/voiceTyping/VoiceTypingDialog.js
packages/app-mobile/components/voiceTyping/AudioRecordingBanner.js
packages/app-mobile/components/voiceTyping/RecordingControls.js
packages/app-mobile/components/voiceTyping/SpeechToTextBanner.js
packages/app-mobile/components/voiceTyping/types.js
packages/app-mobile/gulpfile.js
packages/app-mobile/index.web.js
packages/app-mobile/root.js
@@ -789,12 +811,11 @@ packages/app-mobile/services/e2ee/crypto.js
packages/app-mobile/services/plugins/PlatformImplementation.js
packages/app-mobile/services/profiles/index.js
packages/app-mobile/services/voiceTyping/VoiceTyping.js
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.test.js
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.js
packages/app-mobile/services/voiceTyping/utils/unzip.android.js
packages/app-mobile/services/voiceTyping/utils/unzip.js
packages/app-mobile/services/voiceTyping/vosk.android.js
packages/app-mobile/services/voiceTyping/vosk.js
packages/app-mobile/services/voiceTyping/whisper.test.js
packages/app-mobile/services/voiceTyping/whisper.js
packages/app-mobile/setupQuickActions.js
packages/app-mobile/tools/buildInjectedJs/BundledFile.js
@@ -853,6 +874,7 @@ packages/app-mobile/utils/testing/getWebViewWindowById.js
packages/app-mobile/utils/testing/setupGlobalStore.js
packages/app-mobile/utils/types.js
packages/app-mobile/web/serviceWorker.js
packages/app-mobile/web/webpack.config.js
packages/default-plugins/build.js
packages/default-plugins/buildDefaultPlugins.js
packages/default-plugins/commands/buildAll.js
@@ -879,6 +901,10 @@ packages/editor/CodeMirror/editorCommands/sortSelectedLines.test.js
packages/editor/CodeMirror/editorCommands/sortSelectedLines.js
packages/editor/CodeMirror/editorCommands/supportsCommand.js
packages/editor/CodeMirror/getScrollFraction.js
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.test.js
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.js
packages/editor/CodeMirror/markdown/MarkdownMathExtension.test.js
packages/editor/CodeMirror/markdown/MarkdownMathExtension.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/allLanguages.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/defaultLanguage.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/lookUpLanguage.js
@@ -890,8 +916,6 @@ packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js
packages/editor/CodeMirror/markdown/markdownCommands.test.js
packages/editor/CodeMirror/markdown/markdownCommands.toggleList.test.js
packages/editor/CodeMirror/markdown/markdownCommands.js
packages/editor/CodeMirror/markdown/markdownMathParser.test.js
packages/editor/CodeMirror/markdown/markdownMathParser.js
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.test.js
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.js
packages/editor/CodeMirror/markdown/utils/stripBlockquote.js
@@ -902,6 +926,7 @@ packages/editor/CodeMirror/pluginApi/customEditorCompletion.js
packages/editor/CodeMirror/testUtil/createEditorControl.js
packages/editor/CodeMirror/testUtil/createEditorSettings.js
packages/editor/CodeMirror/testUtil/createTestEditor.js
packages/editor/CodeMirror/testUtil/findNodesWithName.js
packages/editor/CodeMirror/testUtil/forceFullParse.js
packages/editor/CodeMirror/testUtil/loadLanguages.js
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
@@ -931,6 +956,7 @@ packages/editor/CodeMirror/utils/keyUpHandlerExtension.js
packages/editor/CodeMirror/utils/overwriteModeExtension.test.js
packages/editor/CodeMirror/utils/overwriteModeExtension.js
packages/editor/CodeMirror/utils/searchExtension.js
packages/editor/CodeMirror/utils/selectedNoteIdExtension.js
packages/editor/CodeMirror/utils/setupVim.js
packages/editor/SelectionFormatting.js
packages/editor/events.js
@@ -998,7 +1024,10 @@ packages/lib/commands/openMasterPasswordDialog.js
packages/lib/commands/permanentlyDeleteNote.js
packages/lib/commands/renderMarkup.test.js
packages/lib/commands/renderMarkup.js
packages/lib/commands/showEditorPlugin.js
packages/lib/commands/synchronize.js
packages/lib/commands/toggleAllFolders.js
packages/lib/commands/toggleEditorPlugin.js
packages/lib/components/EncryptionConfigScreen/utils.test.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
@@ -1092,6 +1121,9 @@ packages/lib/models/settings/builtInMetadata.js
packages/lib/models/settings/settingValidations.test.js
packages/lib/models/settings/settingValidations.js
packages/lib/models/settings/types.js
packages/lib/models/utils/areAllFoldersCollapsed.test.js
packages/lib/models/utils/areAllFoldersCollapsed.js
packages/lib/models/utils/getCanBeCollapsedFolderIds.js
packages/lib/models/utils/getCollator.js
packages/lib/models/utils/getConflictFolderId.js
packages/lib/models/utils/isItemId.js
@@ -1234,6 +1266,7 @@ packages/lib/services/ocr/utils/filterOcrText.js
packages/lib/services/ocr/utils/types.js
packages/lib/services/plugins/BasePlatformImplementation.js
packages/lib/services/plugins/BasePluginRunner.js
packages/lib/services/plugins/EditorPluginHandler.js
packages/lib/services/plugins/MenuController.js
packages/lib/services/plugins/MenuItemController.js
packages/lib/services/plugins/Plugin.js
@@ -1280,6 +1313,7 @@ packages/lib/services/plugins/utils/getPluginIssueReportUrl.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/getShownPluginEditorView.js
packages/lib/services/plugins/utils/isCompatible/getDefaultPlatforms.js
packages/lib/services/plugins/utils/isCompatible/index.test.js
packages/lib/services/plugins/utils/isCompatible/index.js

52
.gitignore vendored
View File

@@ -145,6 +145,7 @@ packages/app-desktop/commands/switchProfile2.js
packages/app-desktop/commands/switchProfile3.js
packages/app-desktop/commands/toggleExternalEditing.js
packages/app-desktop/commands/toggleSafeMode.js
packages/app-desktop/commands/toggleTabMovesFocus.js
packages/app-desktop/gui/Button/Button.js
packages/app-desktop/gui/ClipperConfigScreen.js
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
@@ -176,6 +177,7 @@ packages/app-desktop/gui/FolderIconBox.js
packages/app-desktop/gui/HelpButton.js
packages/app-desktop/gui/IconButton.js
packages/app-desktop/gui/ImportScreen.js
packages/app-desktop/gui/InlineCombobox.js
packages/app-desktop/gui/ItemList.js
packages/app-desktop/gui/JoplinCloudConfigScreen.js
packages/app-desktop/gui/JoplinCloudLoginScreen.js
@@ -224,13 +226,15 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useRefocusOnVis
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
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/enableTextAreaTab.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.test.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.js
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/useEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useEditDialogEventListeners.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useKeyboardRefocusHandler.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useLinkTooltips.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
@@ -238,10 +242,12 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useTabIndenter.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/StatusBar.js
packages/app-desktop/gui/NoteEditor/WarningBanner/BannerContent.js
packages/app-desktop/gui/NoteEditor/WarningBanner/WarningBanner.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
packages/app-desktop/gui/NoteEditor/commands/focusElementToolbar.js
packages/app-desktop/gui/NoteEditor/commands/index.js
packages/app-desktop/gui/NoteEditor/commands/pasteAsText.js
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
@@ -264,12 +270,12 @@ packages/app-desktop/gui/NoteEditor/utils/useEffectiveNoteId.js
packages/app-desktop/gui/NoteEditor/utils/useFolder.js
packages/app-desktop/gui/NoteEditor/utils/useFormNote.test.js
packages/app-desktop/gui/NoteEditor/utils/useFormNote.js
packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.test.js
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.js
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
packages/app-desktop/gui/NoteEditor/utils/useResourceUnwatcher.js
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
@@ -287,6 +293,7 @@ packages/app-desktop/gui/NoteList/utils/useItemCss.js
packages/app-desktop/gui/NoteList/utils/useMoveNote.js
packages/app-desktop/gui/NoteList/utils/useOnKeyDown.js
packages/app-desktop/gui/NoteList/utils/useOnNoteClick.js
packages/app-desktop/gui/NoteList/utils/useOnNoteDoubleClick.js
packages/app-desktop/gui/NoteList/utils/useScroll.js
packages/app-desktop/gui/NoteList/utils/useVisibleRange.test.js
packages/app-desktop/gui/NoteList/utils/useVisibleRange.js
@@ -329,6 +336,7 @@ packages/app-desktop/gui/PasswordInput/types.js
packages/app-desktop/gui/PdfViewer.js
packages/app-desktop/gui/PluginNotification/PluginNotification.js
packages/app-desktop/gui/PromptDialog.js
packages/app-desktop/gui/ResizableLayout/LayoutItemContainer.js
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
@@ -403,6 +411,7 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/gotoAnything.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/hideModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/linkToNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/moveToFolder.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newFolder.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newNote.js
@@ -423,7 +432,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
@@ -432,7 +440,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareFolderDialog
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.test.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditorPlugin.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditors.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleMenuBar.js
@@ -454,6 +461,7 @@ packages/app-desktop/gui/hooks/useDocument.js
packages/app-desktop/gui/hooks/useEffectDebugger.js
packages/app-desktop/gui/hooks/useElementHeight.js
packages/app-desktop/gui/hooks/useImperativeHandlerDebugger.js
packages/app-desktop/gui/hooks/useMarkupToHtml.js
packages/app-desktop/gui/hooks/usePrevious.js
packages/app-desktop/gui/hooks/usePropsDebugger.js
packages/app-desktop/gui/lib/SearchInput/SearchInput.js
@@ -464,6 +472,7 @@ packages/app-desktop/gui/style/StyledInput.js
packages/app-desktop/gui/style/StyledLink.js
packages/app-desktop/gui/style/StyledMessage.js
packages/app-desktop/gui/style/StyledTextInput.js
packages/app-desktop/gui/utils/NoteListUtils.test.js
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/announceForAccessibility.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
@@ -473,6 +482,8 @@ packages/app-desktop/gulpfile.js
packages/app-desktop/integration-tests/goToAnything.spec.js
packages/app-desktop/integration-tests/main.spec.js
packages/app-desktop/integration-tests/markdownEditor.spec.js
packages/app-desktop/integration-tests/models/ChangeAppLayoutScreen.js
packages/app-desktop/integration-tests/models/EditorCodeDialog.js
packages/app-desktop/integration-tests/models/GoToAnything.js
packages/app-desktop/integration-tests/models/MainScreen.js
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
@@ -481,6 +492,7 @@ packages/app-desktop/integration-tests/models/SettingsScreen.js
packages/app-desktop/integration-tests/models/Sidebar.js
packages/app-desktop/integration-tests/noteList.spec.js
packages/app-desktop/integration-tests/pluginApi.spec.js
packages/app-desktop/integration-tests/resizableLayout.spec.js
packages/app-desktop/integration-tests/richTextEditor.spec.js
packages/app-desktop/integration-tests/settings.spec.js
packages/app-desktop/integration-tests/sidebar.spec.js
@@ -537,6 +549,7 @@ packages/app-desktop/utils/customProtocols/constants.js
packages/app-desktop/utils/customProtocols/handleCustomProtocols.test.js
packages/app-desktop/utils/customProtocols/handleCustomProtocols.js
packages/app-desktop/utils/customProtocols/registerCustomProtocols.js
packages/app-desktop/utils/initializeCommandService.js
packages/app-desktop/utils/isSafeToOpen.test.js
packages/app-desktop/utils/isSafeToOpen.js
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
@@ -581,6 +594,7 @@ packages/app-mobile/components/EditorToolbar/utils/allToolbarCommandNamesFromSta
packages/app-mobile/components/EditorToolbar/utils/isSelected.js
packages/app-mobile/components/EditorToolbar/utils/selectedCommandNamesFromState.js
packages/app-mobile/components/EditorToolbar/utils/toolbarButtonsFromState.js
packages/app-mobile/components/EditorToolbar/utils/useButtonSize.js
packages/app-mobile/components/ExtendedWebView/index.jest.js
packages/app-mobile/components/ExtendedWebView/index.js
packages/app-mobile/components/ExtendedWebView/index.web.js
@@ -615,10 +629,12 @@ packages/app-mobile/components/NoteEditor/ImageEditor/isEditableResource.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/applyTemplateToEditor.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.test.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/polyfills.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/startAutosaveLoop.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/types.js
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/watchEditorForTemplateChanges.js
packages/app-mobile/components/NoteEditor/ImageEditor/promptRestoreAutosave.js
packages/app-mobile/components/NoteEditor/ImageEditor/utils/useEditorMessenger.js
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
packages/app-mobile/components/NoteEditor/NoteEditor.js
packages/app-mobile/components/NoteEditor/SearchPanel.js
@@ -656,6 +672,7 @@ packages/app-mobile/components/buttons/index.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/plugins/PluginNotification.js
packages/app-mobile/components/plugins/PluginRunner.js
packages/app-mobile/components/plugins/PluginRunnerWebView.js
packages/app-mobile/components/plugins/backgroundPage/initializeDialogWebView.js
@@ -676,6 +693,7 @@ packages/app-mobile/components/plugins/dialogs/hooks/useViewInfos.js
packages/app-mobile/components/plugins/dialogs/hooks/useWebViewSetup.js
packages/app-mobile/components/plugins/types.js
packages/app-mobile/components/plugins/utils/createOnLogHandler.js
packages/app-mobile/components/plugins/utils/useOnDevPluginsUpdated.js
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
packages/app-mobile/components/screens/ConfigScreen/FileSystemPathSelector.js
packages/app-mobile/components/screens/ConfigScreen/JoplinCloudConfig.js
@@ -745,12 +763,16 @@ 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/dropbox-login.js
packages/app-mobile/components/screens/encryption-config.test.js
packages/app-mobile/components/screens/encryption-config.js
packages/app-mobile/components/screens/status.js
packages/app-mobile/components/screens/tags.js
packages/app-mobile/components/side-menu-content.js
packages/app-mobile/components/testing/TestProviderStack.js
packages/app-mobile/components/voiceTyping/VoiceTypingDialog.js
packages/app-mobile/components/voiceTyping/AudioRecordingBanner.js
packages/app-mobile/components/voiceTyping/RecordingControls.js
packages/app-mobile/components/voiceTyping/SpeechToTextBanner.js
packages/app-mobile/components/voiceTyping/types.js
packages/app-mobile/gulpfile.js
packages/app-mobile/index.web.js
packages/app-mobile/root.js
@@ -764,12 +786,11 @@ packages/app-mobile/services/e2ee/crypto.js
packages/app-mobile/services/plugins/PlatformImplementation.js
packages/app-mobile/services/profiles/index.js
packages/app-mobile/services/voiceTyping/VoiceTyping.js
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.test.js
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.js
packages/app-mobile/services/voiceTyping/utils/unzip.android.js
packages/app-mobile/services/voiceTyping/utils/unzip.js
packages/app-mobile/services/voiceTyping/vosk.android.js
packages/app-mobile/services/voiceTyping/vosk.js
packages/app-mobile/services/voiceTyping/whisper.test.js
packages/app-mobile/services/voiceTyping/whisper.js
packages/app-mobile/setupQuickActions.js
packages/app-mobile/tools/buildInjectedJs/BundledFile.js
@@ -828,6 +849,7 @@ packages/app-mobile/utils/testing/getWebViewWindowById.js
packages/app-mobile/utils/testing/setupGlobalStore.js
packages/app-mobile/utils/types.js
packages/app-mobile/web/serviceWorker.js
packages/app-mobile/web/webpack.config.js
packages/default-plugins/build.js
packages/default-plugins/buildDefaultPlugins.js
packages/default-plugins/commands/buildAll.js
@@ -854,6 +876,10 @@ packages/editor/CodeMirror/editorCommands/sortSelectedLines.test.js
packages/editor/CodeMirror/editorCommands/sortSelectedLines.js
packages/editor/CodeMirror/editorCommands/supportsCommand.js
packages/editor/CodeMirror/getScrollFraction.js
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.test.js
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.js
packages/editor/CodeMirror/markdown/MarkdownMathExtension.test.js
packages/editor/CodeMirror/markdown/MarkdownMathExtension.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/allLanguages.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/defaultLanguage.js
packages/editor/CodeMirror/markdown/codeBlockLanguages/lookUpLanguage.js
@@ -865,8 +891,6 @@ packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js
packages/editor/CodeMirror/markdown/markdownCommands.test.js
packages/editor/CodeMirror/markdown/markdownCommands.toggleList.test.js
packages/editor/CodeMirror/markdown/markdownCommands.js
packages/editor/CodeMirror/markdown/markdownMathParser.test.js
packages/editor/CodeMirror/markdown/markdownMathParser.js
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.test.js
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.js
packages/editor/CodeMirror/markdown/utils/stripBlockquote.js
@@ -877,6 +901,7 @@ packages/editor/CodeMirror/pluginApi/customEditorCompletion.js
packages/editor/CodeMirror/testUtil/createEditorControl.js
packages/editor/CodeMirror/testUtil/createEditorSettings.js
packages/editor/CodeMirror/testUtil/createTestEditor.js
packages/editor/CodeMirror/testUtil/findNodesWithName.js
packages/editor/CodeMirror/testUtil/forceFullParse.js
packages/editor/CodeMirror/testUtil/loadLanguages.js
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
@@ -906,6 +931,7 @@ packages/editor/CodeMirror/utils/keyUpHandlerExtension.js
packages/editor/CodeMirror/utils/overwriteModeExtension.test.js
packages/editor/CodeMirror/utils/overwriteModeExtension.js
packages/editor/CodeMirror/utils/searchExtension.js
packages/editor/CodeMirror/utils/selectedNoteIdExtension.js
packages/editor/CodeMirror/utils/setupVim.js
packages/editor/SelectionFormatting.js
packages/editor/events.js
@@ -973,7 +999,10 @@ packages/lib/commands/openMasterPasswordDialog.js
packages/lib/commands/permanentlyDeleteNote.js
packages/lib/commands/renderMarkup.test.js
packages/lib/commands/renderMarkup.js
packages/lib/commands/showEditorPlugin.js
packages/lib/commands/synchronize.js
packages/lib/commands/toggleAllFolders.js
packages/lib/commands/toggleEditorPlugin.js
packages/lib/components/EncryptionConfigScreen/utils.test.js
packages/lib/components/EncryptionConfigScreen/utils.js
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
@@ -1067,6 +1096,9 @@ packages/lib/models/settings/builtInMetadata.js
packages/lib/models/settings/settingValidations.test.js
packages/lib/models/settings/settingValidations.js
packages/lib/models/settings/types.js
packages/lib/models/utils/areAllFoldersCollapsed.test.js
packages/lib/models/utils/areAllFoldersCollapsed.js
packages/lib/models/utils/getCanBeCollapsedFolderIds.js
packages/lib/models/utils/getCollator.js
packages/lib/models/utils/getConflictFolderId.js
packages/lib/models/utils/isItemId.js
@@ -1209,6 +1241,7 @@ packages/lib/services/ocr/utils/filterOcrText.js
packages/lib/services/ocr/utils/types.js
packages/lib/services/plugins/BasePlatformImplementation.js
packages/lib/services/plugins/BasePluginRunner.js
packages/lib/services/plugins/EditorPluginHandler.js
packages/lib/services/plugins/MenuController.js
packages/lib/services/plugins/MenuItemController.js
packages/lib/services/plugins/Plugin.js
@@ -1255,6 +1288,7 @@ packages/lib/services/plugins/utils/getPluginIssueReportUrl.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/getShownPluginEditorView.js
packages/lib/services/plugins/utils/isCompatible/getDefaultPlatforms.js
packages/lib/services/plugins/utils/isCompatible/index.test.js
packages/lib/services/plugins/utils/isCompatible/index.js

View File

@@ -1,7 +1,15 @@
# This patch prevents the installer from considering itself as a running instance of Joplin.
# This patch's goal is to work around an issue in the NSIS uninstaller on Windows:
# - For future uninstallers, this patch backports an upstream commit that changes how
# running copies of the app are found.
# - See https://github.com/electron-userland/electron-builder/pull/8133
# - If an existing uninstaller fails, gives an option to continue with the installation
# despite the failure.
# - Updates "uninstall failed" error messages to state that uninstallation failed (rather
# than incorrectly stating that the issue was with closing the app).
#
# See https://github.com/laurent22/joplin/pull/11541
diff --git a/templates/nsis/include/allowOnlyOneInstallerInstance.nsh b/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
index fe5d45c730f36c9fe8d8cfea12e242e501b67139..af2ce5c90ac910b079e24992519bffe33d57668a 100644
index fe5d45c730f36c9fe8d8cfea12e242e501b67139..97b27fce6798e30e3e631221435f09b3579e77c3 100644
--- a/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
+++ b/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
@@ -42,7 +42,7 @@
@@ -9,7 +17,74 @@ index fe5d45c730f36c9fe8d8cfea12e242e501b67139..af2ce5c90ac910b079e24992519bffe3
!else
# find process owned by current user
- nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" /FO csv | %SYSTEMROOT%\System32\find.exe "${_FILE}"`
+ nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c tasklist /FI "USERNAME eq %USERNAME%" /FI "PID ne $pid" /FI "IMAGENAME eq ${_FILE}" /FO csv | %SYSTEMROOT%\System32\find.exe "${_FILE}"`
+ nsExec::Exec `"$SYSDIR\cmd.exe" /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" /FO csv | "$SYSDIR\find.exe" "${_FILE}"`
Pop ${_ERR}
!endif
!macroend
@@ -73,7 +73,7 @@
!ifdef INSTALL_MODE_PER_ALL_USERS
nsExec::Exec `taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid"`
!else
- nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
+ nsExec::Exec `"$SYSDIR\cmd.exe" /c taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
!endif
# to ensure that files are not "in-use"
Sleep 300
@@ -91,7 +91,7 @@
!ifdef INSTALL_MODE_PER_ALL_USERS
nsExec::Exec `taskkill /f /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid"`
!else
- nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c taskkill /f /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
+ nsExec::Exec `"$SYSDIR\cmd.exe" /c taskkill /f /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
!endif
!insertmacro FIND_PROCESS "${APP_EXECUTABLE_FILENAME}" $R0
${If} $R0 == 0
diff --git a/templates/nsis/include/installUtil.nsh b/templates/nsis/include/installUtil.nsh
index 47367741632726ba0886ac516461dbe98b7aea58..675965762375925a505ca6d8bbb67507ef696c2e 100644
--- a/templates/nsis/include/installUtil.nsh
+++ b/templates/nsis/include/installUtil.nsh
@@ -126,10 +126,11 @@ Function handleUninstallResult
Return
${if} $R0 != 0
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(uninstallFailed): $R0"
+ # MessageBox MB_OK|MB_ICONEXCLAMATION "$(uninstallFailed): $R0"
DetailPrint `Uninstall was not successful. Uninstaller error code: $R0.`
- SetErrorLevel 2
- Quit
+ DetailPrint `Continuing anyway. See https://github.com/laurent22/joplin/pull/11612.`
+ # SetErrorLevel 2
+ # Quit
${endif}
FunctionEnd
@@ -216,11 +217,13 @@ Function uninstallOldVersion
IntOp $R5 $R5 + 1
${if} $R5 > 5
- MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeClosed)" /SD IDCANCEL IDRETRY OneMoreAttempt
- Return
+ MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeUninstalled)" /SD IDCANCEL IDRETRY ContinueWithoutUninstall
+ Abort ; Exit early
+ ContinueWithoutUninstall:
+ Return
${endIf}
- OneMoreAttempt:
+# OneMoreAttempt: ; Commented out because unused
ExecWait '"$uninstallerFileNameTemp" /S /KEEP_APP_DATA $0 _?=$installationDir' $R0
ifErrors TryInPlace CheckResult
diff --git a/templates/nsis/messages.yml b/templates/nsis/messages.yml
index a1c2847fa48d79f835b30b48e999ccaf3c818657..6884c18d1e77dbd6be114401d23cf5caf3e0dd94 100644
--- a/templates/nsis/messages.yml
+++ b/templates/nsis/messages.yml
@@ -235,3 +235,8 @@ uninstallFailed:
sv: Det gick inte att avinstallera gamla programfiler. Försök att köra installationsprogrammet igen.
uk: Не вдалось видалити старі файли застосунку. Будь ласка, спробуйте запустити встановлювач знов.
zh_TW: 無法俺安裝舊的應用程式檔案。 請嘗試再次執行安裝程式。
+
+
+appCannotBeUninstalled:
+ en: "The old version of ${PRODUCT_NAME} could not be removed. \nClick Retry to skip this step."
+

View File

@@ -0,0 +1,50 @@
# This is a (hopefully temporary) fix for an accessibility issue in the FAB.Group
# component. See https://github.com/callstack/react-native-paper/pull/4498 for details.
diff --git a/lib/commonjs/components/FAB/FABGroup.js b/lib/commonjs/components/FAB/FABGroup.js
index 26933dd7ac6862c0dd95e52b8cd91c8bbd0b6efc..417c91a0257849eb597afb5e339e13b6d1d54486 100644
--- a/lib/commonjs/components/FAB/FABGroup.js
+++ b/lib/commonjs/components/FAB/FABGroup.js
@@ -209,8 +209,9 @@ const FABGroup = _ref => {
}],
pointerEvents: open ? 'box-none' : 'none',
accessibilityRole: "button",
- importantForAccessibility: "yes",
- accessible: true,
+ importantForAccessibility: open ? 'yes' : 'no-hide-descendants',
+ accessibilityElementsHidden: !open,
+ accessible: open,
accessibilityLabel: accessibilityLabel
}, it.label && /*#__PURE__*/React.createElement(_reactNative.View, null, /*#__PURE__*/React.createElement(_Card.default, {
mode: isV3 ? 'contained' : 'elevated',
diff --git a/lib/module/components/FAB/FABGroup.js b/lib/module/components/FAB/FABGroup.js
index ca5c02679539b17b048d4c82f570791dd8b57545..a06902b744b3bfb06b0644930eda0ba2ce2967ca 100644
--- a/lib/module/components/FAB/FABGroup.js
+++ b/lib/module/components/FAB/FABGroup.js
@@ -200,8 +200,9 @@ const FABGroup = _ref => {
}],
pointerEvents: open ? 'box-none' : 'none',
accessibilityRole: "button",
- importantForAccessibility: "yes",
- accessible: true,
+ importantForAccessibility: open ? 'yes' : 'no-hide-descendants',
+ accessibilityElementsHidden: !open,
+ accessible: open,
accessibilityLabel: accessibilityLabel
}, it.label && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Card, {
mode: isV3 ? 'contained' : 'elevated',
diff --git a/src/components/FAB/FABGroup.tsx b/src/components/FAB/FABGroup.tsx
index af1e85c4cbabfdd05499f9befb9f851be5911835..d010393975b0b31852efba1b7ce9cb09da4feaec 100644
--- a/src/components/FAB/FABGroup.tsx
+++ b/src/components/FAB/FABGroup.tsx
@@ -383,8 +383,9 @@ const FABGroup = ({
]}
pointerEvents={open ? 'box-none' : 'none'}
accessibilityRole="button"
- importantForAccessibility="yes"
- accessible={true}
+ importantForAccessibility={open ? 'yes' : 'no-hide-descendants'}
+ accessibilityElementsHidden={!open}
+ accessible={open}
accessibilityLabel={accessibilityLabel}
>
{it.label && (

View File

@@ -0,0 +1,37 @@
diff --git a/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java b/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
index 4f2391b..f7cc433 100644
--- a/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
+++ b/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
@@ -8,11 +8,14 @@
package org.pgsqlite;
import android.annotation.SuppressLint;
+import android.database.AbstractWindowedCursor;
import android.database.Cursor;
+import android.database.CursorWindow;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.content.Context;
+import android.os.Build;
import android.util.Base64;
import java.io.Closeable;
@@ -808,6 +811,17 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {
throw ex;
}
+ // To try to fix the error "Row too big to fit into CursorWindow" when using sqlite binary bundled with the device
+ // https://github.com/andpor/react-native-sqlite-storage/issues/364#issuecomment-526423153
+ // https://github.com/laurent22/joplin/issues/1767#issuecomment-515617991
+
+ if (cur != null && Build.VERSION.SDK_INT >= 28) {
+ CursorWindow cw = new CursorWindow(null, 50 * 1024 * 1024);
+ AbstractWindowedCursor ac = (AbstractWindowedCursor) cur;
+ ac.setWindow(cw);
+ cur = ac;
+ }
+
// If query result has rows
if (cur != null && cur.moveToFirst()) {
WritableArray rowsArrayResult = Arguments.createArray();

View File

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 682.66669 682.66669"
height="682.66669"
width="682.66669"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="JoplinLetterBlue.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview13"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:zoom="0.77490232"
inkscape:cx="366.49781"
inkscape:cy="360.69062"
inkscape:window-width="1366"
inkscape:window-height="708"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<defs
id="defs6">
<linearGradient
id="linearGradient26"
spreadMethod="pad"
gradientTransform="matrix(-4387.91,4387.91,4387.91,4387.91,4753.95,366.05)"
gradientUnits="userSpaceOnUse"
y2="0"
x2="1"
y1="0"
x1="0">
<stop
id="stop22"
offset="0"
style="stop-opacity:1;stop-color:#004caf" />
<stop
id="stop24"
offset="1"
style="stop-opacity:1;stop-color:#1f95f8" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath829"><path
id="path831"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.999997"
d="M 3961.59,4435.23 H 2570.18 c -13.15,0 -23.78,-10.64 -23.78,-23.77 v -441.84 c 0,-14.87 12.04,-26.92 26.92,-26.92 h 190.77 c 77.16,0 139.73,-59.35 146.43,-134.77 V 3505 3336.23 1728.75 1717.36 h -0.052 c 0.48,-16.84 -0.1898,-33.4 -1.83,-49.71 -0.18,-2.38 -0.5003,-4.73 -0.7902,-7.09 -1.0998,-9.53 -2.3199,-19.01 -4.17,-28.29 -1.0098,-5.29 -2.4399,-10.44 -3.7098,-15.65 -1.71,-6.93 -3.09,-13.97 -5.22,-20.75 -12.5802,-40.27 -32.4702,-77.62 -59.9802,-110.5 -1.0098,-1.17 -2.2599,-2.25 -3.2598,-3.41 -8.3901,-9.72 -17.2002,-19.19 -26.9502,-28.06 -9.84,-8.95 -20.2599,-17.27 -31.2099,-25 -77.8401,-55.14 -182.61,-79.4 -299.67,-68.2 -149.2599,14.03 -297.3399,81.72 -417.03,190.62 -119.6701,108.89 -194.08,243.62 -209.4799,379.41 -13.8501,121.48 22.5498,228.38 102.42,301.05 0.21,0.1598 0.3997,0.3098 0.5602,0.48 3.09,2.77 6.4901,5.2 9.6701,7.87 57.16,47.89 131.6701,76.91 216.7,84.91 0.96,0.09 1.8801,0.24 2.79,0.3203 8.9499,0.79 18.0699,1.15 27.27,1.49 4.8099,0.1598 9.5601,0.5003 14.4399,0.54 1.62,0.023 3.1602,0.1898 4.7802,0.1898 2.8998,0 5.91,-0.3803 8.8098,-0.42 13.4001,-0.21 26.9001,-0.7601 40.6701,-1.9401 1.74,-0.1402 3.3999,-0.08 5.19,-0.24 1.2699,-0.1297 2.5299,-0.4102 3.8001,-0.54 78,-7.82 155.2299,-31.11 228.5199,-66.3999 1.53,-0.068 3.3,-0.54 5.5099,-1.7601 22.34,-12.3399 26.6201,0.9 27.2801,9.6501 v 382.2399 282.8201 c 0,19.05 -13.2501,35.8999 -31.83,39.99 -394.7601,86.88 -782.08,-3.5501 -1055.38,-252.3401 -238.7499,-217.1799 -354.24,-530.5799 -316.8201,-859.7899 33.39,-293.23 183.9102,-574.94 423.88,-793.33 233.8901,-212.79003 531.69,-345.86006 838.8801,-374.80106 42.33,-3.918 84.8601,-5.93797 126.36,-5.93797 293.3799,0 565.6099,100.59802 766.54,283.37903 190.3401,173.3 304.35,411.27 321.0799,670.16 l 1.55,1697.91 h 0.1703 v 453.97 h 0.06 v 7.92 c 1.72,80.1199 67.05,144.58 147.61,144.58 h 190.77 c 14.8599,0 26.9199,12.05 26.9199,26.9199 v 441.84 c 0,13.13 -10.6299,23.77 -23.7799,23.77" /></clipPath></defs>
<g
id="g14"
transform="matrix(0.13333333,0,0,-0.13333333,0,682.66667)"
mask="none"
clip-path="url(#clipPath829)">
<g
clip-path="url(#clipPath20)"
id="g16">
<path
id="path28"
style="fill:url(#linearGradient26);fill-opacity:1;fill-rule:nonzero;stroke:none"
d="M 3873.89,0 H 1246.11 C 560.754,0 0,560.75 0,1246.11 V 3873.88 C 0,4559.25 560.754,5120 1246.11,5120 H 3873.89 C 4559.25,5120 5120,4559.25 5120,3873.88 V 1246.11 C 5120,560.75 4559.25,0 3873.89,0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -8,7 +8,6 @@
import { Node } from '@ephox/dom-globals';
import { Arr, Option } from '@ephox/katamari';
import { HTMLElement } from '@ephox/sand';
import DomQuery from 'tinymce/core/api/dom/DomQuery';
import Editor from 'tinymce/core/api/Editor';
import Tools from 'tinymce/core/api/util/Tools';
import * as NodeType from './NodeType';
@@ -49,7 +48,7 @@ const findParentListItemsNodes = function (editor, elms) {
return parentLi ? parentLi : elm;
});
return DomQuery.unique(listItemsElms);
return [...new Set(listItemsElms)];
};
const getSelectedListItems = function (editor) {
@@ -89,7 +88,7 @@ const getSelectedListRoots = (editor: Editor): Node[] => {
const getUniqueListRoots = (editor: Editor, lists: Node[]): Node[] => {
const listRoots = Arr.map(lists, (list) => findLastParentListNode(editor, list).getOr(list));
return DomQuery.unique(listRoots);
return [...new Set(listRoots)];
};
const isList = (editor: Editor): boolean => {

View File

@@ -48,8 +48,7 @@ const listState = function (editor: Editor, listName, options:any = {}) {
const register = function (editor: Editor) {
const hasPlugin = function (editor, plugin) {
const plugins = editor.settings.plugins ? editor.settings.plugins : '';
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
return editor.hasPlugin(plugin);
};
const _ = Settings.getLocalizationFunction(editor);

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -1,4 +1,30 @@
<?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>Tue, 17 Dec 2024 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Tue, 17 Dec 2024 00:00:00 GMT</pubDate><item><title><![CDATA[Project 4: Handwritten Text Recognition (HTR) for Joplin]]></title><description><![CDATA[<p>Joplin is partnering with a French government institution to bring you innovative new features! We will work on accessibility, voice typing, HTR and add Rocketbook integration. Today we'll present the planned HTR integration:</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>Tue, 14 Jan 2025 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 3.2]]></title><description><![CDATA[<h2>Import OneNote Archives<a name="import-onenote-archives" href="#import-onenote-archives" class="heading-anchor">🔗</a></h2>
<p>Joplin now supports importing OneNote archives, a significant step for users transitioning from OneNote. Microsoft has long made it challenging to leave OneNote, offering limited export options and complex formats that make it difficult for app developers to support it. Despite these hurdles, @pedr tackled these issues head-on, developing an import tool that simplifies the process. This addition makes Joplin a practical choice for those looking to move away from OneNote's ecosystem.</p>
<p>To use this feature, select <strong>File</strong> =&gt; <strong>Import</strong> =&gt; <strong>ZIP - OneNote Notebook</strong></p>
<h2>Multi-window support<a name="multi-window-support" href="#multi-window-support" class="heading-anchor">🔗</a></h2>
<p>We're excited to introduce Multi-Window Support, a highly requested feature that makes managing multiple notes easier than ever. With this update, you can open notes in different windows and each window operates independently, allowing you to compare notes, reference content, and organise projects with greater flexibility.</p>
<p>To use this feature, right-click on a note, select <strong>Open in...</strong> and select <strong>Edit in new window</strong></p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250114-multi-window.png" alt=""></p>
<h2>Customisable toolbar on mobile<a name="customisable-toolbar-on-mobile" href="#customisable-toolbar-on-mobile" class="heading-anchor">🔗</a></h2>
<p>The new customisable toolbar on mobile is now draggable, making it easier to access the buttons you need. You can also choose which buttons to display by tapping the Cog button, allowing for a more personalised and efficient note-editing experience!</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250114-mobile-toolbar.png" alt=""></p>
<h2>Enhanced Accessibility<a name="enhanced-accessibility" href="#enhanced-accessibility" class="heading-anchor">🔗</a></h2>
<p>In order to implement the <a href="https://www.w3.org/TR/WCAG20/">WCAG 2.0 guidelines</a>, accessibility has seen a substantial upgrade thanks to the efforts of @personalizedrefrigerator. The desktop and mobile apps now offer better keyboard navigation, including improved functionality in dropdown menus and settings. Focus indicators have been made more visible, while ARIA labels have been added to boost compatibility with screen readers. Specific areas like note attachments, sidebars, and dialogs have also been optimised to ensure accessibility for all users.</p>
<h2>Refined Drawing and Markdown Editing<a name="refined-drawing-and-markdown-editing" href="#refined-drawing-and-markdown-editing" class="heading-anchor">🔗</a></h2>
<p>Joplin's drawing and editing features have been fine-tuned for a smoother experience. Freehand Drawing on mobile and desktop has been updated to version 2.14.0, addressing several usability issues and bugs. Additionally, the Freehand Drawing plugin is now part of the desktop app by default. These changes enhance the reliability and integration of the drawing tool.</p>
<h2>Faster and more secure encryption<a name="faster-and-more-secure-encryption" href="#faster-and-more-secure-encryption" class="heading-anchor">🔗</a></h2>
<p>For GSoC 2024, @wh201906 worked on optimising the encryption and decryption processes, boosting speed for mobile devices in particular (but desktop too). Additionally, the encryption security was improved by transitioning to a more robust 256-bit key.</p>
<p>As of now this feature is optional and can be enabled by going to the <strong>Configuration Screen</strong>, then <strong>Synchronisation</strong> =&gt; <strong>Advanced options</strong> =&gt; <strong>Use beta encryption</strong>.</p>
<h2>Expanded Plugin Support<a name="expanded-plugin-support" href="#expanded-plugin-support" class="heading-anchor">🔗</a></h2>
<p>Developers will appreciate the updates to Joplin's plugin ecosystem. A new API has been introduced to create <a href="https://joplinapp.org/api/references/plugin_api/classes/joplinviewsdialogs.html#showtoast">toast notifications</a>, alongside updates to CodeMirror packages. A new <a href="https://github.com/laurent22/joplin/blob/5ee8a9a45493683c72a36b52e1460b5acdd4f1ac/packages/lib/commands/renderMarkup.ts#L23"><code>renderMarkup</code></a> command has been introduced to allow you to render Markdown content to HTML using the Joplin built-in API.</p>
<h1>Full changelogs<a name="full-changelogs" href="#full-changelogs" class="heading-anchor">🔗</a></h1>
<p>This is just an overview of the main features. The full changelogs are 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/20250114-release-3-2</link><guid isPermaLink="false">20250114-release-3-2</guid><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><twitter-text>What&apos;s new in Joplin 3.2</twitter-text></item><item><title><![CDATA[Project 4: Handwritten Text Recognition (HTR) for Joplin]]></title><description><![CDATA[<p>Joplin is partnering with a French government institution to bring you innovative new features! We will work on accessibility, voice typing, HTR and add Rocketbook integration. Today we'll present the planned HTR integration:</p>
<p>Currently, Joplin’s OCR (Optical Character Recognition) feature is designed to recognise printed text, which works great for scanning documents with standard fonts. However, we’re looking to expand this functionality to support handwritten text recognition (HTR), which would be beneficial to handle scanned, handwritten documents, as well as for the upcoming Rocketbook integration.</p>
<p>Handwritten text recognition is complex task, requiring significant processing power and large machine learning models. Because of this, we plan to implement HTR via a server, possibly integrated with Joplin Cloud or Joplin Server. The beauty of this approach is that handwritten text recognition is a rapidly evolving field, so we can continuously improve the server-side model. This means that every Joplin app can benefit from these updates without needing to redeploy or update the app itself.</p>
<p>For the Rocketbook integration, this integration will make a significant difference. Right now, your handwritten documents would be scanned as images, but with HTR, Joplin will be able to recognise the actual text you’ve written. Not only will your handwritten notes become searchable, but you’ll also be able to copy and paste the text into other documents.</p>
@@ -393,15 +419,4 @@ sys 0m38.013s</p>
<p>Renovate on the other hand upgrades packages one at a time, and run our test units to ensure everything is still working as expected. It also upgrades multiple instances of the same package across the monorepo, which is convenient to keep our code consistent. It also has a number of options to make our life easier, such as the ability to automatically merge a pull request for patch releases since this is usually safe (when a package is, for example upgraded from 1.0.1 to 1.0.3).</p>
<p>Although Renovate automates the package upgrades it doesn't mean all upgrades are straightforward - our tests won't catch all issues, so the apps might end up being broken or cannot be compiled anymore. So there's manual work involved to get everything working after certain upgrades - for the most part this has been done and the apps appear to be stable so far.</p>
<p>This will however be an important part of pre-release 2.10 (or should it be 3.0?) - we hope that everything works but we may need your support to try this version and report any glitch you may have found. As always pre-release regressions have the highest priority so we aim to fix them as quickly as possible.</p>
]]></description><link>https://joplinapp.org/news/20221115-renovate</link><guid isPermaLink="false">20221115-renovate</guid><pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate><twitter-text>Modernising and securing Joplin, one package at a time</twitter-text></item><item><title><![CDATA[Joplin Cloud is now part of the Joplin company]]></title><description><![CDATA[<p>As some of you may know Joplin Cloud so far has been operating under my own single-person limited company in the UK. This was mostly for convenience since it meant I could get things going quickly without having to setup a special structure for it.</p>
<p>Now that Joplin Cloud is becoming more mature however a proper company, simply called Joplin, has been created. This company will be based in France, and will be used mainly to handle the commercial part of the project, which currently is mostly Joplin Cloud. I'm still heading the company so there won't be any major change to the way the project is managed.</p>
<h2>What does it mean for Joplin Cloud?<a name="what-does-it-mean-for-joplin-cloud" href="#what-does-it-mean-for-joplin-cloud" class="heading-anchor">🔗</a></h2>
<p>There will be no significant change - the website ownership simply moves from one company in the UK to one in France. The new company is still owned by myself so I will keep following the same roadmap.</p>
<h2>What does it mean for the open source apps?<a name="what-does-it-mean-for-the-open-source-apps" href="#what-does-it-mean-for-the-open-source-apps" class="heading-anchor">🔗</a></h2>
<p>On the short term, the only visible change will be moving the non-open source assets, such as logo or trademark from the UK company to the French one. So expect a few changes in copyright notices here and there.</p>
<p>In the medium to long term, I would like to hire one or two software developers to help me with the Joplin Cloud development, because we reached a point where managing the whole project is difficult for a single person, so some help is needed. Some of their work might also touch the open source apps since both are quite related - but of course that work will remain open source too.</p>
<p>As a general rule, there will be a permanent commitment to keep the apps open source and to derive value from Joplin Cloud/Server.</p>
<p>Longer term I would like to create a non-profit organisation to handle the open source applications and to make decisions about the project, as well as to decide how to allocate any funding we receive (for example from GSoC).</p>
<h2>Looking forward<a name="looking-forward" href="#looking-forward" class="heading-anchor">🔗</a></h2>
<p>Those past 6 years of developing Joplin have been an exciting and rewarding experience, thank you to all of you of the friendly and vibrant Joplin community for your contribution toward making Joplin the software it is today, and looking forward to continuing the journey together!</p>
]]></description><link>https://joplinapp.org/news/20221012-Joplin-Company</link><guid isPermaLink="false">20221012-Joplin-Company</guid><pubDate>Wed, 12 Oct 2022 00:00:00 GMT</pubDate><twitter-text>Joplin Cloud is now operated by the Joplin company! More info on the announcement post.</twitter-text></item></channel></rss>
]]></description><link>https://joplinapp.org/news/20221115-renovate</link><guid isPermaLink="false">20221115-renovate</guid><pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate><twitter-text>Modernising and securing Joplin, one package at a time</twitter-text></item></channel></rss>

View File

@@ -1,7 +1,6 @@
<div class="row">
<div class="col-12 col-md-12 social-links">
<a class="social-link-bluesky" href="https://bsky.app/profile/joplinapp.bsky.social" title="Joplin Bluesky feed"><i class="fa-brands fa-bluesky"></i></a>
<a class="social-link-twitter" href="https://twitter.com/joplinapp" title="Joplin Twitter feed"><i class="fab fa-twitter"></i></a>
<a class="social-link-mastodon" href="https://mastodon.social/@joplinapp" title="Joplin Mastodon feed"><i class="fab fa-mastodon"></i></a>
<a class="social-link-patreon" href="https://www.patreon.com/joplin" title="Joplin Patreon"><i class="fab fa-patreon"></i></a>
<a class="social-link-discord" href="https://discord.gg/VSj7AFHvpq" title="Joplin Discord chat"><i class="fab fa-discord"></i></a>

View File

@@ -1,3 +1 @@
<!-- <a href="https://twitter.com/joplinapp" title="Joplin Twitter feed" class="fw500 twitter-link"><i class="fab fa-twitter"></i></a> -->
<a href="https://bsky.app/profile/joplinapp.bsky.social" title="Joplin Bluesky feed" class="fw500 twitter-link"><i class="fa-brands fa-bluesky"></i></a>

View File

@@ -2,11 +2,11 @@
# Build stage
# =============================================================================
FROM node:18-bullseye AS builder
FROM node:18 AS builder
RUN apt-get update \
&& apt-get install -y \
python tini \
python3 tini \
&& rm -rf /var/lib/apt/lists/*
# Enables Yarn
@@ -56,7 +56,7 @@ RUN BUILD_SEQUENCIAL=1 yarn install --inline-builds \
# from a smaller base image.
# =============================================================================
FROM node:18-bullseye-slim
FROM node:18-slim
ARG user=joplin
RUN useradd --create-home --shell /bin/bash $user

View File

@@ -31,7 +31,7 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
# Sponsors
<!-- SPONSORS-ORG -->
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-webseite&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://buyyoutubviews.com"><img title="BYTV" width="256" src="https://joplinapp.org/images/sponsors/BYTV.png"/></a> <a href="https://casinoreviews.net"><img title="Casino Reviews" width="256" src="https://joplinapp.org/images/sponsors/CasinoReviews.png"/></a> <a href="https://useviral.com.br/"><img title="Comprar seguidores Instagram" width="256" src="https://joplinapp.org/images/sponsors/Useviral.png"/></a> <a href="https://ca.edubirdie.com/"><img title="Achieve academic success with Edubirdie — your trusted partner for expert writing assistance and resources!" width="256" src="https://joplinapp.org/images/sponsors/Edubirdie.png" alt="EduBirdie"/></a>
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-webseite&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://casinoreviews.net"><img title="Casino Reviews" width="256" src="https://joplinapp.org/images/sponsors/CasinoReviews.png"/></a> <a href="https://useviral.com.br/"><img title="Comprar seguidores Instagram" width="256" src="https://joplinapp.org/images/sponsors/Useviral.png"/></a> <a href="https://ca.edubirdie.com/"><img title="Achieve academic success with Edubirdie — your trusted partner for expert writing assistance and resources!" width="256" src="https://joplinapp.org/images/sponsors/Edubirdie.png" alt="EduBirdie"/></a> <a href="https://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="web design agency"/></a> <a href="https://realgambling.ca/"><img title="RealGambling.ca" width="256" src="https://joplinapp.org/images/sponsors/RealGambling.png" alt="RealGambling.ca"/></a> <a href="https://essaypro.com/"><img title="write an essay online with EssayPro" width="256" src="https://joplinapp.org/images/sponsors/EssayPro.png" alt="write an essay online with EssayPro"/></a> <a href="https://www.slotozilla.com/nz/no-deposit-bonus"><img title="casino without making any upfront cost" width="256" src="https://joplinapp.org/images/sponsors/Slotozilla.png" alt="casino without making any upfront cost"/></a>
<!-- SPONSORS-ORG -->
* * *
@@ -40,8 +40,8 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
| | | | |
| :---: | :---: | :---: | :---: |
| <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/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/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) | <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/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/1177810?s=96&v=4"/></br>[felixstorm](https://github.com/felixstorm) | <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/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) |
| <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/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <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) |
| | | | |
<!-- SPONSORS-GITHUB -->
@@ -51,7 +51,6 @@ Name | Description
--- | ---
[Support Forum](https://discourse.joplinapp.org/) | This is the main place for general discussion about Joplin, user support, software development questions, and to discuss new features. Also where the latest beta versions are released and discussed.
[Bluesky feed](https://bsky.app/profile/joplinapp.bsky.social) | Follow us on Bluesky
[Twitter feed](https://twitter.com/joplinapp) | Follow us on Twitter
[Mastodon feed](https://mastodon.social/@joplinapp) | Follow us on Mastodon
[Patreon page](https://www.patreon.com/joplin) |The latest news are often posted there
[Discord server](https://discord.gg/VSj7AFHvpq) | Our chat server

View File

@@ -10,6 +10,36 @@ Please [contact support](https://raw.githubusercontent.com/laurent22/joplin/dev/
For general opinions on what makes an app more or less secure, please use the forum.
## Areas outside Joplin's Threat Model
Note: we're mostly linking to Chrome's documentation since our reasoning for these exclusions is the same.
### Denial of Service (DoS)
[Reference](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#are-denial-of-service-issues-considered-security-bugs)
### Physically-local attacks
[Reference](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
### Compromised/infected machines
[Reference](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-compromised_infected-machines-in-chromes-threat-model)
### Is opening a file on the local machine a security vulnerability?
No - users are allowed to link to files on their local computer. This was a feature that was implemented by popular request. There are measures in place to mitigate security risks such as a dialog to confirm whether a file with an unknown file extension should be opened.
### Is DLL sideloading a security vulnerability?
No. This is an Electron issue and not one they will fix: https://github.com/electron/electron/issues/28384
See also [Physically-local attacks](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
### Is local data not being encrypted a security vulnerability?
No, but you should use disk encryption. See also [Physically-local attacks](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
## Bounty
We **do not** offer a bounty for discovering vulnerabilities, please do not ask. We can however credit you and link to your website in the changelog and release announcement.

View File

@@ -33,6 +33,7 @@
"/packages/app-desktop/build/",
"/packages/app-desktop/utils/checkForUpdatesUtilsTestData.ts",
"/packages/app-desktop/vendor/",
"/packages/app-mobile/android/vendor/",
"/packages/app-mobile/ios/Pods/",
"/packages/app-mobile/lib/rnInjectedJs",
"/packages/app-mobile/pluginAssets",

View File

@@ -16,7 +16,7 @@ services:
- POSTGRES_DATABASE=joplin
- POSTGRES_USER=joplin
- POSTGRES_PORT=5432
- POSTGRES_HOST=localhost
- POSTGRES_HOST=db
db:
image: postgres:16
ports:

View File

@@ -0,0 +1 @@
Додаток для заміток і завдань із синхронізацією між Linux, macOS, Windows і мобільними пристроями

View File

@@ -114,6 +114,8 @@
"react-native@0.74.1": "patch:react-native@npm%3A0.74.1#./.yarn/patches/react-native-npm-0.74.1-754c02ae9e.patch",
"rn-fetch-blob@0.12.0": "patch:rn-fetch-blob@npm%3A0.12.0#./.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch",
"app-builder-lib@26.0.0-alpha.7": "patch:app-builder-lib@npm%3A26.0.0-alpha.7#./.yarn/patches/app-builder-lib-npm-26.0.0-alpha.7-e1b3dca119.patch",
"app-builder-lib@24.13.3": "patch:app-builder-lib@npm%3A24.13.3#./.yarn/patches/app-builder-lib-npm-24.13.3-86a66c0bf3.patch"
"app-builder-lib@24.13.3": "patch:app-builder-lib@npm%3A24.13.3#./.yarn/patches/app-builder-lib-npm-24.13.3-86a66c0bf3.patch",
"react-native-sqlite-storage@6.0.1": "patch:react-native-sqlite-storage@npm%3A6.0.1#./.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch",
"react-native-paper@5.13.1": "patch:react-native-paper@npm%3A5.13.1#./.yarn/patches/react-native-paper-npm-5.13.1-f153e542e2.patch"
}
}

View File

@@ -189,9 +189,9 @@ async function fetchAllNotes() {
lines.push('## Searching');
lines.push('');
lines.push('Call **GET /search?query=YOUR_QUERY** to search for notes. This end-point supports the `field` parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/help/#searching');
lines.push('Call **GET /search?query=YOUR_QUERY** to search for notes. This end-point supports the `field` parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/help/apps/search');
lines.push('');
lines.push('To retrieve non-notes items, such as notebooks or tags, add a `type` parameter and set it to the required [item type name](#item-type-id). In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use `*` as a wildcard. This is convenient for example to retrieve notebooks or tags by title.');
lines.push('To retrieve non-notes items, such as notebooks or tags, add a `type` parameter and set it to the required [item type name](#item-type-ids). In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use `*` as a wildcard. This is convenient for example to retrieve notebooks or tags by title.');
lines.push('');
lines.push('For example, to retrieve the notebook named `recipes`: **GET /search?query=recipes&type=folder**');
lines.push('');

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env -S NODE_OPTIONS=--no-deprecation node
#!/usr/bin/env node
// Use njstrace to find out what Node.js might be spending time on
// var njstrace = require('njstrace').inject();

View File

@@ -35,15 +35,15 @@
],
"owner": "Laurent Cozic"
},
"version": "3.2.2",
"version": "3.3.0",
"bin": "./main.js",
"engines": {
"node": ">=10.0.0"
},
"dependencies": {
"@joplin/lib": "~3.2",
"@joplin/renderer": "~3.2",
"@joplin/utils": "~3.2",
"@joplin/lib": "~3.3",
"@joplin/renderer": "~3.3",
"@joplin/utils": "~3.3",
"aws-sdk": "2.1340.0",
"chalk": "4.1.2",
"compare-version": "0.1.2",
@@ -69,10 +69,10 @@
"yargs-parser": "21.1.1"
},
"devDependencies": {
"@joplin/tools": "~3.2",
"@joplin/tools": "~3.3",
"@types/fs-extra": "11.0.4",
"@types/jest": "29.5.12",
"@types/node": "18.19.55",
"@types/node": "18.19.67",
"@types/proper-lockfile": "^4.1.2",
"gulp": "4.0.2",
"jest": "29.7.0",

View File

@@ -1 +1 @@
Should keep this comment: <!-- keep this &amp; that -->
Should keep this comment: <!-- keep this & that -->

View File

@@ -0,0 +1,3 @@
<img src="test/" id="getElementById" class="jop-noMdConv"/>
<img src="http://example.com/test.png" id="getElementById" class="jop-noMdConv"/>
<img id="test2" src="http://example.com/test.png" class="jop-noMdConv"/>

View File

@@ -0,0 +1,5 @@
<img name=getElementById src=test/>
<IMG NAME="getElementById" SRC="http://example.com/test.png">
<IMG NAME="test" ID="test2" SRC="http://example.com/test.png">

View File

@@ -0,0 +1 @@
<math class="jop-noMdConv"><p class="jop-noMdConv"><style class="jop-noMdConv"><!--&lt;/style&gt;&lt;img src onerror=alert(1)&gt;--></style>

View File

@@ -0,0 +1 @@
<math><p><style><!--</style><img src onerror=alert(1)>--></style>

View File

@@ -0,0 +1 @@
![malformed link](https://malformed_uri/%E0%A4%A.jpg)

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Joplin Web Clipper [DEV]",
"version": "3.2.0",
"version": "3.3.0",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplinapp.org",
"content_security_policy": {

View File

@@ -23,7 +23,7 @@
"react-redux": "9.0.4",
"redux": "5.0.1",
"style-loader": "3.3.3",
"webpack": "5.89.0",
"webpack": "5.97.1",
"webpack-cli": "5.1.4"
}
},
@@ -1928,10 +1928,11 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
@@ -1961,148 +1962,163 @@
"dev": true
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
"@webassemblyjs/helper-numbers": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
}
},
"node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
"dev": true
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
"dev": true
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
"dev": true
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
"@webassemblyjs/helper-api-error": "1.13.2",
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
"dev": true
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/wasm-gen": "1.14.1"
}
},
"node_modules/@webassemblyjs/ieee754": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
},
"node_modules/@webassemblyjs/leb128": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
"dev": true
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-opt": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6",
"@webassemblyjs/wast-printer": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/helper-wasm-section": "1.14.1",
"@webassemblyjs/wasm-gen": "1.14.1",
"@webassemblyjs/wasm-opt": "1.14.1",
"@webassemblyjs/wasm-parser": "1.14.1",
"@webassemblyjs/wast-printer": "1.14.1"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/ieee754": "1.13.2",
"@webassemblyjs/leb128": "1.13.2",
"@webassemblyjs/utf8": "1.13.2"
}
},
"node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/wasm-gen": "1.14.1",
"@webassemblyjs/wasm-parser": "1.14.1"
}
},
"node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-api-error": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/ieee754": "1.13.2",
"@webassemblyjs/leb128": "1.13.2",
"@webassemblyjs/utf8": "1.13.2"
}
},
"node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.14.1",
"@xtuc/long": "4.2.2"
}
},
@@ -2154,19 +2170,22 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"dev": true
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
"dev": true,
"license": "Apache-2.0"
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@@ -2174,15 +2193,6 @@
"node": ">=0.4.0"
}
},
"node_modules/acorn-import-assertions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"dev": true,
"peerDependencies": {
"acorn": "^8"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -2393,9 +2403,9 @@
"dev": true
},
"node_modules/browserslist": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
"integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
"dev": true,
"funding": [
{
@@ -2411,11 +2421,12 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001565",
"electron-to-chromium": "^1.4.601",
"node-releases": "^2.0.14",
"update-browserslist-db": "^1.0.13"
"caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.1"
},
"bin": {
"browserslist": "cli.js"
@@ -2441,9 +2452,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001574",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz",
"integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==",
"version": "1.0.30001692",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
"dev": true,
"funding": [
{
@@ -2458,7 +2469,8 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
],
"license": "CC-BY-4.0"
},
"node_modules/chalk": {
"version": "2.4.2",
@@ -2736,16 +2748,18 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.623",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz",
"integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==",
"dev": true
"version": "1.5.83",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
"integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
"dev": true,
"license": "ISC"
},
"node_modules/enhanced-resolve": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -2782,10 +2796,11 @@
"dev": true
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
@@ -3017,7 +3032,8 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"dev": true
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/globals": {
"version": "11.12.0",
@@ -3433,10 +3449,11 @@
}
},
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"dev": true
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"dev": true,
"license": "MIT"
},
"node_modules/nth-check": {
"version": "2.1.1",
@@ -3504,10 +3521,11 @@
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/pkg-dir": {
"version": "4.2.0",
@@ -4254,9 +4272,9 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
"integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
"dev": true,
"funding": [
{
@@ -4272,9 +4290,10 @@
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
@@ -4314,10 +4333,11 @@
"dev": true
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -4327,34 +4347,34 @@
}
},
"node_modules/webpack": {
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"version": "5.97.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
"@webassemblyjs/ast": "^1.11.5",
"@webassemblyjs/wasm-edit": "^1.11.5",
"@webassemblyjs/wasm-parser": "^1.11.5",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0",
"browserslist": "^4.14.5",
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
"@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-edit": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.14.1",
"acorn": "^8.14.0",
"browserslist": "^4.24.0",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7",
"watchpack": "^2.4.0",
"terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"bin": {
@@ -5830,9 +5850,9 @@
}
},
"@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true
},
"@types/html-minifier-terser": {
@@ -5863,148 +5883,148 @@
"dev": true
},
"@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"dev": true,
"requires": {
"@webassemblyjs/helper-numbers": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
"@webassemblyjs/helper-numbers": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
}
},
"@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"dev": true
},
"@webassemblyjs/helper-api-error": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"dev": true
},
"@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"dev": true
},
"@webassemblyjs/helper-numbers": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"dev": true,
"requires": {
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
"@webassemblyjs/helper-api-error": "1.13.2",
"@xtuc/long": "4.2.2"
}
},
"@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
"dev": true
},
"@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/wasm-gen": "1.14.1"
}
},
"@webassemblyjs/ieee754": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"dev": true,
"requires": {
"@xtuc/ieee754": "^1.2.0"
}
},
"@webassemblyjs/leb128": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
"dev": true,
"requires": {
"@xtuc/long": "4.2.2"
}
},
"@webassemblyjs/utf8": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
"dev": true
},
"@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-opt": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6",
"@webassemblyjs/wast-printer": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/helper-wasm-section": "1.14.1",
"@webassemblyjs/wasm-gen": "1.14.1",
"@webassemblyjs/wasm-opt": "1.14.1",
"@webassemblyjs/wasm-parser": "1.14.1",
"@webassemblyjs/wast-printer": "1.14.1"
}
},
"@webassemblyjs/wasm-gen": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/ieee754": "1.13.2",
"@webassemblyjs/leb128": "1.13.2",
"@webassemblyjs/utf8": "1.13.2"
}
},
"@webassemblyjs/wasm-opt": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6",
"@webassemblyjs/wasm-parser": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
"@webassemblyjs/wasm-gen": "1.14.1",
"@webassemblyjs/wasm-parser": "1.14.1"
}
},
"@webassemblyjs/wasm-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6",
"@webassemblyjs/utf8": "1.11.6"
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-api-error": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
"@webassemblyjs/ieee754": "1.13.2",
"@webassemblyjs/leb128": "1.13.2",
"@webassemblyjs/utf8": "1.13.2"
}
},
"@webassemblyjs/wast-printer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/ast": "1.14.1",
"@xtuc/long": "4.2.2"
}
},
@@ -6042,18 +6062,11 @@
"dev": true
},
"acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"dev": true
},
"acorn-import-assertions": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"dev": true,
"requires": {}
},
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -6213,15 +6226,15 @@
"dev": true
},
"browserslist": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
"integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001565",
"electron-to-chromium": "^1.4.601",
"node-releases": "^2.0.14",
"update-browserslist-db": "^1.0.13"
"caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.1"
}
},
"buffer-from": {
@@ -6241,9 +6254,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001574",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz",
"integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==",
"version": "1.0.30001692",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
"dev": true
},
"chalk": {
@@ -6449,15 +6462,15 @@
}
},
"electron-to-chromium": {
"version": "1.4.623",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz",
"integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==",
"version": "1.5.83",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
"integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
"dev": true
},
"enhanced-resolve": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.4",
@@ -6483,9 +6496,9 @@
"dev": true
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true
},
"escape-string-regexp": {
@@ -6937,9 +6950,9 @@
}
},
"node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"dev": true
},
"nth-check": {
@@ -6996,9 +7009,9 @@
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"pkg-dir": {
@@ -7520,13 +7533,13 @@
"dev": true
},
"update-browserslist-db": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
"integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
"dev": true,
"requires": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
}
},
"uri-js": {
@@ -7558,9 +7571,9 @@
"dev": true
},
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"dev": true,
"requires": {
"glob-to-regexp": "^0.4.1",
@@ -7568,34 +7581,33 @@
}
},
"webpack": {
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"version": "5.97.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
"dev": true,
"requires": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
"@webassemblyjs/ast": "^1.11.5",
"@webassemblyjs/wasm-edit": "^1.11.5",
"@webassemblyjs/wasm-parser": "^1.11.5",
"acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0",
"browserslist": "^4.14.5",
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
"@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-edit": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.14.1",
"acorn": "^8.14.0",
"browserslist": "^4.24.0",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^3.2.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7",
"watchpack": "^2.4.0",
"terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"dependencies": {

View File

@@ -23,7 +23,7 @@
"react-redux": "9.0.4",
"redux": "5.0.1",
"style-loader": "3.3.3",
"webpack": "5.89.0",
"webpack": "5.97.1",
"webpack-cli": "5.1.4"
},
"browserslist": [

View File

@@ -262,15 +262,25 @@ export default class ElectronAppWrapper {
// the easiest is to use a timeout. Keep in mind that if you get a white window on Windows it might be due
// to this line though.
if (debugEarlyBugs) {
setTimeout(() => {
// Since a recent release of Electron (v34?), calling openDevTools() here does nothing
// if a plugin devtool window is already opened. Maybe because they do a check on
// `isDevToolsOpened` which indeed returns `true` (but shouldn't since it's for a
// different window). However, if you open the dev tools twice from the Help menu it
// works. So instead we do that here and call openDevTool() three times.
let openDevToolCount = 0;
const openDevToolInterval = setInterval(() => {
try {
this.win_.webContents.openDevTools();
openDevToolCount++;
if (openDevToolCount >= 3) {
clearInterval(openDevToolInterval);
}
} catch (error) {
// This will throw an exception "Object has been destroyed" if the app is closed
// in less that the timeout interval. It can be ignored.
// This will throw an exception "Object has been destroyed" if the app is closed
// in less that the timeout interval. It can be ignored.
console.warn('Error opening dev tools', error);
}
}, 3000);
}, 1000);
}
const addWindowEventHandlers = (webContents: WebContents) => {

View File

@@ -40,6 +40,8 @@ export interface AppWindowState extends WindowState {
visibleDialogs: VisibleDialogs;
dialogs: AppStateDialog[];
devToolsVisible: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
watchedResources: any;
}
interface BackgroundWindowStates {
@@ -62,8 +64,6 @@ export interface AppState extends State, AppWindowState {
modalOverlayMessage: string|null;
// Extra reducer keys go here
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
watchedResources: any;
mainLayout: LayoutItem;
isResettingLayout: boolean;
}
@@ -76,6 +76,7 @@ export const createAppDefaultWindowState = (): AppWindowState => {
noteVisiblePanes: ['editor', 'viewer'],
editorCodeView: true,
devToolsVisible: false,
watchedResources: {},
};
};

View File

@@ -19,7 +19,6 @@ import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerS
import SpellCheckerServiceDriverNative from './services/spellChecker/SpellCheckerServiceDriverNative';
import bridge from './services/bridge';
import menuCommandNames from './gui/menuCommandNames';
import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext';
import ResourceService from '@joplin/lib/services/ResourceService';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
import appReducer, { createAppDefaultState } from './app.reducer';
@@ -35,29 +34,15 @@ const PluginManager = require('@joplin/lib/services/PluginManager');
import RevisionService from '@joplin/lib/services/RevisionService';
import MigrationService from '@joplin/lib/services/MigrationService';
import { loadCustomCss } from '@joplin/lib/CssUtils';
import mainScreenCommands from './gui/WindowCommandsAndDialogs/commands/index';
import noteEditorCommands from './gui/NoteEditor/commands/index';
import noteListCommands from './gui/NoteList/commands/index';
import noteListControlsCommands from './gui/NoteListControls/commands/index';
import sidebarCommands from './gui/Sidebar/commands/index';
import appCommands from './commands/index';
import libCommands from '@joplin/lib/commands/index';
import { homedir } from 'os';
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
const electronContextMenu = require('./services/electron-context-menu');
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
const commands = mainScreenCommands
.concat(noteEditorCommands)
.concat(noteListCommands)
.concat(noteListControlsCommands)
.concat(sidebarCommands);
// Commands that are not tied to any particular component.
// The runtime for these commands can be loaded when the app starts.
const globalCommands = appCommands.concat(libCommands);
import editorCommandDeclarations from './gui/NoteEditor/editorCommandDeclarations';
import PerFolderSortOrderService from './services/sortOrder/PerFolderSortOrderService';
import ShareService from '@joplin/lib/services/share/ShareService';
import checkForUpdates from './checkForUpdates';
@@ -74,6 +59,7 @@ import SearchEngine from '@joplin/lib/services/search/SearchEngine';
import { PackageInfo } from '@joplin/lib/versionInfo';
import { CustomProtocolHandler } from './utils/customProtocols/handleCustomProtocols';
import { refreshFolders } from '@joplin/lib/folders-screen-utils';
import initializeCommandService from './utils/initializeCommandService';
const pluginClasses = [
require('./plugins/GotoAnything').default,
@@ -492,20 +478,7 @@ class Application extends BaseApplication {
PerFolderSortOrderService.initialize();
CommandService.instance().initialize(this.store(), Setting.value('env') === 'dev', stateToWhenClauseContext);
for (const command of commands) {
CommandService.instance().registerDeclaration(command.declaration);
}
for (const command of globalCommands) {
CommandService.instance().registerDeclaration(command.declaration);
CommandService.instance().registerRuntime(command.declaration.name, command.runtime());
}
for (const declaration of editorCommandDeclarations) {
CommandService.instance().registerDeclaration(declaration);
}
initializeCommandService(this.store(), Setting.value('env') === 'dev');
const keymapService = KeymapService.instance();
// We only add the commands that appear in the menu because only
@@ -583,6 +556,12 @@ class Application extends BaseApplication {
value: Setting.value('flagOpenDevTools'),
});
// Always disable on Mac for now - and disable too for the few apps that may have the flag enabled.
// At present, it only seems to work on Windows.
if (shim.isMac()) {
Setting.setValue('featureFlag.autoUpdaterServiceEnabled', false);
}
// Note: Auto-update is a misnomer in the code.
// The code below only checks, if a new version is available.
// We only allow Windows and macOS users to automatically check for updates
@@ -653,6 +632,7 @@ class Application extends BaseApplication {
// 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),
() => this.store().getState().windowId,
);
// Forwards the local event to the global event manager, so that it can

View File

@@ -1,5 +1,5 @@
import ElectronAppWrapper from './ElectronAppWrapper';
import shim from '@joplin/lib/shim';
import shim, { MessageBoxType } from '@joplin/lib/shim';
import { _, setLocale } from '@joplin/lib/locale';
import { BrowserWindow, nativeTheme, nativeImage, shell, dialog, MessageBoxSyncOptions, safeStorage } from 'electron';
import { dirname, toSystemSlashes } from '@joplin/lib/path-utils';
@@ -118,6 +118,8 @@ export class Bridge {
return event;
}
},
integrations: [Sentry.electronMinidumpIntegration()],
};
if (this.autoUploadCrashDumps_) options.dsn = 'https://cceec550871b1e8a10fee4c7a28d5cf2@o4506576757522432.ingest.sentry.io/4506594281783296';
@@ -384,9 +386,14 @@ export class Bridge {
/* returns the index of the clicked button */
public showMessageBox(message: string, options: MessageDialogOptions = {}) {
const defaultButtons = [_('OK')];
if (options.type !== MessageBoxType.Error && options.type !== MessageBoxType.Info) {
defaultButtons.push(_('Cancel'));
}
const result = this.showMessageBox_(this.activeWindow(), { type: 'question',
message: message,
buttons: [_('OK'), _('Cancel')], ...options });
buttons: defaultButtons, ...options });
return result;
}

View File

@@ -1,5 +1,6 @@
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import shim, { MessageBoxType } from '@joplin/lib/shim';
const app = require('@electron/remote').app;
const { clipboard } = require('electron');
@@ -14,7 +15,7 @@ export const runtime = (): CommandRuntime => {
const appPath = app.getPath('exe');
const cmd = `${appPath} --env dev`;
clipboard.writeText(cmd);
alert(`The dev mode command has been copied to clipboard:\n\n${cmd}`);
await shim.showMessageBox(`The dev mode command has been copied to clipboard:\n\n${cmd}`, { type: MessageBoxType.Info });
},
};
};

View File

@@ -16,6 +16,7 @@ export const runtime = (): CommandRuntime => {
if (target === 'noteList') return CommandService.instance().execute('focusElementNoteList');
if (target === 'sideBar') return CommandService.instance().execute('focusElementSideBar');
if (target === 'noteTitle') return CommandService.instance().execute('focusElementNoteTitle', options);
if (target === 'toolbar') return CommandService.instance().execute('focusElementToolbar', options);
throw new Error(`Invalid focus target: ${target}`);
},
};

View File

@@ -18,6 +18,7 @@ import * as switchProfile2 from './switchProfile2';
import * as switchProfile3 from './switchProfile3';
import * as toggleExternalEditing from './toggleExternalEditing';
import * as toggleSafeMode from './toggleSafeMode';
import * as toggleTabMovesFocus from './toggleTabMovesFocus';
const index: any[] = [
copyDevCommand,
@@ -39,6 +40,7 @@ const index: any[] = [
switchProfile3,
toggleExternalEditing,
toggleSafeMode,
toggleTabMovesFocus,
];
export default index;

View File

@@ -7,7 +7,7 @@ import Setting from '@joplin/lib/models/Setting';
export const declaration: CommandDeclaration = {
name: 'openNoteInNewWindow',
label: () => _('Edit in new window'),
label: () => _('Open in new window'),
iconName: 'icon-share',
};

View File

@@ -1,5 +1,6 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import RevisionService from '@joplin/lib/services/RevisionService';
import shim, { MessageBoxType } from '@joplin/lib/shim';
export const declaration: CommandDeclaration = {
name: 'restoreNoteRevision',
@@ -11,9 +12,9 @@ export const runtime = (): CommandRuntime => {
execute: async (_context: CommandContext, noteId: string, reverseRevIndex = 0) => {
try {
const note = await RevisionService.instance().restoreNoteById(noteId, reverseRevIndex);
alert(RevisionService.instance().restoreSuccessMessage(note));
await shim.showMessageBox(RevisionService.instance().restoreSuccessMessage(note), { type: MessageBoxType.Info });
} catch (error) {
alert(error.message);
await shim.showErrorDialog(error.message);
}
},
};

View File

@@ -7,7 +7,7 @@ const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'startExternalEditing',
label: () => _('Edit in external editor'),
label: () => _('Open in external editor'),
iconName: 'icon-share',
};

View File

@@ -0,0 +1,20 @@
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { DesktopCommandContext } from '../services/commands/types';
import Setting from '@joplin/lib/models/Setting';
export const declaration: CommandDeclaration = {
name: 'toggleTabMovesFocus',
label: () => _('Toggle editor tab key navigation'),
iconName: 'fas fa-keyboard',
};
export const runtime = (): CommandRuntime => {
return {
execute: async (_context: DesktopCommandContext, enabled: boolean = null) => {
const newValue = enabled ?? !Setting.value('editor.tabMovesFocus');
Setting.setValue('editor.tabMovesFocus', newValue);
},
enabledCondition: 'oneNoteSelected',
};
};

View File

@@ -18,27 +18,21 @@ export enum ButtonSize {
Normal = 2,
}
interface Props {
type ReactButtonProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
interface Props extends Omit<ReactButtonProps, 'onClick'> {
title?: string;
iconName?: string;
level?: ButtonLevel;
iconLabel?: string;
className?: string;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
onClick?: Function;
onClick?: ()=> void;
color?: string;
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;
iconOnly?: boolean;
fontSize?: number;
'aria-controls'?: string;
'aria-expanded'?: string;
}
const StyledTitle = styled.span`
@@ -215,54 +209,52 @@ function buttonClass(level: ButtonLevel) {
return StyledButtonSecondary;
}
const Button = React.forwardRef(({
iconName, iconLabel, iconAnimation, color, title, level, fontSize, isSquare, tooltip, disabled, onClick: propsOnClick, ...unusedProps
// 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;
}: Props, ref: any) => {
const iconOnly = iconName && !title;
const StyledButton = buttonClass(props.level);
const StyledButton = buttonClass(level);
function renderIcon() {
if (!props.iconName) return null;
if (!iconName) return null;
return <StyledIcon
aria-label={props.iconLabel ?? undefined}
aria-hidden={!props.iconLabel}
animation={props.iconAnimation}
aria-label={iconLabel ?? undefined}
aria-hidden={!iconLabel}
animation={iconAnimation}
mr={iconOnly ? '0' : '6px'}
color={props.color}
className={props.iconName}
color={color}
className={iconName}
role='img'
/>;
}
function renderTitle() {
if (!props.title) return null;
return <StyledTitle color={props.color}>{props.title}</StyledTitle>;
if (!title) return null;
return <StyledTitle color={color}>{title}</StyledTitle>;
}
function onClick() {
if (props.disabled) return;
props.onClick();
if (disabled) return;
propsOnClick();
}
return (
<StyledButton
ref={ref}
fontSize={props.fontSize}
isSquare={props.isSquare}
size={props.size}
style={props.style}
disabled={props.disabled}
title={props.tooltip}
className={props.className}
fontSize={fontSize}
isSquare={isSquare}
disabled={disabled}
title={tooltip}
iconOnly={iconOnly}
onClick={onClick}
// When there's no title, the button needs a label. In this case, fall back
// to the tooltip.
aria-label={props.title ? undefined : props.tooltip}
aria-disabled={props.disabled}
aria-expanded={props['aria-expanded']}
aria-controls={props['aria-controls']}
aria-label={title ? undefined : tooltip}
aria-disabled={disabled}
{...unusedProps}
>
{renderIcon()}
{renderTitle()}

View File

@@ -9,6 +9,7 @@ import ClipperServer from '@joplin/lib/ClipperServer';
import Setting from '@joplin/lib/models/Setting';
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import { AppState } from '../app.reducer';
import shim, { MessageBoxType } from '@joplin/lib/shim';
class ClipperConfigScreenComponent extends React.Component {
public constructor() {
@@ -30,7 +31,7 @@ class ClipperConfigScreenComponent extends React.Component {
private copyToken_click() {
clipboard.writeText(this.props.apiToken);
alert(_('Token has been copied to the clipboard!'));
void shim.showMessageBox(_('Token has been copied to the clipboard!'), { type: MessageBoxType.Info });
}
private renewToken_click() {

View File

@@ -17,7 +17,7 @@ interface Props {
onApplyClick?: Function;
}
export const StyledRoot = styled.div`
const StyledRoot = styled.nav`
display: flex;
align-items: center;
padding: 10px;

View File

@@ -19,6 +19,7 @@ import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/conf
import MacOSMissingPasswordHelpLink from './controls/MissingPasswordHelpLink';
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
import SettingComponent, { UpdateSettingValueEvent } from './controls/SettingComponent';
import shim from '@joplin/lib/shim';
interface Font {
@@ -144,7 +145,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
screenName = section.name;
if (this.hasChanges()) {
const ok = confirm(_('This will open a new screen. Save your current changes?'));
const ok = await shim.showConfirmationDialog(_('This will open a new screen. Save your current changes?'));
if (ok) {
await shared.saveSettings(this);
}

View File

@@ -164,7 +164,7 @@ export default function Sidebar(props: Props) {
}
return (
<StyledRoot role='tablist'>
<StyledRoot className='settings-sidebar _scrollbar2' role='tablist'>
{buttons}
</StyledRoot>
);

View File

@@ -1,8 +1,8 @@
import React = require('react');
import { useMemo, useState, useCallback, CSSProperties, useEffect, useRef } from 'react';
import * as React from 'react';
import { useState, useCallback, CSSProperties, useEffect } from 'react';
import { _ } from '@joplin/lib/locale';
import { SettingItemSubType } from '@joplin/lib/models/Setting';
import { focus } from '@joplin/lib/utils/focusHandler';
import InlineCombobox from '../../InlineCombobox';
interface Props {
type: string;
@@ -17,14 +17,8 @@ interface Props {
const FontSearch = (props: Props) => {
const { type, style, value, availableFonts, onChange, subtype } = props;
const [filteredAvailableFonts, setFilteredAvailableFonts] = useState(availableFonts);
const [inputText, setInputText] = useState(value);
const [showList, setShowList] = useState(false);
const [isListHovered, setIsListHovered] = useState(false);
const [isFontSelected, setIsFontSelected] = useState(value !== '');
const [visibleFonts, setVisibleFonts] = useState<string[]>([]);
const [isMonoBoxChecked, setIsMonoBoxChecked] = useState(false);
const isLoadingFonts = filteredAvailableFonts.length === 0;
const fontInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (subtype === SettingItemSubType.MonospaceFontFamily) {
@@ -41,112 +35,34 @@ const FontSearch = (props: Props) => {
setFilteredAvailableFonts(localMonospacedFonts);
}, [isMonoBoxChecked, availableFonts]);
const displayedFonts = useMemo(() => {
if (isFontSelected) return filteredAvailableFonts;
return filteredAvailableFonts.filter((font: string) =>
font.toLowerCase().startsWith(inputText.toLowerCase()),
);
}, [filteredAvailableFonts, inputText, isFontSelected]);
useEffect(() => {
setVisibleFonts(displayedFonts.slice(0, 20));
}, [displayedFonts]);
// Lazy loading
const handleListScroll: React.UIEventHandler<HTMLDivElement> = useCallback((event) => {
const scrollTop = (event.target as HTMLDivElement).scrollTop;
const scrollHeight = (event.target as HTMLDivElement).scrollHeight;
const clientHeight = (event.target as HTMLDivElement).clientHeight;
// Check if the user has scrolled to the bottom of the container
// A small buffer of 20 pixels is subtracted from the total scrollHeight to ensure new content starts loading slightly before the user reaches the absolute bottom, providing a smoother experience.
if (scrollTop + clientHeight >= scrollHeight - 20) {
// Load the next 20 fonts
const remainingFonts = displayedFonts.slice(visibleFonts.length, visibleFonts.length + 20);
setVisibleFonts([...visibleFonts, ...remainingFonts]);
}
}, [displayedFonts, visibleFonts]);
const handleTextChange: React.ChangeEventHandler<HTMLInputElement> = useCallback((event) => {
setIsFontSelected(false);
setInputText(event.target.value);
onChange(event.target.value);
}, [onChange]);
const handleFocus: React.FocusEventHandler<HTMLInputElement> = useCallback(() => setShowList(true), []);
const handleBlur: React.FocusEventHandler<HTMLInputElement> = useCallback(() => {
if (!isListHovered) {
setShowList(false);
}
}, [isListHovered]);
const handleFontClick: React.MouseEventHandler<HTMLDivElement> = useCallback((event) => {
const font = (event.target as HTMLDivElement).innerText;
setInputText(font);
setShowList(false);
onChange(font);
setIsFontSelected(true);
}, [onChange]);
const handleListHover: React.MouseEventHandler<HTMLDivElement> = useCallback(() => setIsListHovered(true), []);
const handleListLeave: React.MouseEventHandler<HTMLDivElement> = useCallback(() => setIsListHovered(false), []);
const handleMonoBoxCheck: React.ChangeEventHandler<HTMLInputElement> = useCallback(() => {
setIsMonoBoxChecked(!isMonoBoxChecked);
focus('FontSearch::fontInputRef', fontInputRef.current);
}, [isMonoBoxChecked]);
return (
<>
const comboboxControls = <>
{isLoadingFonts ? _('Loading...') : null}
<div className='monospace-checkbox'>
<input
type={type}
style={style}
value={inputText}
onChange={handleTextChange}
onFocus={handleFocus}
onBlur={handleBlur}
spellCheck={false}
id={props.inputId}
ref={fontInputRef}
type='checkbox'
checked={isMonoBoxChecked}
onChange={handleMonoBoxCheck}
id={`show-monospace-fonts_${subtype}`}
/>
<div
className={'font-search-list'}
style={{ display: showList ? 'block' : 'none' }}
onMouseEnter={handleListHover}
onMouseLeave={handleListLeave}
onScroll={handleListScroll}
>
{
isLoadingFonts ? <div>{_('Loading...')}</div> :
<>
<div className='monospace-checkbox'>
<input
type='checkbox'
checked={isMonoBoxChecked}
onChange={handleMonoBoxCheck}
id={`show-monospace-fonts_${subtype}`}
/>
<label htmlFor={`show-monospace-fonts_${subtype}`}>{_('Show monospace fonts only.')}</label>
</div>
{
visibleFonts.map((font: string) =>
<div
key={font}
style={{ fontFamily: `"${font}"` }}
onClick={handleFontClick}
className='font-search-item'
>
{font}
</div>,
)
}
</>
}
</div>
</>
<label htmlFor={`show-monospace-fonts_${subtype}`}>{_('Show monospace fonts only.')}</label>
</div>
</>;
return (
<InlineCombobox
inputType={type}
inputStyle={style}
value={value}
suggestedValues={filteredAvailableFonts}
renderOption={font => <span style={{ fontFamily: font }}>{font}</span>}
controls={comboboxControls}
onChange={onChange}
inputId={props.inputId}
/>
);
};

View File

@@ -1,4 +1,3 @@
@use "./setting-description.scss";
@use "./setting-label.scss";
@use "./setting-header.scss";

View File

@@ -63,23 +63,70 @@ const Dialog: React.FC<Props> = props => {
</div>;
};
// We keep track of the mouse events to allow the action to be cancellable on the mouseup
// If dialogElement is the source of the mouse event it means
// that the user clicked in the dimmed background and not in the content of the dialog
const useClickedOutsideContent = (dialogElement: HTMLDialogElement|null) => {
const mouseDownOutsideContent = useRef(false);
mouseDownOutsideContent.current = false;
const [clickedOutsideContent, setClickedOutsideContent] = useState(false);
useEffect(() => {
if (!dialogElement) return () => {};
const mouseDownListener = (event: MouseEvent) => {
if (event.target === dialogElement) {
mouseDownOutsideContent.current = true;
} else {
mouseDownOutsideContent.current = false;
}
};
const mouseUpListener = (event: MouseEvent) => {
if (!mouseDownOutsideContent.current) return;
if (mouseDownOutsideContent.current && event.target === dialogElement) {
setClickedOutsideContent(true);
mouseDownOutsideContent.current = false;
} else {
setClickedOutsideContent(false);
mouseDownOutsideContent.current = false;
}
};
dialogElement.addEventListener('mousedown', mouseDownListener);
dialogElement.addEventListener('mouseup', mouseUpListener);
return () => {
dialogElement.removeEventListener('mousedown', mouseDownListener);
dialogElement.removeEventListener('mouseup', mouseUpListener);
};
}, [dialogElement]);
return [clickedOutsideContent, setClickedOutsideContent] as const;
};
const useDialogElement = (containerDocument: Document, onCancel: undefined|OnCancelListener) => {
const [dialogElement, setDialogElement] = useState<HTMLDialogElement|null>(null);
const onCancelRef = useRef(onCancel);
onCancelRef.current = onCancel;
const [clickedOutsideContent, setClickedOutsideContent] = useClickedOutsideContent(dialogElement);
useEffect(() => {
if (clickedOutsideContent) {
const onCancel = onCancelRef.current;
if (onCancel) {
onCancel();
} else {
setClickedOutsideContent(false);
}
}
}, [clickedOutsideContent, setClickedOutsideContent]);
useEffect(() => {
if (!containerDocument) return () => {};
const dialog = containerDocument.createElement('dialog');
dialog.addEventListener('click', event => {
const onCancel = onCancelRef.current;
const isBackgroundClick = event.target === dialog;
if (isBackgroundClick && onCancel) {
onCancel();
}
});
dialog.classList.add('dialog-modal-layer');
dialog.addEventListener('cancel', event => {
const canCancel = !!onCancelRef.current;

View File

@@ -1,7 +1,7 @@
import styled from 'styled-components';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const Root = styled.div<any>`
const Root = styled.h1<any>`
display: flex;
justify-content: ${props => props.justifyContent ? props.justifyContent : 'center'};
font-family: ${props => props.theme.fontFamily};

View File

@@ -3,7 +3,7 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import { themeStyle } from '@joplin/lib/theme';
import { _ } from '@joplin/lib/locale';
import time from '@joplin/lib/time';
import shim from '@joplin/lib/shim';
import shim, { MessageBoxType } from '@joplin/lib/shim';
import dialogs from '../dialogs';
import { decryptedStatText, determineKeyPassword, dontReencryptData, enableEncryptionConfirmationMessages, onSavePasswordClick, onToggleEnabledClick, reencryptData, upgradeMasterKey, useInputPasswords, useNeedMasterPassword, usePasswordChecker, useStats, useToggleShowDisabledMasterKeys } from '@joplin/lib/components/EncryptionConfigScreen/utils';
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
@@ -47,7 +47,7 @@ const EncryptionConfigScreen = (props: Props) => {
const onUpgradeMasterKey = useCallback(async (mk: MasterKeyEntity) => {
const password = determineKeyPassword(mk.id, masterPasswordKeys, props.masterPassword, props.passwords);
const result = await upgradeMasterKey(mk, password);
alert(result);
await shim.showMessageBox(result, { type: MessageBoxType.Info });
}, [props.passwords, masterPasswordKeys, props.masterPassword]);
const renderNeedUpgradeSection = () => {

View File

@@ -0,0 +1,199 @@
import * as React from 'react';
import { useState, useCallback, CSSProperties, useEffect, useRef, useId } from 'react';
import { _ } from '@joplin/lib/locale';
import { focus } from '@joplin/lib/utils/focusHandler';
import ItemList from './ItemList';
interface Props {
inputType?: string;
inputStyle: CSSProperties;
value: string;
onChange: (newValue: string)=> void;
suggestedValues: string[];
renderOption: (suggestedValue: string)=> React.ReactElement;
controls?: React.ReactNode;
inputId: string;
}
const suggestionMatchesFilter = (suggestion: string, filter: string) => {
return suggestion.toLowerCase().startsWith(filter.toLowerCase());
};
const InlineCombobox: React.FC<Props> = ({ inputType, controls, inputStyle, value, suggestedValues, renderOption, onChange, inputId }) => {
const [showList, setShowList] = useState(false);
const containerRef = useRef<HTMLDivElement|null>(null);
const inputRef = useRef<HTMLInputElement|null>(null);
const listboxRef = useRef<ItemList<string>|null>(null);
const [filteredSuggestions, setFilteredSuggestions] = useState(suggestedValues);
useEffect(() => {
setFilteredSuggestions(suggestedValues);
}, [suggestedValues]);
const selectedIndex = filteredSuggestions.indexOf(value);
useEffect(() => {
if (selectedIndex >= 0 && showList) {
listboxRef.current?.makeItemIndexVisible(selectedIndex);
}
}, [selectedIndex, showList]);
const focusInput = useCallback(() => {
focus('ComboBox/focus input', inputRef.current);
}, []);
const onTextChange: React.ChangeEventHandler<HTMLInputElement> = useCallback((event) => {
const newValue = event.target.value;
onChange(newValue);
setShowList(true);
const filteredSuggestions = suggestedValues.filter((suggestion: string) =>
suggestionMatchesFilter(suggestion, newValue),
);
// If no suggestions, show all fonts
setFilteredSuggestions(filteredSuggestions.length > 0 ? filteredSuggestions : suggestedValues);
}, [onChange, suggestedValues]);
const onFocus: React.FocusEventHandler<HTMLElement> = useCallback(() => {
setShowList(true);
}, []);
const onBlur = useCallback((event: React.FocusEvent) => {
const hasHoverOrFocus = !!containerRef.current.querySelector(':focus-within, :hover');
const movesToContainedItem = containerRef.current.contains(event.relatedTarget);
if (!hasHoverOrFocus && !movesToContainedItem) {
setShowList(false);
}
}, []);
const onItemClick: React.MouseEventHandler<HTMLDivElement> = useCallback((event) => {
const newValue = event.currentTarget.getAttribute('data-key');
if (!newValue) return;
focusInput();
onChange(newValue);
setFilteredSuggestions(suggestedValues);
setShowList(false);
}, [onChange, suggestedValues, focusInput]);
const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = useCallback(event => {
if (event.nativeEvent.isComposing) return;
let closestIndex = selectedIndex;
if (selectedIndex === -1) {
closestIndex = filteredSuggestions.findIndex(suggestion => {
return suggestionMatchesFilter(suggestion, value);
});
}
const isGoToNext = event.code === 'ArrowDown';
if (isGoToNext || event.code === 'ArrowUp') {
event.preventDefault();
if (!event.altKey) {
let newSelectedIndex;
if (isGoToNext) {
newSelectedIndex = (selectedIndex + 1) % filteredSuggestions.length;
} else {
newSelectedIndex = selectedIndex - 1;
if (newSelectedIndex < 0) {
newSelectedIndex += filteredSuggestions.length;
}
}
const newKey = filteredSuggestions[newSelectedIndex];
onChange(newKey);
}
setShowList(true);
} else if (event.code === 'Enter') {
event.preventDefault();
onChange(filteredSuggestions[closestIndex]);
setShowList(false);
} else if (event.code === 'Escape') {
event.preventDefault();
setShowList(false);
}
}, [filteredSuggestions, value, selectedIndex, onChange]);
const valuesListId = useId();
const itemId = (index: number) => {
if (index < 0) {
return undefined;
} else {
return `combobox-${valuesListId}-option-${index}`;
}
};
const onRenderItem = (key: string, index: number) => {
const selected = key === value;
const id = itemId(index);
return (
<div
key={key}
data-key={key}
className={`combobox-suggestion-option ${selected ? '-selected' : ''}`}
role='option'
aria-posinset={1 + index}
aria-setsize={filteredSuggestions.length}
onClick={onItemClick}
aria-selected={selected}
id={id}
>{renderOption(key)}</div>
);
};
return (
<div
className={`combobox-wrapper ${showList ? '-expanded' : ''}`}
onFocus={onFocus}
onBlur={onBlur}
onKeyDown={onKeyDown}
ref={containerRef}
>
<input
type={inputType ?? 'text'}
style={inputStyle}
value={value}
onChange={onTextChange}
onKeyDown={onKeyDown}
spellCheck={false}
id={inputId}
ref={inputRef}
role='combobox'
aria-autocomplete='list'
aria-controls={valuesListId}
aria-expanded={showList}
aria-activedescendant={itemId(selectedIndex)}
/>
<div className='suggestions'>
{
// Custom controls
controls
}
<ItemList
role='listbox'
aria-label={_('Suggestions')}
style={{ height: 200 }}
itemHeight={26}
alwaysRenderSelection={true}
selectedIndex={selectedIndex >= 0 ? selectedIndex : undefined}
items={filteredSuggestions}
itemRenderer={onRenderItem}
id={valuesListId}
ref={listboxRef}
/>
</div>
</div>
);
};
export default InlineCombobox;

View File

@@ -43,6 +43,7 @@ import UpdateNotification from './UpdateNotification/UpdateNotification';
import NoteEditor from './NoteEditor/NoteEditor';
import PluginNotification from './PluginNotification/PluginNotification';
import { Toast } from '@joplin/lib/services/plugins/api/types';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const ipcRenderer = require('electron').ipcRenderer;
@@ -121,6 +122,18 @@ const defaultLayout: LayoutItem = {
],
};
const layoutKeyToLabel = (key: string, plugins: PluginStates) => {
if (key === 'sideBar') return _('Sidebar');
if (key === 'noteList') return _('Note list');
if (key === 'editor') return _('Editor');
const viewInfo = pluginUtils.viewInfoByViewId(plugins, key);
if (viewInfo) {
return PluginService.instance().safePluginNameById(viewInfo.plugin.id);
}
return key;
};
class MainScreenComponent extends React.Component<Props, State> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -567,7 +580,14 @@ class MainScreenComponent extends React.Component<Props, State> {
return (
<div style={styles.messageBox}>
<span style={theme.textStyle}>{msg}</span>
<span
style={theme.textStyle}
role='alert'
// role='alert' has an implicit aria-live='assertive', which tells screen readers that changes
// to the warning's content should be announced as soon as possible. However, since it's generally
// okay for announcements related to these notifications to be delayed, use aria-live='polite'.
aria-live='polite'
>{msg}</span>
</div>
);
}
@@ -728,6 +748,10 @@ class MainScreenComponent extends React.Component<Props, State> {
);
}
private layoutKeyToLabel = (key: string) => {
return layoutKeyToLabel(key, this.props.plugins);
};
public render() {
const theme = themeStyle(this.props.themeId);
const style = {
@@ -746,6 +770,7 @@ class MainScreenComponent extends React.Component<Props, State> {
onResize={this.resizableLayout_resize}
onMoveButtonClick={this.resizableLayout_moveButtonClick}
renderItem={this.resizableLayout_renderItem}
layoutKeyToLabel={this.layoutKeyToLabel}
moveMode={this.props.layoutMoveMode}
moveModeMessage={_('Use the arrows to move the layout items. Press "Escape" to exit.')}
/>

View File

@@ -11,6 +11,7 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import KvStore from '@joplin/lib/services/KvStore';
import ShareService from '@joplin/lib/services/share/ShareService';
import LabelledPasswordInput from '../PasswordInput/LabelledPasswordInput';
import shim from '@joplin/lib/shim';
interface Props {
themeId: number;
@@ -80,7 +81,7 @@ export default function(props: Props) {
void reg.waitForSyncFinishedThenSync();
onClose();
} catch (error) {
alert(error.message);
void shim.showErrorDialog(error.message);
} finally {
setUpdatingPassword(false);
}

View File

@@ -165,6 +165,7 @@ interface Props {
showNoteCounts: boolean;
uncompletedTodosOnTop: boolean;
showCompletedTodos: boolean;
tabMovesFocus: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
pluginMenuItems: any[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -256,6 +257,7 @@ function useMenuStates(menu: any, props: Props) {
menuItemSetChecked('showNoteCounts', props.showNoteCounts);
menuItemSetChecked('uncompletedTodosOnTop', props.uncompletedTodosOnTop);
menuItemSetChecked('showCompletedTodos', props.showCompletedTodos);
menuItemSetChecked('toggleTabMovesFocus', props.tabMovesFocus);
}
timeoutId = setTimeout(scheduleUpdate, 150);
@@ -276,6 +278,7 @@ function useMenuStates(menu: any, props: Props) {
props['notes.sortOrder.reverse'],
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
props['folders.sortOrder.reverse'],
props.tabMovesFocus,
props.noteListRendererId,
props.showNoteCounts,
props.uncompletedTodosOnTop,
@@ -476,6 +479,7 @@ function useMenu(props: Props) {
menuItemDic.focusElementNoteList,
menuItemDic.focusElementNoteTitle,
menuItemDic.focusElementNoteBody,
menuItemDic.focusElementToolbar,
];
const importItems = [];
@@ -824,6 +828,12 @@ function useMenu(props: Props) {
},
},
separator(),
{
...menuItemDic['toggleTabMovesFocus'],
label: Setting.settingMetadata('editor.tabMovesFocus').label(),
type: 'checkbox',
},
separator(),
{
label: _('Actual Size'),
click: () => {
@@ -877,7 +887,9 @@ function useMenu(props: Props) {
note: {
label: _('&Note'),
submenu: [
menuItemDic.openNoteInNewWindow,
menuItemDic.toggleExternalEditing,
separator(),
menuItemDic.setTags,
menuItemDic.showShareNoteDialog,
separator(),
@@ -987,6 +999,7 @@ function useMenu(props: Props) {
rootMenus.go.submenu.push(menuItemDic.gotoAnything);
rootMenus.tools.submenu.push(menuItemDic.commandPalette);
rootMenus.tools.submenu.push(menuItemDic.linkToNote);
rootMenus.tools.submenu.push(menuItemDic.openMasterPasswordDialog);
for (const view of props.pluginMenuItems) {
@@ -1143,6 +1156,7 @@ const mapStateToProps = (state: AppState): Partial<Props> => {
['folders.sortOrder.field']: state.settings['folders.sortOrder.field'],
['notes.sortOrder.reverse']: state.settings['notes.sortOrder.reverse'],
['folders.sortOrder.reverse']: state.settings['folders.sortOrder.reverse'],
tabMovesFocus: state.settings['editor.tabMovesFocus'],
pluginSettings: state.settings['plugins.states'],
showNoteCounts: state.settings.showNoteCounts,
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,

View File

@@ -32,6 +32,14 @@ function styles_(props: MultiNoteActionsProps) {
display: 'flex',
flexDirection: 'column',
},
divider: {
borderTopWidth: 1,
borderTopStyle: 'solid',
borderTopColor: theme.dividerColor,
width: '100%',
height: 1,
marginBottom: 10,
},
button: {
...theme.buttonStyle,
marginBottom: 10,
@@ -68,11 +76,17 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
const item = menuItems[i];
if (!item.enabled) continue;
itemComps.push(
<button key={item.label} style={styles.button} onClick={() => multiNotesButton_click(item)}>
{item.label}
</button>,
);
if (item.type === 'separator') {
itemComps.push(
<div key={`divider${i}`} style={styles.divider}/>,
);
} else {
itemComps.push(
<button key={item.label} style={styles.button} onClick={() => multiNotesButton_click(item)}>
{item.label}
</button>,
);
}
}
return (

View File

@@ -1,50 +1,66 @@
import * as React from 'react';
const { connect } = require('react-redux');
import { connect } from 'react-redux';
import Setting from '@joplin/lib/models/Setting';
import { AppState, AppStateRoute } from '../app.reducer';
import bridge from '../services/bridge';
import { useContext, useEffect, useRef } from 'react';
import { useContext, useEffect, useMemo, useRef } from 'react';
import { WindowIdContext } from './NewWindowOrIFrame';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of code from before rule was applied
type ScreenProps = any;
interface AppScreen {
screen: React.ComponentType<ScreenProps>;
title?: ()=> string;
}
interface Props {
route: AppStateRoute;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
screens: Record<string, any>;
screens: Record<string, AppScreen>;
style: React.CSSProperties;
className?: string;
}
const NavigatorComponent: React.FC<Props> = props => {
const useWindowTitleManager = (screenInfo: AppScreen) => {
const windowTitle = useMemo(() => {
const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : '';
const windowTitle = [`Joplin${devMarker}`];
if (screenInfo?.title) {
windowTitle.push(screenInfo.title());
}
return windowTitle.join(' - ');
}, [screenInfo]);
const windowId = useContext(WindowIdContext);
useEffect(() => {
bridge().windowById(windowId)?.setTitle(windowTitle);
}, [windowTitle, windowId]);
};
const useWindowRefocusManager = (route: AppStateRoute) => {
const windowId = useContext(WindowIdContext);
const route = props.route;
const screenInfo = props.screens[route?.routeName];
const screensRef = useRef(props.screens);
screensRef.current = props.screens;
const prevRoute = useRef<AppStateRoute|null>(null);
const prevRouteName = useRef<string|null>(null);
const routeName = route?.routeName;
useEffect(() => {
const routeName = route?.routeName;
if (route) {
const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : '';
const windowTitle = [`Joplin${devMarker}`];
if (screenInfo.title) {
windowTitle.push(screenInfo.title());
}
bridge().windowById(windowId)?.setTitle(windowTitle.join(' - '));
}
// When a navigation happens in an unfocused window, show the window to the user.
// This might happen if, for example, a secondary window triggers a navigation in
// the main window.
if (routeName && routeName !== prevRoute.current?.routeName) {
if (routeName && routeName !== prevRouteName.current) {
bridge().switchToWindow(windowId);
}
prevRoute.current = route;
}, [route, screenInfo, windowId]);
prevRouteName.current = routeName;
}, [routeName, windowId]);
};
const NavigatorComponent: React.FC<Props> = props => {
const route = props.route;
const screenInfo = props.screens[route?.routeName];
useWindowTitleManager(screenInfo);
useWindowRefocusManager(route);
if (!route) throw new Error('Route must not be null');

View File

@@ -32,7 +32,9 @@ function Toolbar(props: ToolbarProps) {
const styles = styles_(props);
return (
<ToolbarBase
id="CodeMirrorToolbar"
style={styles.root}
scrollable={true}
items={props.toolbarButtonInfos}
disabled={!!props.disabled}
aria-label={_('Editor actions')}

View File

@@ -2,7 +2,7 @@ import * as React from 'react';
import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHandle, ForwardedRef, useContext } from 'react';
// eslint-disable-next-line no-unused-vars
import { EditorCommand, MarkupToHtmlOptions, NoteBodyEditorProps, NoteBodyEditorRef } from '../../../utils/types';
import { EditorCommand, NoteBodyEditorProps, NoteBodyEditorRef } from '../../../utils/types';
import { commandAttachFileToBody, getResourcesFromPasteEvent } from '../../../utils/resourceHandling';
import { ScrollOptions, ScrollOptionTypes } from '../../../utils/types';
import { CommandValue } from '../../../utils/types';
@@ -34,6 +34,7 @@ import useWebviewIpcMessage from '../utils/useWebviewIpcMessage';
import useEditorSearchHandler from '../utils/useEditorSearchHandler';
import { focus } from '@joplin/lib/utils/focusHandler';
import { WindowIdContext } from '../../../../NewWindowOrIFrame';
import { MarkupToHtmlOptions } from '../../../../hooks/useMarkupToHtml';
function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions {
return { ...override };
@@ -48,7 +49,10 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
const [webviewReady, setWebviewReady] = useState(false);
const editorRef = useRef(null);
const rootRef = useRef(null);
const [editorRoot, setEditorRoot] = useState<HTMLDivElement|null>(null);
const rootRef = useRef<HTMLDivElement|null>(null);
rootRef.current = editorRoot;
const webviewRef = useRef(null);
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
const props_onChangeRef = useRef<Function>(null);
@@ -410,6 +414,8 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
}, [styles.editor.codeMirrorTheme]);
useEffect(() => {
if (!editorRoot) return () => {};
const theme = themeStyle(props.themeId);
// Selection in dark mode is hard to see so make it brighter.
@@ -431,10 +437,11 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
max-width: ${props.contentMaxWidth}px !important;
` : '';
const element = document.createElement('style');
const ownerDoc = editorRoot.ownerDocument;
const element = ownerDoc.createElement('style');
element.setAttribute('id', 'codemirrorStyle');
document.head.appendChild(element);
element.appendChild(document.createTextNode(`
ownerDoc.head.appendChild(element);
element.appendChild(ownerDoc.createTextNode(`
/* These must be important to prevent the codemirror defaults from taking over*/
.CodeMirror {
font-family: monospace;
@@ -449,6 +456,11 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
line-height: ${theme.lineHeight} !important;
}
.CodeMirror-code:focus-visible {
/* Avoid showing additional focus-visible decoration */
outline: none;
}
.CodeMirror-lines {
/* This is used to enable the scroll-past end behaviour. The same height should */
/* be applied to the viewer. */
@@ -591,10 +603,9 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
`));
return () => {
document.head.removeChild(element);
ownerDoc.head.removeChild(element);
};
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [props.themeId, props.contentMaxWidth]);
}, [props.themeId, props.contentMaxWidth, props.fontSize, editorRoot]);
const webview_domReady = useCallback(() => {
setWebviewReady(true);
@@ -774,7 +785,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
const windowId = useContext(WindowIdContext);
return (
<ErrorBoundary message="The text editor encountered a fatal error and could not continue. The error might be due to a plugin, so please try to disable some of them and try again.">
<div style={styles.root} ref={rootRef}>
<div style={styles.root} ref={setEditorRoot}>
<div style={styles.rowToolbar}>
<Toolbar themeId={props.themeId} windowId={windowId}/>
{props.noteToolbar}

View File

@@ -358,6 +358,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
return {
language: isHTMLNote ? EditorLanguageType.Html : EditorLanguageType.Markdown,
readOnly: props.disabled,
markdownMarkEnabled: Setting.value('markdown.plugin.mark'),
katexEnabled: Setting.value('markdown.plugin.katex'),
themeData: {
...styles.globalTheme,
@@ -372,18 +373,23 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
spellcheckEnabled: Setting.value('editor.spellcheckBeta'),
keymap: keyboardMode,
indentWithTabs: true,
tabMovesFocus: props.tabMovesFocus,
editorLabel: _('Markdown editor'),
};
}, [
props.contentMarkupLanguage, props.disabled, props.keyboardMode, styles.globalTheme,
props.tabMovesFocus,
]);
// Update the editor's value
useEffect(() => {
if (editorRef.current?.updateBody(props.content)) {
// Include the noteId in the update props to give plugins access
// to the current note ID.
const updateProps = { noteId: props.noteId };
if (editorRef.current?.updateBody(props.content, updateProps)) {
editorRef.current?.clearHistory();
}
}, [props.content]);
}, [props.content, props.noteId]);
const renderEditor = () => {
return (
@@ -391,6 +397,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
<Editor
style={styles.editor}
initialText={props.content}
initialNoteId={props.noteId}
ref={editorRef}
settings={editorSettings}
pluginStates={props.plugins}

View File

@@ -19,12 +19,11 @@ import { MarkupLanguage, MarkupToHtml } from '@joplin/renderer';
import BaseItem from '@joplin/lib/models/BaseItem';
import setupToolbarButtons from './utils/setupToolbarButtons';
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
import openEditDialog from './utils/openEditDialog';
import { themeStyle } from '@joplin/lib/theme';
import { loadScript } from '../../../utils/loadScript';
import bridge from '../../../../services/bridge';
import { TinyMceEditorEvents } from './utils/types';
import type { Editor } from 'tinymce';
import type { Editor, EditorEvent } from 'tinymce';
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
import shouldPasteResources from './utils/shouldPasteResources';
import lightTheme from '@joplin/lib/themes/light';
@@ -42,6 +41,8 @@ import { hasProtocol } from '@joplin/utils/url';
import useTabIndenter from './utils/useTabIndenter';
import useKeyboardRefocusHandler from './utils/useKeyboardRefocusHandler';
import useDocument from '../../../hooks/useDocument';
import useEditDialog from './utils/useEditDialog';
import useEditDialogEventListeners from './utils/useEditDialogEventListeners';
const logger = Logger.create('TinyMCE');
@@ -72,14 +73,6 @@ function awfulInitHack(html: string): string {
return html === '<div id="rendered-md"></div>' ? '<div id="rendered-md"><p></p></div>' : html;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function findEditableContainer(node: any): any {
while (node) {
if (node.classList && node.classList.contains('joplin-editable')) return node;
node = node.parentNode;
}
return null;
}
let markupToHtml_ = new MarkupToHtml();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -130,19 +123,23 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const { scrollToPercent } = useScroll({ editor, onScroll: props.onScroll });
usePluginServiceRegistration(ref);
useContextMenu(editor, props.plugins, props.dispatch, props.htmlToMarkdown, props.markupToHtml);
useTabIndenter(editor);
useKeyboardRefocusHandler(editor);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const dispatchDidUpdate = (editor: any) => {
const dispatchDidUpdate = useCallback((editor: Editor) => {
if (dispatchDidUpdateIID_) shim.clearTimeout(dispatchDidUpdateIID_);
dispatchDidUpdateIID_ = shim.setTimeout(() => {
dispatchDidUpdateIID_ = null;
if (editor && editor.getDoc()) editor.getDoc().dispatchEvent(new Event('joplin-noteDidUpdate'));
}, 10);
};
}, []);
const editDialog = useEditDialog({ editor, markupToHtml, dispatchDidUpdate });
const editDialogRef = useRef(editDialog);
editDialogRef.current = editDialog;
useEditDialogEventListeners(editor, editDialog);
usePluginServiceRegistration(ref);
useContextMenu(editor, props.plugins, props.dispatch, props.htmlToMarkdown, props.markupToHtml, editDialog);
useTabIndenter(editor, !props.tabMovesFocus);
useKeyboardRefocusHandler(editor);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const insertResourcesIntoContent = useCallback(async (filePaths: string[] = null, options: any = null) => {
@@ -179,7 +176,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
props.onMessage({ channel: href });
}
}
}, [editor, props.onMessage]);
}, [editor, props.onMessage, dispatchDidUpdate]);
useImperativeHandle(ref, () => {
return {
@@ -412,9 +409,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
element.setAttribute('id', 'tinyMceStyle');
editorContainerDom.head.appendChild(element);
element.appendChild(editorContainerDom.createTextNode(`
.joplin-tinymce .tox-editor-header {
padding-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
padding-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
.joplin-tinymce .tox-editor-header.tox-editor-header {
margin-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
margin-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
padding: 0;
box-shadow: none;
}
.tox .tox-toolbar,
@@ -434,7 +433,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
.tox .tox-dialog__body-content,
.tox .tox-collection__item {
.tox .tox-collection__item,
.tox .tox-insert-table-picker__label {
color: ${theme.color};
}
@@ -473,7 +473,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
*/
.tox .tox-dialog textarea {
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace !important;
}
.tox .tox-dialog-wrap__backdrop {
@@ -498,6 +498,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
.tox .tox-toolbar-label {
color: ${theme.color3} !important;
fill: ${theme.color3} !important;
background: transparent;
}
.tox .tox-statusbar a,
@@ -524,6 +525,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
.tox .tox-split-button:focus {
background-color: ${theme.backgroundColor3}
}
.tox .tox-tbtn:focus-visible,
.tox .tox-split-button:focus-visible {
background-color: ${theme.backgroundColorHover3}
}
.tox .tox-tbtn:hover,
.tox .tox-menu button:hover > svg {
@@ -560,6 +566,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
.tox .tox-split-button:hover {
box-shadow: none;
}
/* Decrease the spacing between groups */
.tox .tox-toolbar__group {
padding-left: 7px;
padding-right: 7px;
}
.tox-tinymce,
.tox .tox-toolbar__group,
@@ -605,6 +617,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
background: none;
background-color: ${theme.backgroundColor3} !important;
}
.tox .tox-tbtn,
.tox .tox-tbtn button,
.tox .tox-split-button,
.tox .tox-split-button button {
margin: 0 !important;
}
`));
return () => {
@@ -628,7 +647,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
useEffect(() => {
if (!editor) return;
editor.setMode(props.disabled ? 'readonly' : 'design');
editor.mode.set(props.disabled ? 'readonly' : 'design');
}, [editor, props.disabled]);
// -----------------------------------------------------------------------------------------
@@ -661,7 +680,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
// we create small groups of just one button towards the end.
const toolbar = [
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', '|',
'joplinInsert', 'joplinSup', 'joplinSub', 'forecolor', '|',
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
'bullist', 'numlist', 'joplinChecklist', '|',
'h1', 'h2', 'h3', '|',
@@ -675,14 +695,19 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const containerWindow = editorContainerDom.defaultView as any;
const editors = await containerWindow.tinymce.init({
selector: `#${editorContainer.id}`,
// Ensures that the "Premium plugins" toolbar option is disabled. See
// https://www.tiny.cloud/docs/tinymce/latest/editor-premium-upgrade-promotion/
promotion: false,
width: '100%',
body_class: 'jop-tinymce',
height: '100%',
resize: false,
highlight_on_focus: false,
icons: 'Joplin',
icons_url: 'gui/NoteEditor/NoteBody/TinyMCE/icons.js',
plugins: 'noneditable link joplinLists hr searchreplace codesample table',
noneditable_noneditable_class: 'joplin-editable', // Can be a regex too
plugins: 'link joplinLists searchreplace codesample table',
noneditable_class: 'joplin-editable', // Can be a regex too
iframe_aria_text: _('Rich Text editor. Press Escape then Tab to escape focus.'),
// #p: Pad empty paragraphs with &nbsp; to prevent them from being removed.
@@ -694,7 +719,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
relative_urls: false,
branding: false,
statusbar: false,
target_list: false,
link_target_list: false,
// Handle the first table row as table header.
// https://www.tiny.cloud/docs/plugins/table/#table_header_type
table_header_type: 'sectionCells',
@@ -704,13 +729,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
contextmenu: false,
browser_spellcheck: true,
// Work around an issue where images with a base64 SVG data URL would be broken.
//
// See https://github.com/tinymce/tinymce/issues/3864
//
// This was fixed in TinyMCE 6.1, so remove it when we upgrade.
images_dataimg_filter: (img: HTMLImageElement) => !img.src.startsWith('data:'),
formats: {
joplinHighlight: { inline: 'mark', remove: 'all' },
joplinStrikethrough: { inline: 's', remove: 'all' },
@@ -739,7 +757,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
tooltip: _('Code Block'),
icon: 'code-sample',
onAction: async function() {
openEditDialog(editor, markupToHtml, dispatchDidUpdate, null);
editDialogRef.current.editNew();
},
});
@@ -747,14 +765,15 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
tooltip: _('Inline Code'),
icon: 'sourcecode',
onAction: function() {
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' });
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' } as any);
},
onSetup: function(api) {
api.setActive(editor.formatter.match('code'));
const unbind = editor.formatter.formatChanged('code', api.setActive).unbind;
const handle = editor.formatter.formatChanged('code', active => api.setActive(active));
return function() {
if (unbind) unbind();
handle?.unbind();
};
},
});
@@ -805,11 +824,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
editor.addShortcut('Meta+Shift+9', '', () => editor.execCommand('InsertJoplinChecklist'));
// TODO: remove event on unmount?
editor.on('DblClick', (event) => {
const editable = findEditableContainer(event.target);
if (editable) openEditDialog(editor, markupToHtml, dispatchDidUpdate, editable);
});
editor.on('drop', (event) => {
// Prevent the message "Dropped file type is not supported" from showing up.
// It was added in TinyMCE 5.4 and doesn't apply since we do support
@@ -1035,6 +1049,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const allAssetsOptions: NoteStyleOptions = {
contentMaxWidthTarget: '.mce-content-body',
scrollbarSize: props.scrollbarSize,
themeId: props.contentMarkupLanguage === MarkupLanguage.Html ? 1 : null,
whiteBackgroundNoteRendering: props.whiteBackgroundNoteRendering,
};
@@ -1051,7 +1066,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
cancelled = true;
};
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [editor, props.themeId, props.markupToHtml, props.allAssets, props.content, props.resourceInfos, props.contentKey, props.contentMarkupLanguage, props.whiteBackgroundNoteRendering]);
}, [editor, props.themeId, props.scrollbarSize, props.markupToHtml, props.allAssets, props.content, props.resourceInfos, props.contentKey, props.contentMarkupLanguage, props.whiteBackgroundNoteRendering]);
useEffect(() => {
if (!editor) return () => {};
@@ -1205,9 +1220,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onSetAttrib = (event: any) => {
const onSetAttrib = (event: EditorEvent<any>) => {
// Dispatch onChange when a link is edited
if (event.attrElm[0].nodeName === 'A') {
const target = Array.isArray(event.attrElm) ? event.attrElm[0] : event.attrElm;
if (target.nodeName === 'A') {
if (event.attrName === 'title' || event.attrName === 'href' || event.attrName === 'rel') {
onChangeHandler();
}
@@ -1336,7 +1352,9 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
editor.on(TinyMceEditorEvents.KeyUp, onKeyUp);
editor.on(TinyMceEditorEvents.KeyDown, onKeyDown);
editor.on(TinyMceEditorEvents.KeyPress, onKeypress);
editor.on(TinyMceEditorEvents.Paste, onPaste);
// Passing `true` adds the listener to the front of the listener list.
// This allows overriding TinyMCE's built-in paste handler with .preventDefault.
editor.on(TinyMceEditorEvents.Paste, onPaste, true);
editor.on(TinyMceEditorEvents.PasteAsText, onPasteAsText);
editor.on(TinyMceEditorEvents.Copy, onCopy);
// `compositionend` means that a user has finished entering a Chinese
@@ -1379,7 +1397,17 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
useEffect(() => {
return () => {
if (editorRef.current) editorRef.current.remove();
if (!editorRef.current) return;
const ownerDocument = editorRef.current.getContainer().ownerDocument;
const parentWindow = ownerDocument.defaultView;
// Calling .remove after the parent window is closed throws an Error
// related to DOM API access. Since closing the window also removes the editor,
// it shouldn't be necessary to call .remove in this case:
if (parentWindow) {
editorRef.current.remove();
}
};
}, []);

View File

@@ -886,7 +886,7 @@
var parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListRootElm(editor, elm));
return parentLi ? parentLi : elm;
});
return DomQuery.unique(listItemsElms);
return [...new Set(listItemsElms)];
};
var getSelectedListItems = function (editor) {
var selectedBlocks = editor.selection.getSelectedBlocks();
@@ -919,7 +919,7 @@
var listRoots = map(lists, function (list) {
return findLastParentListNode(editor, list).getOr(list);
});
return DomQuery.unique(listRoots);
return [...new Set(listRoots)];
};
var shouldIndentOnTab = function (editor) {
@@ -2119,8 +2119,7 @@
};
var register$1 = function (editor) {
var hasPlugin = function (editor, plugin) {
var plugins = editor.settings.plugins ? editor.settings.plugins : '';
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
return editor.hasPlugin(plugin);
};
var _ = getLocalizationFunction(editor);
var exec = function (command) {

View File

@@ -0,0 +1,71 @@
import Setting from '@joplin/lib/models/Setting';
import { focus } from '@joplin/lib/utils/focusHandler';
const taboverride = require('taboverride');
export interface TextAreaTabHandler {
remove(): void;
}
const createTextAreaKeyListeners = () => {
let hasListeners = true;
// Selectively enable/disable taboverride based on settings -- remove taboverride
// when pressing tab if tab is expected to move focus.
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Tab') {
if (Setting.value('editor.tabMovesFocus')) {
taboverride.utils.removeListeners(event.currentTarget);
hasListeners = false;
} else {
// Prevent the default focus-changing behavior
event.preventDefault();
requestAnimationFrame(() => {
focus('openEditDialog::dialogTextArea_keyDown', event.target);
});
}
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Tab' && !hasListeners) {
taboverride.utils.addListeners(event.currentTarget);
hasListeners = true;
}
};
return { onKeyDown, onKeyUp };
};
// Allows pressing tab in a textarea to input an actual tab (instead of changing focus)
// taboverride will take care of actually inserting the tab character, while the keydown
// event listener will override the default behaviour, which is to focus the next field.
const enableTextAreaTab = (textAreas: HTMLTextAreaElement[]): TextAreaTabHandler => {
type RemoveCallback = ()=> void;
const removeCallbacks: RemoveCallback[] = [];
for (const textArea of textAreas) {
const { onKeyDown, onKeyUp } = createTextAreaKeyListeners();
textArea.addEventListener('keydown', onKeyDown);
textArea.addEventListener('keyup', onKeyUp);
// Enable/disable taboverride **after** the listeners above.
// The custom keyup/keydown need to have higher precedence.
taboverride.set(textArea, true);
removeCallbacks.push(() => {
taboverride.set(textArea, false);
textArea.removeEventListener('keyup', onKeyUp);
textArea.removeEventListener('keydown', onKeyDown);
});
}
return {
remove: () => {
for (const callback of removeCallbacks) {
callback();
}
},
};
};
export default enableTextAreaTab;

View File

@@ -60,13 +60,12 @@ export default function(editor: any) {
});
}
const items: string[] = definitions.filter(d => !!d.grouped).map(d => d.name);
// Additional built-in buttons to show in the formatting sub-menu:
items.push('forecolor');
editor.ui.registry.addGroupToolbarButton('formattingExtras', {
icon: 'image-options',
items: items.join(' '),
});
// Old code to format a group of buttons into a dropdown
// const items: string[] = definitions.filter(d => !!d.grouped).map(d => d.name);
// items.push('forecolor');
// editor.ui.registry.addGroupToolbarButton('formattingExtras', {
// icon: 'image-options',
// tooltip: _('Formatting'),
// items: items.join(' '),
// });
}

View File

@@ -1,3 +1,5 @@
import type { Editor } from 'tinymce';
// eslint-disable-next-line import/prefer-default-export
export enum TinyMceEditorEvents {
KeyUp = 'keyup',
@@ -14,3 +16,5 @@ export enum TinyMceEditorEvents {
ExecCommand = 'ExecCommand',
SetAttrib = 'SetAttrib',
}
export type DispatchDidUpdateCallback = (editor: Editor)=> void;

View File

@@ -8,12 +8,16 @@ import { menuItems } from '../../../utils/contextMenu';
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import CommandService from '@joplin/lib/services/CommandService';
import Setting from '@joplin/lib/models/Setting';
import type { Event as ElectronEvent } from 'electron';
import type { Event as ElectronEvent, MenuItemConstructorOptions } from 'electron';
import Resource from '@joplin/lib/models/Resource';
import { TinyMceEditorEvents } from './types';
import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from '../../../utils/types';
import { Editor } from 'tinymce';
import { EditDialogControl } from './useEditDialog';
import { Dispatch } from 'redux';
import { _ } from '@joplin/lib/locale';
import type { MenuItem as MenuItemType } from 'electron';
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
@@ -52,23 +56,14 @@ interface ContextMenuActionOptions {
const contextMenuActionOptions: ContextMenuActionOptions = { current: null };
// 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 function(editor: Editor, plugins: PluginStates, dispatch: Function, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) {
export default function(editor: Editor, plugins: PluginStates, dispatch: Dispatch, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler, editDialog: EditDialogControl) {
useEffect(() => {
if (!editor) return () => {};
const contextMenuItems = menuItems(dispatch, htmlToMd, mdToHtml);
const targetWindow = bridge().activeWindow();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function onContextMenu(event: ElectronEvent, params: any) {
const element = contextMenuElement(editor, params.x, params.y);
if (!element) return;
event.preventDefault();
const menu = new Menu();
const makeMainMenuItems = (element: Element) => {
let itemType: ContextMenuItemType = ContextMenuItemType.None;
let resourceId = '';
let linkToCopy = null;
@@ -103,29 +98,64 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Functio
mdToHtml,
};
const result = [];
for (const itemName in contextMenuItems) {
const item = contextMenuItems[itemName];
if (!item.isActive(itemType, contextMenuActionOptions.current)) continue;
menu.append(new MenuItem({
result.push(new MenuItem({
label: item.label,
click: () => {
item.onAction(contextMenuActionOptions.current);
},
}));
}
return result;
};
const makeEditableMenuItems = (element: Element) => {
if (editDialog.isEditable(element)) {
return [
new MenuItem({
type: 'normal',
label: _('Edit'),
click: () => {
editDialog.editExisting(element);
},
}),
new MenuItem({ type: 'separator' }),
];
}
return [];
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function onContextMenu(event: ElectronEvent, params: any) {
const element = contextMenuElement(editor, params.x, params.y);
if (!element) return;
event.preventDefault();
const menu = new Menu();
const menuItems: MenuItemType[] = [];
const toMenuItems = (specs: MenuItemConstructorOptions[]) => {
return specs.map(spec => new MenuItem(spec));
};
menuItems.push(...makeEditableMenuItems(element));
menuItems.push(...makeMainMenuItems(element));
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(params.misspelledWord, params.dictionarySuggestions);
menuItems.push(
...toMenuItems(spellCheckerMenuItems),
);
menuItems.push(
...toMenuItems(menuUtils.pluginContextMenuItems(plugins, MenuItemLocation.EditorContextMenu)),
);
for (const item of spellCheckerMenuItems) {
menu.append(new MenuItem(item));
for (const item of menuItems) {
menu.append(item);
}
for (const item of menuUtils.pluginContextMenuItems(plugins, MenuItemLocation.EditorContextMenu)) {
menu.append(new MenuItem(item));
}
menu.popup({ window: targetWindow });
}
@@ -136,5 +166,5 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Functio
targetWindow.webContents.off('context-menu', onContextMenu);
}
};
}, [editor, plugins, dispatch, htmlToMd, mdToHtml]);
}, [editor, plugins, dispatch, htmlToMd, mdToHtml, editDialog]);
}

View File

@@ -1,43 +1,32 @@
import { RefObject, useMemo } from 'react';
import type { Editor } from 'tinymce';
import { DispatchDidUpdateCallback, TinyMceEditorEvents } from './types';
import { MarkupToHtmlHandler } from '../../../utils/types';
import { _ } from '@joplin/lib/locale';
import enableTextAreaTab, { TextAreaTabHandler } from './enableTextAreaTab';
import { MarkupToHtml } from '@joplin/renderer';
import { TinyMceEditorEvents } from './types';
import { focus } from '@joplin/lib/utils/focusHandler';
const taboverride = require('taboverride');
interface Props {
editor: Editor;
markupToHtml: RefObject<MarkupToHtmlHandler>;
dispatchDidUpdate: DispatchDidUpdateCallback;
}
export interface EditDialogControl {
editNew: ()=> void;
editExisting: (elementInEditable: Node)=> void;
isEditable: (element: Node)=> boolean;
}
interface SourceInfo {
openCharacters: string;
closeCharacters: string;
content: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
node: any;
node: Element;
language: string;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function dialogTextArea_keyDown(event: any) {
if (event.key === 'Tab') {
window.requestAnimationFrame(() => focus('openEditDialog::dialogTextArea_keyDown', event.target));
}
}
// Allows pressing tab in a textarea to input an actual tab (instead of changing focus)
// taboverride will take care of actually inserting the tab character, while the keydown
// event listener will override the default behaviour, which is to focus the next field.
function enableTextAreaTab(enable: boolean) {
const textAreas = document.getElementsByClassName('tox-textarea');
for (const textArea of textAreas) {
taboverride.set(textArea, enable);
if (enable) {
textArea.addEventListener('keydown', dialogTextArea_keyDown);
} else {
textArea.removeEventListener('keydown', dialogTextArea_keyDown);
}
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function findBlockSource(node: any): SourceInfo {
function findBlockSource(node: Element): SourceInfo {
const sources = node.getElementsByClassName('joplin-source');
if (!sources.length) throw new Error('No source for node');
const source = sources[0];
@@ -81,9 +70,14 @@ function editableInnerHtml(html: string): string {
return editable[0].innerHTML;
}
// 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 function openEditDialog(editor: any, markupToHtml: any, dispatchDidUpdate: Function, editable: any) {
function openEditDialog(
editor: Editor,
markupToHtml: RefObject<MarkupToHtmlHandler>,
dispatchDidUpdate: DispatchDidUpdateCallback,
editable: Element,
) {
const source = editable ? findBlockSource(editable) : newBlockSource();
let tabHandler: TextAreaTabHandler|null = null;
editor.windowManager.open({
title: _('Edit'),
@@ -113,7 +107,7 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
dispatchDidUpdate(editor);
},
onClose: () => {
enableTextAreaTab(false);
tabHandler?.remove();
},
body: {
type: 'panel',
@@ -124,12 +118,11 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
label: 'Language',
// Katex is a special case with special opening/closing tags
// and we don't currently handle switching the language in this case.
disabled: source.language === 'katex',
enabled: source.language !== 'katex',
},
{
type: 'textarea',
name: 'codeTextArea',
value: source.content,
},
],
},
@@ -142,6 +135,40 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
});
window.requestAnimationFrame(() => {
enableTextAreaTab(true);
const containerDocument = editor.getContainer().ownerDocument;
const textAreas = containerDocument.querySelectorAll<HTMLTextAreaElement>('.tox-textarea');
tabHandler = enableTextAreaTab([...textAreas]);
});
}
const findEditableContainer = (node: Node) => {
if (node.nodeName.startsWith('#')) { // Not an element, e.g. #text
node = node.parentElement;
}
return (node as Element)?.closest('.joplin-editable');
};
const useEditDialog = ({
editor, markupToHtml, dispatchDidUpdate,
}: Props): EditDialogControl => {
return useMemo(() => {
const edit = (editable: Element|null) => {
openEditDialog(editor, markupToHtml, dispatchDidUpdate, editable);
};
return {
isEditable: element => !!findEditableContainer(element),
editExisting: (element: Node) => {
const editable = findEditableContainer(element);
if (editable) {
edit(editable);
}
},
editNew: () => {
edit(null);
},
};
}, [editor, markupToHtml, dispatchDidUpdate]);
};
export default useEditDialog;

View File

@@ -0,0 +1,34 @@
import { Editor } from 'tinymce';
import { EditDialogControl } from './useEditDialog';
import { useEffect } from 'react';
import { TinyMceEditorEvents } from './types';
const useEditDialogEventListeners = (editor: Editor|null, editDialog: EditDialogControl) => {
useEffect(() => {
if (!editor) return () => {};
const dblClickHandler = (event: Event) => {
editDialog.editExisting(event.target as Node);
};
const keyDownHandler = (event: KeyboardEvent) => {
const hasModifiers = event.shiftKey || event.altKey || event.ctrlKey || event.metaKey;
if (event.code === 'Enter' && !event.isComposing && !hasModifiers) {
const selection = editor.selection.getNode();
if (editDialog.isEditable(selection)) {
editDialog.editExisting(selection);
event.preventDefault();
}
}
};
editor.on(TinyMceEditorEvents.KeyDown, keyDownHandler);
editor.on('DblClick', dblClickHandler);
return () => {
editor.off(TinyMceEditorEvents.KeyDown, keyDownHandler);
editor.off('DblClick', dblClickHandler);
};
}, [editor, editDialog]);
};
export default useEditDialogEventListeners;

View File

@@ -1,9 +1,9 @@
import { useEffect } from 'react';
import type { Editor, EditorEvent } from 'tinymce';
const useTabIndenter = (editor: Editor) => {
const useTabIndenter = (editor: Editor, enabled: boolean) => {
useEffect(() => {
if (!editor) return () => {};
if (!editor || !enabled) return () => {};
const canChangeIndentation = () => {
const selectionElement = editor.selection.getNode();
@@ -70,7 +70,7 @@ const useTabIndenter = (editor: Editor) => {
return () => {
editor.off('keydown', eventHandler);
};
}, [editor]);
}, [editor, enabled]);
};
export default useTabIndenter;

View File

@@ -9,20 +9,18 @@ import useNoteSearchBar from './utils/useNoteSearchBar';
import useMessageHandler from './utils/useMessageHandler';
import useWindowCommandHandler from './utils/useWindowCommandHandler';
import useDropHandler from './utils/useDropHandler';
import useMarkupToHtml from './utils/useMarkupToHtml';
import useMarkupToHtml from '../hooks/useMarkupToHtml';
import useFormNote, { OnLoadEvent, OnSetFormNote } from './utils/useFormNote';
import useEffectiveNoteId from './utils/useEffectiveNoteId';
import useFolder from './utils/useFolder';
import styles_ from './styles';
import { NoteEditorProps, FormNote, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
import CommandService from '@joplin/lib/services/CommandService';
import ToolbarButton from '../ToolbarButton/ToolbarButton';
import Button, { ButtonLevel } from '../Button/Button';
import eventManager, { EventName } from '@joplin/lib/eventManager';
import { AppState } from '../../app.reducer';
import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import { _, _n } from '@joplin/lib/locale';
import TagList from '../TagList';
import NoteTitleBar from './NoteTitle/NoteTitleBar';
import markupLanguageUtils from '@joplin/lib/utils/markupLanguageUtils';
import Setting from '@joplin/lib/models/Setting';
@@ -45,7 +43,6 @@ import PlainEditor from './NoteBody/PlainEditor/PlainEditor';
import CodeMirror6 from './NoteBody/CodeMirror/v6/CodeMirror';
import CodeMirror5 from './NoteBody/CodeMirror/v5/CodeMirror';
import { openItemById } from './utils/contextMenu';
import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginSettingValue';
import { MarkupLanguage } from '@joplin/renderer';
import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions';
import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks';
@@ -55,10 +52,10 @@ import Logger from '@joplin/utils/Logger';
import usePluginEditorView from './utils/usePluginEditorView';
import { stateUtils } from '@joplin/lib/reducer';
import { WindowIdContext } from '../NewWindowOrIFrame';
import { EditorActivationCheckFilterObject } from '@joplin/lib/services/plugins/api/types';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import WebviewController from '@joplin/lib/services/plugins/WebviewController';
import AsyncActionQueue, { IntervalType } from '@joplin/lib/AsyncActionQueue';
import EditorPluginHandler from '@joplin/lib/services/plugins/EditorPluginHandler';
import useResourceUnwatcher from './utils/useResourceUnwatcher';
import StatusBar from './StatusBar';
const debounce = require('debounce');
@@ -73,15 +70,6 @@ const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
const onDragOver: React.DragEventHandler = event => event.preventDefault();
let editorIdCounter = 0;
const makeNoteUpdateAction = (shownEditorViewIds: string[]) => {
return async () => {
for (const viewId of shownEditorViewIds) {
const controller = PluginService.instance().viewControllerByViewId(viewId) as WebviewController;
if (controller) controller.emitUpdate();
}
};
};
function NoteEditorContent(props: NoteEditorProps) {
const [showRevisions, setShowRevisions] = useState(false);
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
@@ -91,7 +79,10 @@ function NoteEditorContent(props: NoteEditorProps) {
const titleInputRef = useRef<HTMLInputElement>();
const isMountedRef = useRef(true);
const noteSearchBarRef = useRef(null);
const viewUpdateAsyncQueue_ = useRef<AsyncActionQueue>(new AsyncActionQueue(100, IntervalType.Fixed));
const editorPluginHandler = useMemo(() => {
return new EditorPluginHandler(PluginService.instance());
}, []);
const shownEditorViewIds = props['plugins.shownEditorViewIds'];
@@ -115,25 +106,15 @@ function NoteEditorContent(props: NoteEditorProps) {
const effectiveNoteId = useEffectiveNoteId(props);
useAsyncEffect(async (event) => {
useAsyncEffect(async (_event) => {
if (!props.startupPluginsLoaded) return;
let filterObject: EditorActivationCheckFilterObject = {
activatedEditors: [],
};
filterObject = await eventManager.filterEmit('editorActivationCheck', filterObject);
if (event.cancelled) return;
for (const editor of filterObject.activatedEditors) {
const controller = PluginService.instance().pluginById(editor.pluginId).viewController(editor.viewId) as WebviewController;
controller.setActive(editor.isActive);
}
}, [effectiveNoteId, props.startupPluginsLoaded]);
await editorPluginHandler.emitActivationCheck();
}, [effectiveNoteId, editorPluginHandler, props.startupPluginsLoaded]);
useEffect(() => {
if (!props.startupPluginsLoaded) return;
viewUpdateAsyncQueue_.current.push(makeNoteUpdateAction(shownEditorViewIds));
}, [effectiveNoteId, shownEditorViewIds, props.startupPluginsLoaded]);
editorPluginHandler.emitUpdate(shownEditorViewIds);
}, [effectiveNoteId, editorPluginHandler, shownEditorViewIds, props.startupPluginsLoaded]);
const { editorPlugin, editorView } = usePluginEditorView(props.plugins, shownEditorViewIds);
const builtInEditorVisible = !editorPlugin;
@@ -179,7 +160,7 @@ function NoteEditorContent(props: NoteEditorProps) {
whiteBackgroundNoteRendering,
customCss: props.customCss,
plugins: props.plugins,
settingValue: getPluginSettingValue,
scrollbarSize: props.scrollbarSize,
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -199,9 +180,10 @@ function NoteEditorContent(props: NoteEditorProps) {
return markupToHtml.allAssets(markupLanguage, theme, {
contentMaxWidth: props.contentMaxWidth,
contentMaxWidthTarget: options.contentMaxWidthTarget,
scrollbarSize: props.scrollbarSize,
whiteBackgroundNoteRendering: options.whiteBackgroundNoteRendering,
});
}, [props.themeId, props.customCss, props.contentMaxWidth]);
}, [props.themeId, props.scrollbarSize, props.customCss, props.contentMaxWidth]);
const handleProvisionalFlag = useCallback(() => {
if (props.isProvisional) {
@@ -358,6 +340,8 @@ function NoteEditorContent(props: NoteEditorProps) {
const windowId = useContext(WindowIdContext);
const onMessage = useMessageHandler(scrollWhenReady, clearScrollWhenReady, windowId, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
useResourceUnwatcher({ noteId: formNote.id, windowId });
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const externalEditWatcher_noteChange = useCallback((event: any) => {
if (event.id === formNote.id) {
@@ -437,24 +421,6 @@ function NoteEditorContent(props: NoteEditorProps) {
return <div style={emptyDivStyle} ref={containerRef}></div>;
}
function renderTagButton() {
return <ToolbarButton
themeId={props.themeId}
toolbarButtonInfo={props.setTagsToolbarButtonInfo}
/>;
}
function renderTagBar() {
const theme = themeStyle(props.themeId);
const noteIds = [formNote.id];
const instructions = <span onClick={() => { void CommandService.instance().execute('setTags', noteIds); }} style={{ ...theme.clickableTextStyle, whiteSpace: 'nowrap' }}>{_('Click to add tags...')}</span>;
const tagList = props.selectedNoteTags.length ? <TagList items={props.selectedNoteTags} /> : null;
return (
<div style={{ paddingLeft: 8, display: 'flex', flexDirection: 'row', alignItems: 'center' }}>{tagList}{instructions}</div>
);
}
const searchMarkers = useSearchMarkers(showLocalSearch, localSearchMarkerOptions, props.searches, props.selectedSearchId, props.highlightedWords);
const editorProps: NoteBodyEditorProps = {
@@ -485,12 +451,14 @@ function NoteEditorContent(props: NoteEditorProps) {
searchMarkers: searchMarkers,
visiblePanes: props.noteVisiblePanes || ['editor', 'viewer'],
keyboardMode: Setting.value('editor.keyboardMode'),
tabMovesFocus: props.tabMovesFocus,
locale: Setting.value('locale'),
onDrop: onDrop,
noteToolbarButtonInfos: props.toolbarButtonInfos,
plugins: props.plugins,
fontSize: Setting.value('style.editor.fontSize'),
contentMaxWidth: props.contentMaxWidth,
scrollbarSize: props.scrollbarSize,
isSafeMode: props.isSafeMode,
useCustomPdfViewer: props.useCustomPdfViewer,
// We need it to identify the context for which media is rendered.
@@ -535,6 +503,7 @@ function NoteEditorContent(props: NoteEditorProps) {
verticalAlign: 'top',
boxSizing: 'border-box',
flex: 1,
overflowX: 'scroll',
};
return (
@@ -685,10 +654,11 @@ function NoteEditorContent(props: NoteEditorProps) {
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{renderSearchBar()}
</div>
<div className="tag-bar" style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
{renderTagButton()}
{renderTagBar()}
</div>
<StatusBar
noteId={formNote.id}
setTagsToolbarButtonInfo={props.setTagsToolbarButtonInfo}
selectedNoteTags={props.selectedNoteTags}
/>
<WarningBanner bodyEditor={props.bodyEditor}/>
</div>
</div>
@@ -729,7 +699,7 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
selectedSearchId: windowState.selectedSearchId,
customCss: state.customViewerCss,
noteVisiblePanes: windowState.noteVisiblePanes,
watchedResources: state.watchedResources,
watchedResources: windowState.watchedResources,
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
pluginHtmlContents: state.pluginService.pluginHtmlContents,
@@ -744,6 +714,8 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
'setTags',
], whenClauseContext)[0] as ToolbarButtonInfo,
contentMaxWidth: state.settings['style.editor.contentMaxWidth'],
scrollbarSize: state.settings['style.scrollbarSize'],
tabMovesFocus: state.settings['editor.tabMovesFocus'],
isSafeMode: state.settings.isSafeMode,
useCustomPdfViewer: false,
syncUserId: state.settings['sync.userId'],

View File

@@ -0,0 +1,88 @@
import * as React from 'react';
import ToolbarButton from '../ToolbarButton/ToolbarButton';
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import CommandService from '@joplin/lib/services/CommandService';
import { themeStyle } from '@joplin/lib/theme';
import { AppState } from '../../app.reducer';
import { connect } from 'react-redux';
import { TagEntity } from '@joplin/lib/services/database/types';
import TagList from '../TagList';
import { _ } from '@joplin/lib/locale';
import { useCallback } from 'react';
import KeymapService from '@joplin/lib/services/KeymapService';
interface Props {
themeId: number;
tabMovesFocus: boolean;
noteId: string;
setTagsToolbarButtonInfo: ToolbarButtonInfo;
selectedNoteTags: TagEntity[];
}
interface StatusIndicatorProps {
commandName: string;
showWhenUnfocused: boolean;
// Even if not visible, [label] should reflect the current state
// of the indicator.
label: string;
}
const StatusIndicator: React.FC<StatusIndicatorProps> = props => {
const runCommand = useCallback(() => {
void CommandService.instance().execute(props.commandName);
}, [props.commandName]);
const keyshortcuts = KeymapService.instance().getAriaKeyShortcuts(props.commandName);
return <span
className={`status editor-status-indicator ${props.showWhenUnfocused ? '-show' : ''}`}
aria-live='polite'
>
<button
className='button'
aria-keyshortcuts={keyshortcuts}
onClick={runCommand}
>
{props.label}
</button>
</span>;
};
const StatusBar: React.FC<Props> = props => {
function renderTagButton() {
return <ToolbarButton
themeId={props.themeId}
toolbarButtonInfo={props.setTagsToolbarButtonInfo}
/>;
}
function renderTagBar() {
const theme = themeStyle(props.themeId);
const noteIds = [props.noteId];
const instructions = <span onClick={() => { void CommandService.instance().execute('setTags', noteIds); }} style={{ ...theme.clickableTextStyle, whiteSpace: 'nowrap' }}>{_('Click to add tags...')}</span>;
const tagList = props.selectedNoteTags.length ? <TagList items={props.selectedNoteTags} /> : null;
return <div className='tag-bar'>
{renderTagButton()}
<div className='content'>{tagList}{instructions}</div>
</div>;
}
const keyboardStatus = <StatusIndicator
commandName='toggleTabMovesFocus'
label={props.tabMovesFocus ? _('Tab moves focus') : _('Tab indents')}
showWhenUnfocused={props.tabMovesFocus}
/>;
return <div className='editor-status-bar'>
{renderTagBar()}
<div className='spacer'/>
{keyboardStatus}
</div>;
};
export default connect((state: AppState) => {
return {
themeId: state.settings.theme,
tabMovesFocus: state.settings['editor.tabMovesFocus'],
};
})(StatusBar);

View File

@@ -38,6 +38,7 @@ const incompatiblePluginIds = [
'ylc395.noteLinkSystem',
'outline',
'joplin.plugin.cmoptions',
'com.asdibiase.joplin-languagetool',
// cSpell:enable
];

View File

@@ -0,0 +1,36 @@
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { focus } from '@joplin/lib/utils/focusHandler';
import { WindowCommandDependencies } from '../utils/types';
export const declaration: CommandDeclaration = {
name: 'focusElementToolbar',
label: () => _('Toolbar'),
parentLabel: () => _('Focus'),
};
export const runtime = (dependencies: WindowCommandDependencies): CommandRuntime => {
return {
execute: async () => {
if (!dependencies || !dependencies.containerRef || !dependencies.containerRef.current) return;
const firstButtonOnRTEToolbar = dependencies.containerRef.current.querySelector(
'.tox-toolbar__group button',
);
if (firstButtonOnRTEToolbar) {
focus('focusElementToolbar', firstButtonOnRTEToolbar);
return;
}
const firstButtonOnMarkdownToolbar = dependencies.containerRef.current.querySelector(
'#CodeMirrorToolbar .button:not(.disabled)',
);
if (firstButtonOnMarkdownToolbar) {
focus('focusElementToolbar', firstButtonOnMarkdownToolbar);
}
},
};
};

View File

@@ -1,6 +1,7 @@
// AUTO-GENERATED using `gulp buildScriptIndexes`
import * as focusElementNoteBody from './focusElementNoteBody';
import * as focusElementNoteTitle from './focusElementNoteTitle';
import * as focusElementToolbar from './focusElementToolbar';
import * as pasteAsText from './pasteAsText';
import * as showLocalSearch from './showLocalSearch';
import * as showRevisions from './showRevisions';
@@ -8,6 +9,7 @@ import * as showRevisions from './showRevisions';
const index: any[] = [
focusElementNoteBody,
focusElementNoteTitle,
focusElementToolbar,
pasteAsText,
showLocalSearch,
showRevisions,

View File

@@ -5,3 +5,8 @@
@use "./styles/note-title-wrapper.scss";
@use "./styles/note-editor-wrapper.scss";
@use "./styles/note-editor-viewer-row.scss";
@use "./styles/revision-viewer-root.scss";
@use "./styles/revision-viewer-title.scss";
@use "./styles/tag-bar.scss";
@use "./styles/editor-status-bar.scss";
@use "./styles/editor-status-indicator.scss";

View File

@@ -0,0 +1,13 @@
.editor-status-bar {
display: flex;
flex-direction: row;
> .spacer {
flex-grow: 1;
}
> .status {
align-self: end;
}
}

View File

@@ -0,0 +1,18 @@
.editor-status-indicator {
width: 0;
overflow: hidden;
&:has(> :focus-visible), &.-show {
width: unset;
overflow: visible;
}
> .button {
font-size: var(--joplin-font-size-small);
background-color: var(--joplin-background-color-active3);
color: var(--joplin-color);
border: none;
padding: 4px;
}
}

View File

@@ -0,0 +1,7 @@
.revision-viewer-root {
background-color: var(--joplin-background-color);
display: flex;
flex-direction: column;
flex: 1;
}

View File

@@ -0,0 +1,20 @@
.revision-viewer-title {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
border-width: 1px;
border-bottom-style: solid;
border-color: var(--joplin-divider-color);
padding-bottom: 10px;
> .revisions {
margin-left: 10px;
flex: 0.5;
}
> .title {
flex: 1;
}
}

View File

@@ -0,0 +1,14 @@
.tag-bar {
padding-left: var(--joplin-editor-padding-left);
display: flex;
flex-direction: row;
align-items: center;
height: 40px;
> .content {
padding-left: 8px;
display: flex;
flex-direction: row;
align-items: center;
}
}

View File

@@ -51,6 +51,7 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
createFileURL: options.createFileURL,
resizeLargeImages: Setting.value('imageResizing'),
markupLanguage: options.markupLanguage,
resourceSuffix: i > 0 ? ' ' : '',
});
if (!newBody) {

View File

@@ -9,6 +9,10 @@ import { DropHandler } from './useDropHandler';
import { SearchMarkers } from './useSearchMarkers';
import { ParseOptions } from '@joplin/lib/HtmlToMd';
import { ScrollStrategy } from '@joplin/editor/CodeMirror/CodeMirrorControl';
import { MarkupToHtmlOptions } from '../../hooks/useMarkupToHtml';
import { ScrollbarSize } from '@joplin/lib/models/settings/builtInMetadata';
import { RefObject, SetStateAction } from 'react';
import * as React from 'react';
export interface AllAssetsOptions {
contentMaxWidthTarget?: string;
@@ -47,10 +51,12 @@ export interface NoteEditorProps {
watchedResources: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
highlightedWords: any[];
tabMovesFocus: boolean;
plugins: PluginStates;
toolbarButtonInfos: ToolbarItem[];
setTagsToolbarButtonInfo: ToolbarButtonInfo;
contentMaxWidth: number;
scrollbarSize: ScrollbarSize;
isSafeMode: boolean;
useCustomPdfViewer: boolean;
shareCacheSetting: string;
@@ -72,22 +78,7 @@ export interface NoteBodyEditorRef {
execCommand(command: CommandValue): Promise<void>;
}
export interface MarkupToHtmlOptions {
replaceResourceInternalToExternalLinks?: boolean;
resourceInfos?: ResourceInfos;
contentMaxWidth?: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
plugins?: Record<string, any>;
bodyOnly?: boolean;
mapsToLine?: boolean;
useCustomPdfViewer?: boolean;
noteId?: string;
vendorDir?: string;
platformName?: string;
allowedFilePrefixes?: string[];
whiteBackgroundNoteRendering?: boolean;
}
export { MarkupToHtmlOptions };
export type MarkupToHtmlHandler = (markupLanguage: MarkupLanguage, markup: string, options: MarkupToHtmlOptions)=> Promise<RenderResult>;
export type HtmlToMarkdownHandler = (markupLanguage: number, html: string, originalCss: string, parseOptions?: ParseOptions)=> Promise<string>;
@@ -105,6 +96,8 @@ export interface NoteBodyEditorProps {
// avoid cases where black text is rendered over a dark background.
whiteBackgroundNoteRendering: boolean;
scrollbarSize: ScrollbarSize;
content: string;
contentKey: string;
contentMarkupLanguage: number;
@@ -119,8 +112,7 @@ export interface NoteBodyEditorProps {
htmlToMarkdown: HtmlToMarkdownHandler;
allAssets: (markupLanguage: MarkupLanguage, options: AllAssetsOptions)=> Promise<RenderResultPluginAsset[]>;
disabled: boolean;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
dispatch: Function;
dispatch: Dispatch;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
noteToolbar: any;
setLocalSearchResultCount(count: number): void;
@@ -131,6 +123,7 @@ export interface NoteBodyEditorProps {
searchMarkers: SearchMarkers;
visiblePanes: string[];
keyboardMode: string;
tabMovesFocus: boolean;
resourceInfos: ResourceInfos;
resourceDirectory: string;
locale: string;
@@ -281,3 +274,11 @@ export interface ScrollToTextValue {
element: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'strong' | 'ul';
scrollStrategy?: ScrollStrategy;
}
export interface WindowCommandDependencies {
setShowLocalSearch: React.Dispatch<SetStateAction<boolean>>;
noteSearchBarRef: RefObject<HTMLInputElement>;
editorRef: RefObject<NoteBodyEditorRef>;
titleInputRef: RefObject<HTMLInputElement>;
containerRef: RefObject<HTMLDivElement|null>;
}

View File

@@ -0,0 +1,43 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const react_1 = require('react');
const markupLanguageUtils_1 = require('@joplin/lib/utils/markupLanguageUtils');
const Setting_1 = require('@joplin/lib/models/Setting');
const shim_1 = require('@joplin/lib/shim');
const { themeStyle } = require('@joplin/lib/theme');
const Note_1 = require('@joplin/lib/models/Note');
const resourceUtils_1 = require('@joplin/lib/models/utils/resourceUtils');
function useMarkupToHtml(deps) {
const { themeId, customCss, plugins, whiteBackgroundNoteRendering } = deps;
const resourceBaseUrl = (0, react_1.useMemo)(() => {
return `joplin-content://note-viewer/${Setting_1.default.value('resourceDir')}/`;
}, []);
const markupToHtml = (0, react_1.useMemo)(() => {
return markupLanguageUtils_1.default.newMarkupToHtml(plugins, {
resourceBaseUrl,
customCss: customCss || '',
});
}, [plugins, customCss, resourceBaseUrl]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
return (0, react_1.useCallback)(async (markupLanguage, md, options = null) => {
options = { replaceResourceInternalToExternalLinks: false, resourceInfos: {}, platformName: shim_1.default.platformName(), ...options };
md = md || '';
const theme = themeStyle(themeId);
let resources = {};
if (options.replaceResourceInternalToExternalLinks) {
md = await Note_1.default.replaceResourceInternalToExternalLinks(md, { useAbsolutePaths: true });
} else {
resources = options.resourceInfos;
}
delete options.replaceResourceInternalToExternalLinks;
const result = await markupToHtml.render(markupLanguage, md, theme, { codeTheme: theme.codeThemeCss, resources: resources, postMessageSyntax: 'ipcProxySendToHost', splitted: true, externalAssetsOnly: true, codeHighlightCacheKey: 'useMarkupToHtml', settingValue: deps.settingValue, whiteBackgroundNoteRendering, itemIdToUrl: (id, urlParameters = '') => {
if (!(id in resources) || !resources[id]) {
return null;
}
return (0, resourceUtils_1.resourceFullPath)(resources[id].item, resourceBaseUrl) + urlParameters;
}, ...options });
return result;
}, [themeId, markupToHtml, whiteBackgroundNoteRendering, resourceBaseUrl, deps.settingValue]);
}
exports.default = useMarkupToHtml;
// # sourceMappingURL=useMarkupToHtml.js.map

View File

@@ -1,15 +1,11 @@
import { useMemo } from 'react';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
import getShownPluginEditorView from '@joplin/lib/services/plugins/utils/getShownPluginEditorView';
// If a plugin editor should be shown for the current note, this function will return the plugin and
// associated view.
export default (plugins: PluginStates, shownEditorViewIds: string[]) => {
return useMemo(() => {
const { editorPlugin, editorView } = getActivePluginEditorView(plugins);
if (editorView) {
if (!shownEditorViewIds.includes(editorView.id)) return { editorPlugin: null, editorView: null };
}
return { editorPlugin, editorView };
return getShownPluginEditorView(plugins, shownEditorViewIds);
}, [plugins, shownEditorViewIds]);
};

View File

@@ -0,0 +1,21 @@
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
import { useEffect } from 'react';
interface Props {
noteId: string;
windowId: string;
}
const useResourceUnwatcher = ({ noteId, windowId }: Props) => {
useEffect(() => {
// All resources associated with the current window should no longer be watched after:
// 1. The editor unloads, or
// 2. The note shown in the editor changes.
// Unwatching in a cleanup callback handles both cases.
return () => {
void ResourceEditWatcher.instance().stopWatchingAll(windowId);
};
}, [noteId, windowId]);
};
export default useResourceUnwatcher;

View File

@@ -1,7 +1,6 @@
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { NoteBodyEditorRef, ScrollOptions, ScrollOptionTypes } from './types';
import usePrevious from '@joplin/lib/hooks/usePrevious';
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
import type { EditorScrollPercents } from '../../../app.reducer';
interface Props {
@@ -30,8 +29,6 @@ const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollP
type: selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
});
void ResourceEditWatcher.instance().stopWatchingAll();
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
const clearScrollWhenReady = useCallback(() => {

View File

@@ -1,5 +1,5 @@
import { RefObject, useEffect } from 'react';
import { NoteBodyEditorRef, OnChangeEvent, ScrollOptionTypes } from './types';
import { RefObject, Dispatch, SetStateAction, useEffect } from 'react';
import { WindowCommandDependencies, NoteBodyEditorRef, OnChangeEvent, ScrollOptionTypes } from './types';
import editorCommandDeclarations, { enabledCondition } from '../editorCommandDeclarations';
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext, RegisteredRuntime } from '@joplin/lib/services/CommandService';
import time from '@joplin/lib/time';
@@ -10,14 +10,14 @@ const commandsWithDependencies = [
require('../commands/showLocalSearch'),
require('../commands/focusElementNoteTitle'),
require('../commands/focusElementNoteBody'),
require('../commands/focusElementToolbar'),
require('../commands/pasteAsText'),
];
type OnBodyChange = (event: OnChangeEvent)=> void;
interface HookDependencies {
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
setShowLocalSearch: Function;
setShowLocalSearch: Dispatch<SetStateAction<boolean>>;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
dispatch: Function;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -93,11 +93,12 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
));
}
const dependencies = {
const dependencies: WindowCommandDependencies = {
editorRef,
setShowLocalSearch,
noteSearchBarRef,
titleInputRef,
containerRef,
};
for (const command of commandsWithDependencies) {

View File

@@ -29,6 +29,7 @@ import getNoteElementIdFromJoplinId from '../NoteListItem/utils/getNoteElementId
import useFocusVisible from './utils/useFocusVisible';
import { stateUtils } from '@joplin/lib/reducer';
import { connect } from 'react-redux';
import useOnNoteDoubleClick from './utils/useOnNoteDoubleClick';
const commands = {
focusElementNoteList,
@@ -103,6 +104,8 @@ const NoteList = (props: Props) => {
const onNoteClick = useOnNoteClick(props.dispatch, focusNote);
const onNoteDoubleClick = useOnNoteDoubleClick();
const onKeyDown = useOnKeyDown(
activeNoteId,
props.selectedNoteIds,
@@ -115,6 +118,8 @@ const NoteList = (props: Props) => {
props.notes.length,
listRenderer.flow,
itemsPerLine,
props.showCompletedTodos,
props.uncompletedTodosOnTop,
);
useItemCss(listRenderer.itemCss);
@@ -198,7 +203,9 @@ const NoteList = (props: Props) => {
const renderEmptyList = () => {
if (props.notes.length) return null;
return <div className="emptylist">{getEmptyFolderMessage(props.folders, props.selectedFolderId)}</div>;
// Role status is necessary for the screenreader to announce that the list is empty, since when there are
// zero items there is not list to render
return <div className="emptylist" role="status">{getEmptyFolderMessage(props.folders, props.selectedFolderId)}</div>;
};
const renderFiller = (key: string, style: React.CSSProperties) => {
@@ -226,6 +233,7 @@ const NoteList = (props: Props) => {
itemSize={itemSize}
onChange={listRenderer.onChange}
onClick={onNoteClick}
onDoubleClick={onNoteDoubleClick}
onContextMenu={onItemContextMenu}
onDragStart={onDragStart}
onDragOver={onDragOver}
@@ -302,6 +310,7 @@ const NoteList = (props: Props) => {
onKeyUp={onKeyUp}
onDrop={onDrop}
onContextMenu={onContainerContextMenu}
id='notes-list'
>
{renderEmptyList()}
{renderFiller('top', topFillerStyle)}

View File

@@ -23,6 +23,8 @@ const useOnKeyDown = (
noteCount: number,
flow: ItemFlow,
itemsPerLine: number,
showCompletedTodos: boolean,
uncompletedTodosOnTop: boolean,
) => {
const scrollNoteIndex = useCallback((visibleItemCount: number, key: KeyboardEventKey, ctrlKey: boolean, metaKey: boolean, noteIndex: number) => {
if (flow === ItemFlow.TopToBottom) {
@@ -142,13 +144,32 @@ const useOnKeyDown = (
const todos = selectedNotes.filter(n => !!n.is_todo);
if (!todos.length) return;
const firstNoteIndex = notes.findIndex(n => n.id === todos[0].id);
let nextSelectedNoteIndex = firstNoteIndex + 1;
if (nextSelectedNoteIndex > notes.length - 1) nextSelectedNoteIndex = notes.length - 1;
const nextSelectedNote = nextSelectedNoteIndex >= 0 ? notes[nextSelectedNoteIndex] : todos[0];
for (let i = 0; i < todos.length; i++) {
const toggledTodo = Note.toggleTodoCompleted(todos[i]);
await Note.save(toggledTodo);
}
// When the settings `uncompletedTodosOnTop` or `showCompletedTodos` are enabled, the
// note that got set as completed or uncompleted is going to disappear from view,
// possibly hidden or moved to the top or bottom of the note list. It is assumed that
// the user does not want to keep that note selected since the to-do is indeed
// "completed". And by keeping that selection, the cursor would jump, making you lose
// context if you have multiple to-dos that need to be ticked. For that reason we set
// the selection to the next note in the list, which also ensures that the scroll
// position doesn't change. This is the same behaviour as when deleting a note.
const maintainScrollPosition = !showCompletedTodos || uncompletedTodosOnTop;
if (maintainScrollPosition) {
dispatch({ type: 'NOTE_SELECT', noteId: nextSelectedNote.id });
}
dispatch({ type: 'NOTE_SORT' });
focusNote(todos[0].id);
if (!maintainScrollPosition) focusNote(todos[0].id);
const wasCompleted = !!todos[0].todo_completed;
announceForAccessibility(!wasCompleted ? _('Complete') : _('Incomplete'));
}
@@ -171,7 +192,7 @@ const useOnKeyDown = (
type: 'NOTE_SELECT_ALL',
});
}
}, [moveNote, focusNote, visibleItemCount, scrollNoteIndex, makeItemIndexVisible, notes, selectedNoteIds, activeNoteId, dispatch, flow, itemsPerLine]);
}, [moveNote, focusNote, visibleItemCount, scrollNoteIndex, makeItemIndexVisible, notes, selectedNoteIds, activeNoteId, dispatch, flow, itemsPerLine, showCompletedTodos, uncompletedTodosOnTop]);
return onKeyDown;

View File

@@ -0,0 +1,10 @@
import * as React from 'react';
import { useCallback } from 'react';
import CommandService from '@joplin/lib/services/CommandService';
export default () => {
return useCallback((event: React.MouseEvent<HTMLDivElement>) => {
const noteId = event.currentTarget.getAttribute('data-id');
void CommandService.instance().execute('openNoteInNewWindow', noteId);
}, []);
};

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