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

Compare commits

...

298 Commits

Author SHA1 Message Date
Laurent Cozic
c524f5a6b5 Server v3.3.6 2025-03-30 12:06:08 +02:00
Laurent Cozic
ee2b186752 Server: Trying to build ARM64 Docker image 2025-03-30 12:05:22 +02:00
Maxim Medvedev
76a3250707 Server: buildx support for Docker images (#11582)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-03-30 12:01:39 +02:00
Laurent Cozic
e8144f9ee2 Doc: Update sponsors 2025-03-30 11:15:19 +02:00
Laurent Cozic
47f6b1ce33 Chore: Remove JS files 2025-03-30 10:32:42 +02:00
Laurent Cozic
f0121e7799 Server v3.3.5 2025-03-30 10:32:01 +02:00
Laurent Cozic
0acb14d0bf Server: Trying to build Joplin Server Docker image for ARM64 (#12030) 2025-03-30 10:31:16 +02:00
Henry Heino
18ebd16428 iOS: Fixes #11711: Fix Markdown toolbar partially covered by keyboard on some iOS devices (#12027) 2025-03-29 13:46:37 +01:00
Nick
6bc1965ec0 Update Swedish translation (#12023) 2025-03-28 18:08:52 +01:00
Henry Heino
1fed875140 Mobile: Plugins: Add command to hide the plugin panel viewer (#12018) 2025-03-28 13:40:36 +01:00
Henry Heino
98fe57e87a Desktop: Resolves #11903: Accessibility: Remove redundant accessibility information from sidebar notebooks (#12020) 2025-03-28 13:40:22 +01:00
Henry Heino
5bcb2531f4 Desktop: Rich Text Editor: Add setting to allow disabling auto-format (#12022) 2025-03-28 13:40:09 +01:00
Laurent Cozic
f6d69ef702 Desktop: Fixes #12021: App without a profile directory cannot start 2025-03-27 22:14:37 +01:00
mrjo118
1f05a3212f Mobile: Resolves #10883: Remove slider component module and replace integer settings with new validated component (#11822) 2025-03-27 22:01:09 +01:00
Henry Heino
ff0321e906 Chore: Improve note list test reliability (#12019) 2025-03-27 21:58:43 +01:00
Henry Heino
6b881b226e Web: Fix crash on opening settings (#12017) 2025-03-27 21:58:11 +01:00
Henry Heino
6a26ec8105 Android: Voice typing: Disable "Download update" button while downloading an updated model (#12015) 2025-03-27 21:57:38 +01:00
Henry Heino
5966402d8b Chore: Make useFormNote.test.ts less likely to fail in CI (#12014) 2025-03-27 21:57:28 +01:00
Henry Heino
5198b598bb Android: Voice typing: Improve transcription at the end of paragraphs (#12013) 2025-03-27 21:57:05 +01:00
Henry Heino
0a4c97c631 Android: Voice typing: Performance: Disable preview generation logic (#12008) 2025-03-27 21:56:56 +01:00
Laurent Cozic
0bc62aa05e All: Fixes #11934: Restoring a note which was in a deleted notebook (#12016) 2025-03-27 21:55:36 +01:00
Joplin Bot
44d1e9e3ca Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-03-27 18:47:07 +00:00
Henry Heino
0cef6cc611 Android: Voice typing: Fix incorrectly-calculated audio length (#12012) 2025-03-27 18:57:55 +01:00
Laurent Cozic
3e5acfbc09 Doc: update sponsors 2025-03-27 18:31:54 +01:00
Henry Heino
675f55d152 Desktop: Resolves #11741: Accessibility: Add screen reader announcements when toggling the note list and/or sidebar (#11776) 2025-03-27 15:53:59 +01:00
Henry Heino
b1edb84b49 Android: Voice typing: Default to a larger model (#12009) 2025-03-27 15:44:19 +01:00
mrjo118
ece7a4ccf0 Mobile: Fixes #11820: Fix cursor moves to incorrect position when revising TextInput value (#11821) 2025-03-27 15:33:00 +01:00
PARAMESH T S
cfd98d2723 Desktop, Mobile: Fixes #11971: Changing the type of one list changes it for all the lists (#11986) 2025-03-27 15:25:12 +01:00
Henry Heino
2a17301a9f Chore: Testing: Attach Playwright logs to CI results (#12007) 2025-03-25 21:07:27 +01:00
Laurent Cozic
e3762dc3f8 Desktop: Resolves #11992: Multiple instances: Secure local server (#11999) 2025-03-25 19:48:11 +01:00
Henry Heino
0959a19d65 Chore: Desktop: Fix tests in IPC pull request (#12004) 2025-03-25 18:50:16 +01:00
Henry Heino
93219575b4 Chore: Desktop: Update Playwright, allow debugging Playwright tests from VSCode (#12003) 2025-03-25 12:12:38 +01:00
Laurent Cozic
baaeea1307 Server: Fixes #11910: Disable faulty dark theme to prevent published notes from being unreadable 2025-03-25 12:06:00 +01:00
Joplin Bot
de6c5d448f Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-03-24 18:45:45 +00:00
Laurent Cozic
cc2cf5f521 Doc: added sponsor 2025-03-24 15:34:58 +01:00
Laurent Cozic
ef513862a9 Plugins: Add setting.globalValues and deprecate setting.globalValue 2025-03-23 13:14:02 +01:00
Joplin Bot
d07f3b5f16 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-03-22 02:03:52 +00:00
Laurent Cozic
a6079869bc Desktop: Fixes #11975: Regression: Restarting app is broken 2025-03-21 23:26:54 +01:00
Laurent Cozic
2fdbb22481 Android 3.3.4 2025-03-21 19:35:20 +01:00
Henry Heino
c5bb88ddf4 Android: Resolves #11955: Voice typing: Improve re-download button UI (#11979) 2025-03-21 19:00:49 +01:00
Henry Heino
5d7c78c361 Android: Voice typing: Improve processing with larger models (#11983) 2025-03-21 19:00:38 +01:00
Laurent Cozic
808eb7d49a Chore: Resolves #11993: Display a message explaining why the app did not start in dev mode 2025-03-21 12:08:09 +01:00
Laurent Cozic
2142373fff Desktop: Fixes #11989: Joplin became unusably slow on MacOS due to incorrect detection of architecture 2025-03-21 11:56:13 +01:00
Laurent Cozic
20f7f37b49 Update config.yml 2025-03-20 17:05:09 +01:00
Laurent Cozic
04fc634092 Server: Added links to social networks 2025-03-18 16:41:58 +00:00
Laurent Cozic
d40c9d3ff9 Doc: Add YouTube link to doc 2025-03-18 16:41:58 +00:00
marph91
224b4f619a Docs: update "Importing from other applications" help section (#11969)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-03-16 22:22:57 +00:00
Joplin Bot
88d1d4b7d1 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-03-16 12:54:34 +00:00
Laurent Cozic
6a22ffbcb1 iOS 13.3.2 2025-03-16 11:47:09 +00:00
Laurent Cozic
d735cf64e0 Chore: Fixing iOS build 2025-03-16 11:46:09 +00:00
Laurent Cozic
d7d6fd5ccd Android 3.3.3 2025-03-16 10:49:01 +00:00
Laurent Cozic
23254e6ffd Desktop release v3.3.3 2025-03-16 10:25:28 +00:00
Meow
eb8bfd5aec iOS: Re-Add iOS Dark Icon (#11943)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-03-16 10:21:58 +00:00
Laurent Cozic
cb5ffd968d Desktop: Add support for multiple instances (#11963) 2025-03-16 10:18:32 +00:00
Henry Heino
7b2b3a4f80 Chore: Increase Playwright test timeouts and reduce test flakiness (#11970) 2025-03-15 23:50:53 +00:00
Henry Heino
cbfe109c41 iOS: Accessibility: Fix focus gets stuck on "Attach" in the note actions menu (#11958) 2025-03-15 13:20:23 +00:00
Henry Heino
c8b01d11d6 Mobile: Accessibility: Make default modal close button accessible (#11957) 2025-03-15 13:20:11 +00:00
Henry Heino
b042395fd1 Web: Accessibility: Fix "sort notes by" button is sometimes not keyboard focusable (#11959) 2025-03-15 13:20:01 +00:00
Henry Heino
ba5ad18093 Desktop: Accessibility: Add a menu item that moves focus to the note viewer (#11967) 2025-03-15 13:19:47 +00:00
Henry Heino
ff15232a10 Android: Resolves #11956: Voice typing: Transcribe more unprocessed audio after pressing "done" (#11960) 2025-03-15 12:29:05 +00:00
Henry Heino
5a6e72197a Desktop: Upgrade to Electron 35.0.1 (#11968) 2025-03-15 12:01:18 +00:00
summoner001
de555b6871 All: Translation: Update hu_HU.po (#11962) 2025-03-14 13:00:27 -04:00
cro
9a2548a5e3 Update webdav.md (#11951) 2025-03-14 00:15:51 +00:00
Henry Heino
107996289f Mobile: Accessibility: Fix missing label on note actions menu dismiss button (#11954) 2025-03-13 19:56:19 +00:00
Henry Heino
c3c0101555 Android: Voice typing: Fix potential output duplication when finalizing voice typing (#11953) 2025-03-13 19:14:06 +00:00
Laurent Cozic
64f3dae8cc Doc: Fixed sponsor "alt" tag on website main page 2025-03-12 21:03:20 +00:00
Henry Heino
a39b51cc97 Docs: Fix website build (#11947) 2025-03-10 16:40:35 +00:00
Henry Heino
10bb8ef1a9 Docs: Resolves #11860: Add guidelines for making new contributions accessible (#11863) 2025-03-08 12:12:00 +00:00
Laurent Cozic
60ba22b233 Doc: Fix "How to" documents 2025-03-08 12:09:37 +00:00
Henry Heino
1bfd997be2 Docs: Accessibility: Document how to use the app with a screen reader (#11897) 2025-03-08 11:55:36 +00:00
Henry Heino
81e4a7fb74 Desktop: Fix adding tags to a note through drag-and-drop (#11911) 2025-03-08 11:54:24 +00:00
Henry Heino
360568d325 Desktop: Fixes #11894: Fix ctrl-p doesn't open the goto anything dialog in the Rich Text Editor (#11926) 2025-03-08 11:54:12 +00:00
Henry Heino
1aa0f11670 Mobile: Accessibility: Improve focus handling in the note actions menu and modal dialogs (#11929) 2025-03-08 11:53:06 +00:00
Henry Heino
0430ccb3e7 iOS: Accessibility: Fix plugins can't be installed using VoiceOver (#11931) 2025-03-08 11:52:03 +00:00
av
c0d6c1eb0b Tools: add giflib to devbox dependencies (#11938) 2025-03-08 11:51:48 +00:00
Amine Zouaoui
215f09d73c Desktop: Resolves #11696: Add "Disable synchronisation" to Joplin Cloud prompt message (#11705) 2025-03-08 11:50:30 +00:00
pedr
1f192696de Desktop: Fixes #11939: Import audio from OneNote as file links (#11942) 2025-03-08 11:49:20 +00:00
Laurent Cozic
ab86b95fad Desktop, Mobile, Cli: Add setting migration for ocr.enabled 2025-03-07 15:47:44 +00:00
Laurent Cozic
0f07c0f53a Desktop, Mobile: Fixes #11673: Make tab size consistent between Markdown editor and viewer (and RTE) (#11940)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2025-03-07 15:42:32 +00:00
Dmitriy Q
a6d04c4781 All: Translation: Update ru_RU.po (#11937) 2025-03-06 23:44:58 -05:00
PARAMESH T S
bc27f47881 Desktop: Fixes #11923: Sharing a notebook with nobody prints "No user with ID public_key" (#11932)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-03-06 15:58:10 +00:00
Henry Heino
d1d75449f5 Chore: CI: Upgrade Linux actions runner to Ubuntu 22.04 (#11927) 2025-03-06 00:19:57 +00:00
Laurent Cozic
bbea5388ed Doc: Describe how to migrate from Joplin Cloud Basic or Pro to Team 2025-03-05 19:42:47 +00:00
Laurent Cozic
99e773855e Chore: Improve error message when website does not build 2025-03-05 18:57:02 +00:00
Laurent Cozic
55b73347e5 Doc: Fixed downloading Apple Silicon version on Download page 2025-03-05 18:56:44 +00:00
Laurent Cozic
7e8dee4906 Desktop: Added keyboard shortcut and menu item for toggleEditorPlugin command 2025-03-05 00:43:39 +00:00
Laurent Cozic
69fb1ab104 Chore: Fixed test that fails on fast enough computers 2025-03-05 00:43:39 +00:00
Helmut K. C. Tessarek
67ae0ea2d1 Desktop: improve download in install script (#11921) 2025-03-04 19:06:31 -05:00
Celestial.y
cdb61b922b All: Translation: Update zh_CN.po (#11922) 2025-03-04 18:52:12 -05:00
pedr
da80443796 Chore: Remove file created during automated test (#11915) 2025-03-04 11:58:57 +00:00
Henry Heino
1924dd31d2 Desktop: Make "toggle all folders" button also expand the folder list (#11917) 2025-03-04 11:58:31 +00:00
Henry Heino
b831d8c068 Desktop: Accessibility: Improve "toggle all notebooks" accessibility (#11918) 2025-03-04 11:57:11 +00:00
Joplin Bot
4ad1b49769 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-03-04 02:03:55 +00:00
klxiang
0d6c1067e3 All: Translation: Update zh_CN.po (#11920) 2025-03-03 21:01:55 -05:00
Eric Duarte
0bdc38a6be All: Translation: Update es_ES.po (#11913) 2025-03-03 20:43:27 -05:00
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
1122 changed files with 204101 additions and 21099 deletions

View File

@@ -158,6 +158,7 @@ packages/app-desktop/commands/exportFolders.js
packages/app-desktop/commands/exportNotes.js
packages/app-desktop/commands/focusElement.js
packages/app-desktop/commands/index.js
packages/app-desktop/commands/newAppInstance.js
packages/app-desktop/commands/openNoteInNewWindow.js
packages/app-desktop/commands/openProfileDirectory.js
packages/app-desktop/commands/replaceMisspelling.js
@@ -170,6 +171,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 +203,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 +252,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 +268,13 @@ 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/focusElementNoteViewer.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,7 +297,6 @@ 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
@@ -313,6 +320,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
@@ -355,6 +363,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
@@ -429,6 +438,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
@@ -449,7 +459,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
@@ -458,7 +467,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
@@ -480,6 +488,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
@@ -490,6 +499,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
@@ -499,6 +509,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
@@ -507,6 +519,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
@@ -563,12 +576,14 @@ 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
packages/app-desktop/utils/restartInSafeModeFromMain.js
packages/app-desktop/utils/window/types.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/commands/dismissPluginPanels.js
packages/app-mobile/commands/index.js
packages/app-mobile/commands/newNote.test.js
packages/app-mobile/commands/newNote.js
@@ -607,6 +622,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
@@ -670,9 +686,15 @@ packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenuContentNote.js
packages/app-mobile/components/TextInput.js
packages/app-mobile/components/ToggleSpaceButton.js
packages/app-mobile/components/accessibility/AccessibleModalMenu.js
packages/app-mobile/components/accessibility/AccessibleView.test.js
packages/app-mobile/components/accessibility/AccessibleView.js
packages/app-mobile/components/accessibility/FocusControl/AutoFocusProvider.js
packages/app-mobile/components/accessibility/FocusControl/FocusControl.js
packages/app-mobile/components/accessibility/FocusControl/FocusControlProvider.js
packages/app-mobile/components/accessibility/FocusControl/MainAppContent.js
packages/app-mobile/components/accessibility/FocusControl/ModalWrapper.js
packages/app-mobile/components/accessibility/FocusControl/types.js
packages/app-mobile/components/app-nav.js
packages/app-mobile/components/base-screen.js
packages/app-mobile/components/biometrics/BiometricPopup.js
@@ -684,6 +706,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
@@ -724,8 +747,11 @@ packages/app-mobile/components/screens/ConfigScreen/SectionSelector/SectionTab.j
packages/app-mobile/components/screens/ConfigScreen/SectionSelector/index.js
packages/app-mobile/components/screens/ConfigScreen/SettingComponent.js
packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
packages/app-mobile/components/screens/ConfigScreen/SettingTextInput.js
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
packages/app-mobile/components/screens/ConfigScreen/SettingsToggle.js
packages/app-mobile/components/screens/ConfigScreen/ValidatedIntegerInput.test.js
packages/app-mobile/components/screens/ConfigScreen/ValidatedIntegerInput.js
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
packages/app-mobile/components/screens/ConfigScreen/plugins/EnablePluginSupportPage.js
packages/app-mobile/components/screens/ConfigScreen/plugins/InstalledPluginBox.js
@@ -774,12 +800,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
@@ -793,12 +823,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
@@ -829,7 +858,7 @@ packages/app-mobile/utils/fs-driver/testUtil/createFilesFromPathRecord.js
packages/app-mobile/utils/fs-driver/testUtil/verifyDirectoryMatches.js
packages/app-mobile/utils/getPackageInfo.js
packages/app-mobile/utils/getVersionInfoText.js
packages/app-mobile/utils/hooks/useKeyboardVisible.js
packages/app-mobile/utils/hooks/useKeyboardState.js
packages/app-mobile/utils/hooks/useOnLongPressProps.js
packages/app-mobile/utils/hooks/useReduceMotionEnabled.js
packages/app-mobile/utils/image/fileToImage.web.js
@@ -857,6 +886,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
@@ -883,6 +913,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
@@ -894,8 +928,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
@@ -906,6 +938,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
@@ -935,6 +968,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
@@ -1002,7 +1036,11 @@ 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.test.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
@@ -1038,6 +1076,8 @@ packages/lib/fs-driver-base.js
packages/lib/fs-driver-node.js
packages/lib/fsDriver.test.js
packages/lib/geolocation-node.js
packages/lib/getAppName.test.js
packages/lib/getAppName.js
packages/lib/hooks/useAsyncEffect.js
packages/lib/hooks/useElementSize.js
packages/lib/hooks/useEventListener.js
@@ -1096,6 +1136,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
@@ -1238,6 +1281,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
@@ -1284,6 +1328,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

@@ -57,6 +57,8 @@ module.exports = {
'tinymce': 'readonly',
'JSX': 'readonly',
'NodeJS': 'readonly',
},
'parserOptions': {
'ecmaVersion': 2018,
@@ -309,7 +311,7 @@ module.exports = {
selector: 'interface',
format: null,
'filter': {
'regex': '^(RSA|RSAKeyPair)$',
'regex': '^(RSA|RSAKeyPair|iOS.*)$',
'match': true,
},
},

View File

@@ -1,8 +1,8 @@
blank_issues_enabled: false
blank_issues_enabled: true
contact_links:
- name: Feature Requests
url: https://discourse.joplinapp.org/c/features/
about: Discuss ideas for new features or changes
- name: Support
url: https://discourse.joplinapp.org/c/support/
about: Please ask for help here
about: Please ask for help here

View File

@@ -35,6 +35,8 @@ else
IS_MACOS=1
fi
DOCKER_IMAGE_PLATFORM="linux/amd64"
# Tests can randomly fail in some cases, so only run them when not publishing
# a release
RUN_TESTS=0
@@ -43,10 +45,33 @@ if [ "$IS_SERVER_RELEASE" = 0 ] && [ "$IS_DESKTOP_RELEASE" = 0 ]; then
RUN_TESTS=1
fi
if [ "$RUNNER_ARCH" == "ARM64" ] && [ "$IS_SERVER_RELEASE" == "0" ]; then
# We exit now because nothing works properly with the ARM64 architecture.
# We only proceed if building the server image.
echo "Running on ARM64 and not trying to build server image - early exit"
exit 0
fi
if [ "$RUNNER_ARCH" == "ARM64" ]; then
# Canvas is only needed for tests and it doesn't build in ARM64 so remove it
RUN_TESTS=0
cd "$ROOT_DIR/packages/lib"
yarn remove canvas
cd "$ROOT_DIR"
DOCKER_IMAGE_PLATFORM="linux/arm64"
# Delete certain directories because `yarn install` will fail on ARM64.
rm -rf app-desktop
rm -rf app-mobile
fi
# =============================================================================
# Print environment
# =============================================================================
echo "RUNNER_OS=$RUNNER_OS"
echo "RUNNER_ARCH=$RUNNER_ARCH"
echo "GITHUB_WORKFLOW=$GITHUB_WORKFLOW"
echo "GITHUB_EVENT_NAME=$GITHUB_EVENT_NAME"
echo "GITHUB_REF=$GITHUB_REF"
@@ -277,7 +302,7 @@ if [ "$IS_DESKTOP_RELEASE" == "1" ]; then
elif [[ $IS_LINUX = 1 ]] && [ "$IS_SERVER_RELEASE" == "1" ]; then
echo "Step: Building Docker Image..."
cd "$ROOT_DIR"
yarn buildServerDocker --tag-name $GIT_TAG_NAME --push-images --repository $SERVER_REPOSITORY
yarn buildServerDocker --platform $DOCKER_IMAGE_PLATFORM --tag-name $GIT_TAG_NAME --push-images --repository $SERVER_REPOSITORY
else
echo "Step: Building but *not* publishing desktop application..."

View File

@@ -9,47 +9,12 @@ jobs:
matrix:
# Do not use unbuntu-latest because it causes `The operation was canceled` failures:
# https://github.com/actions/runner-images/issues/6709
os: [macos-13, ubuntu-20.04, windows-2019]
os: [macos-13, ubuntu-22.04, windows-2019, ubuntu-22.04-arm]
steps:
- uses: actions/checkout@v4
# Trying to fix random networking issues on Windows
# https://github.com/actions/runner-images/issues/1187#issuecomment-686735760
- name: Disable TCP/UDP offload on Windows
if: runner.os == 'Windows'
run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6
- name: Disable TCP/UDP offload on Linux
if: runner.os == 'Linux'
run: sudo ethtool -K eth0 tx off rx off
- name: Disable TCP/UDP offload on macOS
if: runner.os == 'macOS'
run: |
sudo sysctl -w net.link.generic.system.hwcksum_tx=0
sudo sysctl -w net.link.generic.system.hwcksum_rx=0
# Silence apt-get update errors (for example when a module doesn't
# exist) since otherwise it will make the whole build fails, even though
# it might work without update. libsecret-1-dev is required for keytar -
# https://github.com/atom/node-keytar
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update || true
sudo apt-get install -y gettext
sudo apt-get install -y libsecret-1-dev
sudo apt-get install -y translate-toolkit
sudo apt-get install -y rsync
# Provides a virtual display on Linux. Used for Playwright integration
# testing.
sudo apt-get install -y xvfb
- name: Install macOs dependencies
if: runner.os == 'macOS'
run: |
# Required for building the canvas package
brew install pango
- name: Setup build environment
uses: ./.github/workflows/shared/setup-build-environment
- name: Install Docker Engine
# if: runner.os == 'Linux' && startsWith(github.ref, 'refs/tags/server-v')
@@ -65,23 +30,8 @@ jobs:
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update || true
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- uses: actions/checkout@v4
- uses: olegtarasov/get-tag@v2.1.3
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v4
with:
# We need to pin the version to 18.15, because 18.16+ fails with this error:
# https://github.com/facebook/react-native/issues/36440
node-version: '18.15.0'
cache: 'yarn'
- name: Install Yarn
run: |
# https://yarnpkg.com/getting-started/install
corepack enable
# Login to Docker only if we're on a server release tag. If we run this on
# a pull request it will fail because the PR doesn't have access to
# secrets
@@ -91,15 +41,6 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# macos-latest ships with Python 3.12 by default, but this removes a
# utility that's used by electron-builder (distutils) so we need to pin
# Python to an earlier version.
# Fixes error `ModuleNotFoundError: No module named 'distutils'`
# Ref: https://github.com/nodejs/node-gyp/issues/2869
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Run tests, build and publish Linux and macOS apps
if: runner.os == 'Linux' || runner.os == 'macOs'
env:
@@ -150,7 +91,7 @@ jobs:
matrix:
# Do not use unbuntu-latest because it causes `The operation was canceled` failures:
# https://github.com/actions/runner-images/issues/6709
os: [ubuntu-20.04]
os: [ubuntu-22.04, ubuntu-22.04-arm]
steps:
- name: Install Docker Engine
@@ -165,7 +106,7 @@ jobs:
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update || true
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- uses: actions/checkout@v4
@@ -183,11 +124,19 @@ jobs:
env:
BUILD_SEQUENCIAL: 1
run: |
echo "RUNNER_OS=$RUNNER_OS"
echo "RUNNER_ARCH=$RUNNER_ARCH"
# Canvas is only needed for tests and it doesn't build in ARM64 so remove it
cd packages/lib
yarn remove canvas
cd ../..
yarn install
yarn buildServerDocker --tag-name server-v0.0.0 --repository joplin/server
# Basic test to ensure that the created build is valid. It should exit with
# code 0 if it works.
# code 0 if it works.
docker run joplin/server:0.0.0-beta node dist/app.js migrate list
- name: Check HTTP request

View File

@@ -0,0 +1,72 @@
name: 'Setup build environment'
description: 'Install Joplin build dependencies'
runs:
using: 'composite'
steps:
# Trying to fix random networking issues on Windows
# https://github.com/actions/runner-images/issues/1187#issuecomment-686735760
- name: Disable TCP/UDP offload on Windows
if: runner.os == 'Windows'
shell: pwsh
run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6
- name: Disable TCP/UDP offload on Linux
if: runner.os == 'Linux'
shell: bash
run: sudo ethtool -K eth0 tx off rx off
- name: Disable TCP/UDP offload on macOS
if: runner.os == 'macOS'
shell: bash
run: |
sudo sysctl -w net.link.generic.system.hwcksum_tx=0
sudo sysctl -w net.link.generic.system.hwcksum_rx=0
# Silence apt-get update errors (for example when a module doesn't
# exist) since otherwise it will make the whole build fails, even though
# it might work without update. libsecret-1-dev is required for keytar -
# https://github.com/atom/node-keytar
- name: Install Linux dependencies
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update || true
sudo apt-get install -y gettext
sudo apt-get install -y libsecret-1-dev
sudo apt-get install -y translate-toolkit
sudo apt-get install -y rsync
# Provides a virtual display on Linux. Used for Playwright integration
# testing.
sudo apt-get install -y xvfb
- name: Install macOs dependencies
if: runner.os == 'macOS'
shell: bash
run: |
# Required for building the canvas package
brew install pango
- uses: olegtarasov/get-tag@v2.1.3
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v4
with:
# We need to pin the version to 18.15, because 18.16+ fails with this error:
# https://github.com/facebook/react-native/issues/36440
node-version: '18.15.0'
cache: 'yarn'
- name: Install Yarn
shell: bash
run: |
# https://yarnpkg.com/getting-started/install
corepack enable
# macos-latest ships with Python 3.12 by default, but this removes a
# utility that's used by electron-builder (distutils) so we need to pin
# Python to an earlier version.
# Fixes error `ModuleNotFoundError: No module named 'distutils'`
# Ref: https://github.com/nodejs/node-gyp/issues/2869
- uses: actions/setup-python@v5
with:
python-version: '3.11'

29
.github/workflows/ui-tests.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Joplin UI tests
on: [push, pull_request]
permissions:
contents: read
jobs:
Main:
# Don't run on forks
if: github.repository == 'laurent22/joplin'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-13, ubuntu-22.04]
steps:
- uses: actions/checkout@v4
- name: Setup build environment
uses: ./.github/workflows/shared/setup-build-environment
- name: Build
run: yarn install
- name: Run UI tests
run: |
cd ${GITHUB_WORKSPACE}/packages/app-desktop/
bash ./integration-tests/run-ci.sh
# See https://playwright.dev/docs/ci-intro#setting-up-github-actions
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report-${{ matrix.os }}
path: packages/app-desktop/playwright-report/
retention-days: 7

67
.gitignore vendored
View File

@@ -133,6 +133,7 @@ packages/app-desktop/commands/exportFolders.js
packages/app-desktop/commands/exportNotes.js
packages/app-desktop/commands/focusElement.js
packages/app-desktop/commands/index.js
packages/app-desktop/commands/newAppInstance.js
packages/app-desktop/commands/openNoteInNewWindow.js
packages/app-desktop/commands/openProfileDirectory.js
packages/app-desktop/commands/replaceMisspelling.js
@@ -145,6 +146,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 +178,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 +227,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 +243,13 @@ 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/focusElementNoteViewer.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,7 +272,6 @@ 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
@@ -288,6 +295,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
@@ -330,6 +338,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
@@ -404,6 +413,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
@@ -424,7 +434,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
@@ -433,7 +442,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
@@ -455,6 +463,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
@@ -465,6 +474,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
@@ -474,6 +484,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
@@ -482,6 +494,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
@@ -538,12 +551,14 @@ 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
packages/app-desktop/utils/restartInSafeModeFromMain.js
packages/app-desktop/utils/window/types.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/commands/dismissPluginPanels.js
packages/app-mobile/commands/index.js
packages/app-mobile/commands/newNote.test.js
packages/app-mobile/commands/newNote.js
@@ -582,6 +597,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
@@ -645,9 +661,15 @@ packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenuContentNote.js
packages/app-mobile/components/TextInput.js
packages/app-mobile/components/ToggleSpaceButton.js
packages/app-mobile/components/accessibility/AccessibleModalMenu.js
packages/app-mobile/components/accessibility/AccessibleView.test.js
packages/app-mobile/components/accessibility/AccessibleView.js
packages/app-mobile/components/accessibility/FocusControl/AutoFocusProvider.js
packages/app-mobile/components/accessibility/FocusControl/FocusControl.js
packages/app-mobile/components/accessibility/FocusControl/FocusControlProvider.js
packages/app-mobile/components/accessibility/FocusControl/MainAppContent.js
packages/app-mobile/components/accessibility/FocusControl/ModalWrapper.js
packages/app-mobile/components/accessibility/FocusControl/types.js
packages/app-mobile/components/app-nav.js
packages/app-mobile/components/base-screen.js
packages/app-mobile/components/biometrics/BiometricPopup.js
@@ -659,6 +681,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
@@ -699,8 +722,11 @@ packages/app-mobile/components/screens/ConfigScreen/SectionSelector/SectionTab.j
packages/app-mobile/components/screens/ConfigScreen/SectionSelector/index.js
packages/app-mobile/components/screens/ConfigScreen/SettingComponent.js
packages/app-mobile/components/screens/ConfigScreen/SettingItem.js
packages/app-mobile/components/screens/ConfigScreen/SettingTextInput.js
packages/app-mobile/components/screens/ConfigScreen/SettingsButton.js
packages/app-mobile/components/screens/ConfigScreen/SettingsToggle.js
packages/app-mobile/components/screens/ConfigScreen/ValidatedIntegerInput.test.js
packages/app-mobile/components/screens/ConfigScreen/ValidatedIntegerInput.js
packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.js
packages/app-mobile/components/screens/ConfigScreen/plugins/EnablePluginSupportPage.js
packages/app-mobile/components/screens/ConfigScreen/plugins/InstalledPluginBox.js
@@ -749,12 +775,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
@@ -768,12 +798,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
@@ -804,7 +833,7 @@ packages/app-mobile/utils/fs-driver/testUtil/createFilesFromPathRecord.js
packages/app-mobile/utils/fs-driver/testUtil/verifyDirectoryMatches.js
packages/app-mobile/utils/getPackageInfo.js
packages/app-mobile/utils/getVersionInfoText.js
packages/app-mobile/utils/hooks/useKeyboardVisible.js
packages/app-mobile/utils/hooks/useKeyboardState.js
packages/app-mobile/utils/hooks/useOnLongPressProps.js
packages/app-mobile/utils/hooks/useReduceMotionEnabled.js
packages/app-mobile/utils/image/fileToImage.web.js
@@ -832,6 +861,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
@@ -858,6 +888,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
@@ -869,8 +903,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
@@ -881,6 +913,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
@@ -910,6 +943,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
@@ -977,7 +1011,11 @@ 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.test.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
@@ -1013,6 +1051,8 @@ packages/lib/fs-driver-base.js
packages/lib/fs-driver-node.js
packages/lib/fsDriver.test.js
packages/lib/geolocation-node.js
packages/lib/getAppName.test.js
packages/lib/getAppName.js
packages/lib/hooks/useAsyncEffect.js
packages/lib/hooks/useElementSize.js
packages/lib/hooks/useEventListener.js
@@ -1071,6 +1111,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
@@ -1213,6 +1256,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
@@ -1259,6 +1303,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,62 +0,0 @@
diff --git a/android/src/newarch/java/com/reactnativecommunity/slider/ReactSliderManager.java b/android/src/newarch/java/com/reactnativecommunity/slider/ReactSliderManager.java
index a5bb95eec3337b93a2338a2869a2bda176c91cae..87817688eb280c2f702c26dc35558c6a0a4db1ea 100644
--- a/android/src/newarch/java/com/reactnativecommunity/slider/ReactSliderManager.java
+++ b/android/src/newarch/java/com/reactnativecommunity/slider/ReactSliderManager.java
@@ -42,12 +42,20 @@ public class ReactSliderManager extends SimpleViewManager<ReactSlider> implement
public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
ReactSlider slider = (ReactSlider)seekbar;
- if(progress < slider.getLowerLimit()) {
- progress = slider.getLowerLimit();
- seekbar.setProgress(progress);
- } else if (progress > slider.getUpperLimit()) {
- progress = slider.getUpperLimit();
- seekbar.setProgress(progress);
+ // During initialization, lowerLimit can be greater than upperLimit.
+ //
+ // If a change event is received during this, we need a check to prevent
+ // infinite recursion.
+ //
+ // Issue: https://github.com/callstack/react-native-slider/issues/571
+ if (slider.getLowerLimit() <= slider.getUpperLimit()) {
+ if(progress < slider.getLowerLimit()) {
+ progress = slider.getLowerLimit();
+ seekbar.setProgress(progress);
+ } else if (progress > slider.getUpperLimit()) {
+ progress = slider.getUpperLimit();
+ seekbar.setProgress(progress);
+ }
}
ReactContext reactContext = (ReactContext) seekbar.getContext();
diff --git a/android/src/oldarch/java/com/reactnativecommunity/slider/ReactSliderManager.java b/android/src/oldarch/java/com/reactnativecommunity/slider/ReactSliderManager.java
index 3ff5930f85a3cd92c2549925f41058abb188a57e..ab3681fdfe0b736c97020e1434e450c8183e6f18 100644
--- a/android/src/oldarch/java/com/reactnativecommunity/slider/ReactSliderManager.java
+++ b/android/src/oldarch/java/com/reactnativecommunity/slider/ReactSliderManager.java
@@ -30,12 +30,20 @@ public class ReactSliderManager extends SimpleViewManager<ReactSlider> {
public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
ReactSlider slider = (ReactSlider)seekbar;
- if(progress < slider.getLowerLimit()) {
- progress = slider.getLowerLimit();
- seekbar.setProgress(progress);
- } else if(progress > slider.getUpperLimit()) {
- progress = slider.getUpperLimit();
- seekbar.setProgress(progress);
+ // During initialization, lowerLimit can be greater than upperLimit.
+ //
+ // If a change event is received during this, we need a check to prevent
+ // infinite recursion.
+ //
+ // Issue: https://github.com/callstack/react-native-slider/issues/571
+ if (slider.getLowerLimit() <= slider.getUpperLimit()) {
+ if(progress < slider.getLowerLimit()) {
+ progress = slider.getLowerLimit();
+ seekbar.setProgress(progress);
+ } else if (progress > slider.getUpperLimit()) {
+ progress = slider.getUpperLimit();
+ seekbar.setProgress(progress);
+ }
}
ReactContext reactContext = (ReactContext) seekbar.getContext();

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,55 @@
# This patch improves the note actions menu (the kebab menu)'s accessibility
# by labelling its dismiss button.
diff --git a/build/rnpm.js b/build/rnpm.js
index 1111c2de99b3d4c5651ca4eee3ba59c0ce8e13e1..d410ee12b38d02c399b0a40973217da0082d73c0 100644
--- a/build/rnpm.js
+++ b/build/rnpm.js
@@ -1573,7 +1573,9 @@
onPress = _this$props.onPress,
style = _this$props.style;
return /*#__PURE__*/React__default.createElement(reactNative.TouchableWithoutFeedback, {
- onPress: onPress
+ onPress: onPress,
+ accessibilityLabel: _this$props.accessibilityLabel,
+ accessibilityRole: 'button',
}, /*#__PURE__*/React__default.createElement(reactNative.Animated.View, {
style: [styles.fullscreen, {
opacity: this.fadeAnim
@@ -1588,7 +1590,8 @@
}(React.Component);
Backdrop.propTypes = {
- onPress: propTypes.func.isRequired
+ onPress: propTypes.func.isRequired,
+ accessibilityLabel: propTypes.string,
};
var styles = reactNative.StyleSheet.create({
fullscreen: {
@@ -1658,6 +1661,7 @@
style: styles$1.placeholder
}, /*#__PURE__*/React__default.createElement(Backdrop, {
onPress: ctx._onBackdropPress,
+ accessibilityLabel: this.props.closeButtonLabel,
style: backdropStyles,
ref: ctx.onBackdropRef
}), ctx._makeOptions());
@@ -2090,6 +2094,7 @@
}), /*#__PURE__*/React__default.createElement(MenuPlaceholder, {
ctx: this,
backdropStyles: customStyles.backdrop,
+ closeButtonLabel: this.props.closeButtonLabel,
ref: this._onPlaceholderRef
}))));
}
diff --git a/src/index.d.ts b/src/index.d.ts
index 1db1e643a915e4bfb715e33354678ec1be219f50..007157e366d1935368bdd8eff5e7a0773e183d0f 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -18,6 +18,7 @@ declare module "react-native-popup-menu" {
menuProviderWrapper?: StyleProp<ViewStyle>;
backdrop?: StyleProp<ViewStyle>;
};
+ closeButtonLabel: string;
backHandler?: boolean | Function;
skipInstanceCheck?: boolean;
children: React.ReactNode;

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

@@ -0,0 +1,77 @@
<?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>

After

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: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -80,7 +80,7 @@ async function setupDownloadPage() {
if (href.indexOf('-Setup') > 0) downloadLinks['windows'] = href;
if (href.indexOf('.dmg') > 0) downloadLinks['macOs'] = href;
if (href.endsWith('arm64.DMG')) downloadLinks['macOsM1'] = href;
if (href.indexOf('arm64.DMG') > 0) downloadLinks['macOsM1'] = href;
if (href.indexOf('.AppImage') > 0) downloadLinks['linux'] = href;
});
@@ -98,6 +98,8 @@ async function setupDownloadPage() {
} else {
const os = await getOs();
console.info('Found OS: ' + os);
if (os === 'macOsUndefined') {
// If we don't know which macOS version it is, we let the user choose.
$('.main-content .intro').html('<p class="macos-m1-info">The macOS release is available for Intel processors or for Apple Silicon (M1) processors. Please select your version:</p>');

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

@@ -398,7 +398,7 @@
<div class="text-center sponsors-org">
{{#sponsors.orgs}}
<a class="sponsor-org-item" href="{{url}}"><img title="{{title}}" src="{{imageBaseUrl}}/sponsors/{{imageName}}"></a>
<a class="sponsor-org-item" href="{{url}}"><img alt="{{alt}}" title="{{title}}" src="{{imageBaseUrl}}/sponsors/{{imageName}}"></a>
{{/sponsors.orgs}}
</div>

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

@@ -7,6 +7,9 @@ FROM node:18 AS builder
RUN apt-get update \
&& apt-get install -y \
python3 tini \
# needed for node-canvas for ARM32 platform.
# See also https://github.com/Automattic/node-canvas/wiki/Installation:-Ubuntu-and-other-Debian-based-systems
libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev \
&& rm -rf /var/lib/apt/lists/*
# Enables Yarn
@@ -47,9 +50,9 @@ RUN sed --in-place '/onenote-converter/d' ./packages/lib/package.json
# Note that `yarn install` ignores `NODE_ENV=production` and will install dev
# dependencies too, but this is fine because we need them to build the app.
RUN BUILD_SEQUENCIAL=1 yarn install --inline-builds \
&& yarn cache clean \
&& rm -rf .yarn/berry
RUN --mount=type=cache,target=/build/.yarn/cache --mount=type=cache,target=/build/.yarn/berry/cache\
BUILD_SEQUENCIAL=1 yarn config set cacheFolder /build/.yarn/cache \
&& yarn install --inline-builds
# =============================================================================
# Final stage - we copy only the relevant files from the build stage and start
@@ -81,10 +84,11 @@ CMD ["yarn", "start-prod"]
ARG BUILD_DATE
ARG REVISION
ARG VERSION
ARG SOURCE
LABEL org.opencontainers.image.created="$BUILD_DATE" \
org.opencontainers.image.title="Joplin Server" \
org.opencontainers.image.description="Docker image for Joplin Server" \
org.opencontainers.image.url="https://joplinapp.org/" \
org.opencontainers.image.revision="$REVISION" \
org.opencontainers.image.source="https://github.com/laurent22/joplin.git" \
org.opencontainers.image.version="${VERSION}"
org.opencontainers.image.source="$SOURCE" \
org.opencontainers.image.version="$VERSION"

View File

@@ -67,10 +67,23 @@ showHelp() {
fi
}
#-----------------------------------------------------
# Setup Download Helper: DL
#-----------------------------------------------------
if [[ `command -v wget2` ]]; then
DL='wget2 -qO'
elif [[ `command -v wget` ]]; then
DL='wget -qO'
elif [[ `command -v curl` ]]; then
DL='curl -sLo'
else
print "${COLOR_RED}Error: wget2, wget, and curl not found. Please install one of these tools.${COLOR_RESET}"
exit 1
fi
#-----------------------------------------------------
# PARSE ARGUMENTS
#-----------------------------------------------------
optspec=":h-:"
while getopts "${optspec}" OPT; do
[ "${OPT}" = " " ] && continue
@@ -140,9 +153,9 @@ fi
# Get the latest version to download
if [[ "$INCLUDE_PRE_RELEASE" == true ]]; then
RELEASE_VERSION=$(wget -qO - "https://api.github.com/repos/laurent22/joplin/releases" | grep -Po '"tag_name": ?"v\K.*?(?=")' | sort -rV | head -1)
RELEASE_VERSION=$($DL - "https://api.github.com/repos/laurent22/joplin/releases" | grep -Po '"tag_name": ?"v\K.*?(?=")' | sort -rV | head -1)
else
RELEASE_VERSION=$(wget -qO - "https://api.github.com/repos/laurent22/joplin/releases/latest" | grep -Po '"tag_name": ?"v\K.*?(?=")')
RELEASE_VERSION=$($DL - "https://api.github.com/repos/laurent22/joplin/releases/latest" | grep -Po '"tag_name": ?"v\K.*?(?=")')
fi
# Check if it's in the latest version
@@ -163,8 +176,8 @@ fi
#-----------------------------------------------------
print 'Downloading Joplin...'
TEMP_DIR=$(mktemp -d)
wget -O "${TEMP_DIR}/Joplin.AppImage" "https://objects.joplinusercontent.com/v${RELEASE_VERSION}/Joplin-${RELEASE_VERSION}.AppImage?source=LinuxInstallScript&type=$DOWNLOAD_TYPE"
wget -O "${TEMP_DIR}/joplin.png" https://joplinapp.org/images/Icon512.png
$DL "${TEMP_DIR}/Joplin.AppImage" "https://objects.joplinusercontent.com/v${RELEASE_VERSION}/Joplin-${RELEASE_VERSION}.AppImage?source=LinuxInstallScript&type=$DOWNLOAD_TYPE"
$DL "${TEMP_DIR}/joplin.png" https://joplinapp.org/images/Icon512.png
#-----------------------------------------------------
print 'Installing Joplin...'
@@ -287,7 +300,7 @@ echo "$RELEASE_VERSION" > "${INSTALL_DIR}/VERSION"
#-----------------------------------------------------
if [[ "$SHOW_CHANGELOG" == true ]]; then
NOTES=$(wget -qO - https://api.github.com/repos/laurent22/joplin/releases/latest | grep -Po '"body": "\K.*(?=")')
NOTES=$($DL - https://api.github.com/repos/laurent22/joplin/releases/latest | grep -Po '"body": "\K.*(?=")')
print "${COLOR_BLUE}Changelog:${COLOR_RESET}\n${NOTES}"
fi

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://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png"/></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://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 -->
* * *
@@ -42,7 +42,7 @@ 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/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) |
| | | | |
| <img width="50" src="https://avatars2.githubusercontent.com/u/668977?s=96&v=4"/></br>[ugoertz](https://github.com/ugoertz) | | | |
<!-- SPONSORS-GITHUB -->
# Community
@@ -50,10 +50,10 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
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
[Bluesky feed](https://bsky.app/profile/joplinapp.bsky.social) | Follow us on Bluesky
[Mastodon feed](https://mastodon.social/@joplinapp) | Follow us on Mastodon
[YouTube](https://www.youtube.com/@joplinapp) | Discover information and tutorials on how to use the apps
[Discord server](https://discord.gg/VSj7AFHvpq) | Our chat server
[LinkedIn](https://www.linkedin.com/company/joplin) | Our LinkedIn page
[Lemmy Community](https://sopuli.xyz/c/joplinapp) | Also a good place to get help

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

@@ -25,7 +25,8 @@
"version": "latest",
"excluded_platforms": ["aarch64-darwin", "x86_64-darwin"],
},
"git": "latest",
"git": "latest",
"giflib": "latest",
},
"shell": {
"init_hook": [

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

@@ -108,12 +108,14 @@
"app-builder-lib@24.4.0": "patch:app-builder-lib@npm%3A24.4.0#./.yarn/patches/app-builder-lib-npm-24.4.0-05322ff057.patch",
"nanoid": "patch:nanoid@npm%3A3.3.7#./.yarn/patches/nanoid-npm-3.3.7-98824ba130.patch",
"pdfjs-dist": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
"@react-native-community/slider": "patch:@react-native-community/slider@npm%3A4.4.4#./.yarn/patches/@react-native-community-slider-npm-4.4.4-d78e472f48.patch",
"husky": "patch:husky@npm%3A3.1.0#./.yarn/patches/husky-npm-3.1.0-5cc13e4e34.patch",
"chokidar@^2.0.0": "3.5.3",
"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",
"react-native-popup-menu@0.16.1": "patch:react-native-popup-menu@npm%3A0.16.1#./.yarn/patches/react-native-popup-menu-npm-0.16.1-28fd66ecb5.patch"
}
}

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

@@ -1,2 +1,3 @@
<img src="test/" class="jop-noMdConv"/>
<img src="http://example.com/test.png" class="jop-noMdConv"/>
<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

@@ -1,3 +1,5 @@
<img name=getElementById src=test/>
<IMG NAME="getElementById" SRC="http://example.com/test.png">
<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

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

@@ -1,11 +1,12 @@
import Logger, { LoggerWrapper } from '@joplin/utils/Logger';
import Logger, { LoggerWrapper, TargetType } from '@joplin/utils/Logger';
import { PluginMessage } from './services/plugins/PluginRunner';
import AutoUpdaterService, { defaultUpdateInterval, initialUpdateStartup } from './services/autoUpdater/AutoUpdaterService';
import type ShimType from '@joplin/lib/shim';
const shim: typeof ShimType = require('@joplin/lib/shim').default;
import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import { BrowserWindow, Tray, WebContents, screen } from 'electron';
import { FileLocker } from '@joplin/utils/fs';
import { IpcMessageHandler, IpcServer, Message, newHttpError, sendMessage, SendMessageOptions, startServer, stopServer } from '@joplin/utils/ipc';
import { BrowserWindow, Tray, WebContents, screen, App } from 'electron';
import bridge from './bridge';
const url = require('url');
const path = require('path');
@@ -19,6 +20,9 @@ import handleCustomProtocols, { CustomProtocolHandler } from './utils/customProt
import { clearTimeout, setTimeout } from 'timers';
import { resolve } from 'path';
import { defaultWindowId } from '@joplin/lib/reducer';
import { msleep, Second } from '@joplin/utils/time';
import determineBaseAppDirs from '@joplin/lib/determineBaseAppDirs';
import getAppName from '@joplin/lib/getAppName';
interface RendererProcessQuitReply {
canClose: boolean;
@@ -34,13 +38,21 @@ interface SecondaryWindowData {
electronId: number;
}
export interface Options {
env: string;
profilePath: string|null;
isDebugMode: boolean;
isEndToEndTesting: boolean;
initialCallbackUrl: string;
}
export default class ElectronAppWrapper {
private logger_: Logger = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private electronApp_: any;
private electronApp_: App;
private env_: string;
private isDebugMode_: boolean;
private profilePath_: string;
private isEndToEndTesting_: boolean;
private win_: BrowserWindow = null;
private mainWindowHidden_ = true;
@@ -58,13 +70,29 @@ export default class ElectronAppWrapper {
private customProtocolHandler_: CustomProtocolHandler = null;
private updatePollInterval_: ReturnType<typeof setTimeout>|null = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
public constructor(electronApp: any, env: string, profilePath: string|null, isDebugMode: boolean, initialCallbackUrl: string) {
private profileLocker_: FileLocker|null = null;
private ipcServer_: IpcServer|null = null;
private ipcStartPort_ = 2658;
private ipcLogger_: Logger;
public constructor(electronApp: App, { env, profilePath, isDebugMode, initialCallbackUrl, isEndToEndTesting }: Options) {
this.electronApp_ = electronApp;
this.env_ = env;
this.isDebugMode_ = isDebugMode;
this.profilePath_ = profilePath;
this.initialCallbackUrl_ = initialCallbackUrl;
this.isEndToEndTesting_ = isEndToEndTesting;
this.profileLocker_ = new FileLocker(`${this.profilePath_}/lock`);
// Note: in certain contexts `this.logger_` doesn't seem to be available, especially for IPC
// calls, either because it hasn't been set or other issue. So we set one here specifically
// for this.
this.ipcLogger_ = new Logger();
this.ipcLogger_.addTarget(TargetType.File, {
path: `${profilePath}/log-cross-app-ipc.txt`,
});
}
public electronApp() {
@@ -262,15 +290,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) => {
@@ -400,7 +438,7 @@ export default class ElectronAppWrapper {
if (message.target === 'plugin') {
const win = this.pluginWindows_[message.pluginId];
if (!win) {
this.logger().error(`Trying to send IPC message to non-existing plugin window: ${message.pluginId}`);
this.ipcLogger_.error(`Trying to send IPC message to non-existing plugin window: ${message.pluginId}`);
return;
}
@@ -455,12 +493,24 @@ export default class ElectronAppWrapper {
});
}
public quit() {
private onExit() {
this.stopPeriodicUpdateCheck();
this.profileLocker_.unlockSync();
// Probably doesn't matter if the server is not closed cleanly? Thus the lack of `await`
// eslint-disable-next-line promise/prefer-await-to-then -- Needed here because onExit() is not async
void stopServer(this.ipcServer_).catch(_error => {
// Ignore it since we're stopping, and to prevent unnecessary messages.
});
}
public quit() {
this.onExit();
this.electronApp_.quit();
}
public exit(errorCode = 0) {
this.onExit();
this.electronApp_.exit(errorCode);
}
@@ -526,20 +576,32 @@ export default class ElectronAppWrapper {
this.tray_ = null;
}
public ensureSingleInstance() {
if (this.env_ === 'dev') return false;
public async sendCrossAppIpcMessage(message: Message, port: number|null = null, options: SendMessageOptions = null) {
this.ipcLogger_.info('Sending message:', message);
const gotTheLock = this.electronApp_.requestSingleInstanceLock();
if (port === null) port = this.ipcStartPort_;
if (!gotTheLock) {
// Another instance is already running - exit
this.quit();
return true;
return await sendMessage(port, {
...message,
sourcePort: this.ipcServer_.port,
secretKey: this.ipcServer_.secretKey,
}, {
logger: this.ipcLogger_,
...options,
});
}
public async ensureSingleInstance() {
// When end-to-end testing, multiple instances of Joplin are intentionally created at the same time,
// or very close to one another. The single instance handling logic can interfere with this, so disable it.
if (this.isEndToEndTesting_) return false;
interface OnSecondInstanceMessageData {
profilePath: string;
argv: string[];
}
// Someone tried to open a second instance - focus our window instead
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
this.electronApp_.on('second-instance', (_e: any, argv: string[]) => {
const activateWindow = (argv: string[]) => {
const win = this.mainWindow();
if (!win) return;
if (win.isMinimized()) win.restore();
@@ -552,9 +614,96 @@ export default class ElectronAppWrapper {
void this.openCallbackUrl(url);
}
}
};
const messageHandlers: Record<string, IpcMessageHandler> = {
'onSecondInstance': async (message) => {
const data = message.data as OnSecondInstanceMessageData;
if (data.profilePath === this.profilePath_) activateWindow(data.argv);
},
'restartAltInstance': async (message) => {
if (bridge().altInstanceId()) return false;
// We do this in a timeout after a short interval because we need this call to
// return the response immediately, so that the caller can call `quit()`
setTimeout(async () => {
const maxWait = 10000;
const interval = 300;
const loopCount = Math.ceil(maxWait / interval);
let callingAppGone = false;
for (let i = 0; i < loopCount; i++) {
const response = await this.sendCrossAppIpcMessage({
action: 'ping',
data: null,
secretKey: this.ipcServer_.secretKey,
}, message.sourcePort, {
sendToSpecificPortOnly: true,
});
if (!response.length) {
callingAppGone = true;
break;
}
await msleep(interval);
}
if (callingAppGone) {
// Wait a bit more because even if the app is not responding, the process
// might still be there for a short while.
await msleep(1000);
this.ipcLogger_.warn('restartAltInstance: App is gone - restarting it');
void bridge().launchNewAppInstance(this.env());
} else {
this.ipcLogger_.warn('restartAltInstance: Could not restart calling app because it was still open');
}
}, 100);
return true;
},
'ping': async (_message) => {
return true;
},
};
const defaultProfileDir = determineBaseAppDirs('', getAppName(true, this.env() === 'dev'), '').rootProfileDir;
const secretKeyFilePath = `${defaultProfileDir}/ipc_secret_key.txt`;
this.ipcLogger_.info('Starting server using secret key:', secretKeyFilePath);
this.ipcServer_ = await startServer(this.ipcStartPort_, secretKeyFilePath, async (message) => {
if (messageHandlers[message.action]) {
this.ipcLogger_.info('Got message:', message);
return messageHandlers[message.action](message);
}
throw newHttpError(404);
}, {
logger: this.ipcLogger_,
});
return false;
// First check that no other app is running from that profile folder
const gotAppLock = await this.profileLocker_.lock();
if (gotAppLock) return false;
const message: Message = {
action: 'onSecondInstance',
data: {
senderPort: this.ipcServer_.port,
profilePath: this.profilePath_,
argv: process.argv,
},
secretKey: this.ipcServer_.secretKey,
};
await this.sendCrossAppIpcMessage(message);
this.quit();
if (this.env() === 'dev') console.warn(`Closing the application because another instance is already running, or the previous instance was force-quit within the last ${Math.round(this.profileLocker_.options.interval / Second)} seconds.`);
return true;
}
public initializeCustomProtocolHandler(logger: LoggerWrapper) {
@@ -596,7 +745,7 @@ export default class ElectronAppWrapper {
// the "ready" event. So we use the function below to make sure that the app is ready.
await this.waitForElectronAppReady();
const alreadyRunning = this.ensureSingleInstance();
const alreadyRunning = await this.ensureSingleInstance();
if (alreadyRunning) return;
this.createWindow();

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
@@ -638,10 +617,11 @@ class Application extends BaseApplication {
clipperLogger.addTarget(TargetType.Console);
ClipperServer.instance().initialize(actionApi);
ClipperServer.instance().setEnabled(!Setting.value('altInstanceId'));
ClipperServer.instance().setLogger(clipperLogger);
ClipperServer.instance().setDispatch(this.store().dispatch);
if (Setting.value('clipperServer.autoStart')) {
if (ClipperServer.instance().enabled() && Setting.value('clipperServer.autoStart')) {
void ClipperServer.instance().start();
}

View File

@@ -15,6 +15,7 @@ import isSafeToOpen from './utils/isSafeToOpen';
import { closeSync, openSync, readSync, statSync } from 'fs';
import { KB } from '@joplin/utils/bytes';
import { defaultWindowId } from '@joplin/lib/reducer';
import { execCommand } from '@joplin/utils';
interface LastSelectedPath {
file: string;
@@ -43,16 +44,18 @@ export class Bridge {
private appName_: string;
private appId_: string;
private logFilePath_ = '';
private altInstanceId_ = '';
private extraAllowedExtensions_: string[] = [];
private onAllowedExtensionsChangeListener_: OnAllowedExtensionsChange = ()=>{};
public constructor(electronWrapper: ElectronAppWrapper, appId: string, appName: string, rootProfileDir: string, autoUploadCrashDumps: boolean) {
public constructor(electronWrapper: ElectronAppWrapper, appId: string, appName: string, rootProfileDir: string, autoUploadCrashDumps: boolean, altInstanceId: string) {
this.electronWrapper_ = electronWrapper;
this.appId_ = appId;
this.appName_ = appName;
this.rootProfileDir_ = rootProfileDir;
this.autoUploadCrashDumps_ = autoUploadCrashDumps;
this.altInstanceId_ = altInstanceId;
this.lastSelectedPaths_ = {
file: null,
directory: null,
@@ -118,6 +121,8 @@ export class Bridge {
return event;
}
},
integrations: [Sentry.electronMinidumpIntegration()],
};
if (this.autoUploadCrashDumps_) options.dsn = 'https://cceec550871b1e8a10fee4c7a28d5cf2@o4506576757522432.ingest.sentry.io/4506594281783296';
@@ -216,6 +221,10 @@ export class Bridge {
return this.electronApp().electronApp().getLocale();
};
public altInstanceId() {
return this.altInstanceId_;
}
// Applies to electron-context-menu@3:
//
// For now we have to disable spell checking in non-editor text
@@ -489,7 +498,38 @@ export class Bridge {
}
}
public restart(linuxSafeRestart = true) {
public appLaunchCommand(env: string, altInstanceId = '') {
const altInstanceArgs = altInstanceId ? ['--alt-instance-id', altInstanceId] : [];
if (env === 'dev') {
// This is convenient to quickly test on dev, but the path needs to be adjusted
// depending on how things are setup.
return {
execPath: `${homedir()}/.npm-global/bin/electron`,
args: [
`${homedir()}/src/joplin/packages/app-desktop`,
'--env', 'dev',
'--log-level', 'debug',
'--open-dev-tools',
'--no-welcome',
].concat(altInstanceArgs),
};
} else {
return {
execPath: bridge().electronApp().electronApp().getPath('exe'),
args: [].concat(altInstanceArgs),
};
}
}
public async launchNewAppInstance(env: string) {
const cmd = this.appLaunchCommand(env, 'alt1');
await execCommand([cmd.execPath].concat(cmd.args), { detached: true });
}
public async restart() {
// Note that in this case we are not sending the "appClose" event
// to notify services and component that the app is about to close
// but for the current use-case it's not really needed.
@@ -500,13 +540,39 @@ export class Bridge {
execPath: process.env.PORTABLE_EXECUTABLE_FILE,
};
app.relaunch(options);
} else if (shim.isLinux() && linuxSafeRestart) {
this.showInfoMessageBox(_('The app is now going to close. Please relaunch it to complete the process.'));
} else if (this.altInstanceId_) {
// Couldn't get it to work using relaunch() - it would just "close" the app, but it
// would still be open in the tray except unusable. Or maybe it reopens it quickly but
// in a broken state. It might be due to the way it is launched from the main instance.
// So here we ask the main instance to relaunch this app after a short delay.
const responses = await this.electronApp().sendCrossAppIpcMessage({
action: 'restartAltInstance',
data: null,
});
// However is the main instance is not running, we're stuck, so the user needs to
// manually restart. `relaunch()` doesn't appear to work even when the main instance is
// not running.
const r = responses.find(r => !!r.response);
if (!r || !r.response) {
this.showInfoMessageBox(_('The app is now going to close. Please relaunch it to complete the process.'));
// Note: this should work, but doesn't:
// const cmd = this.appLaunchCommand(this.env(), this.altInstanceId_);
// app.relaunch({
// execPath: cmd.execPath,
// args: cmd.args,
// });
}
} else {
app.relaunch();
}
app.exit();
this.electronApp().exit();
}
public createImageFromPath(path: string) {
@@ -532,9 +598,9 @@ export class Bridge {
let bridge_: Bridge = null;
export function initBridge(wrapper: ElectronAppWrapper, appId: string, appName: string, rootProfileDir: string, autoUploadCrashDumps: boolean) {
export function initBridge(wrapper: ElectronAppWrapper, appId: string, appName: string, rootProfileDir: string, autoUploadCrashDumps: boolean, altInstanceId: string) {
if (bridge_) throw new Error('Bridge already initialized');
bridge_ = new Bridge(wrapper, appId, appName, rootProfileDir, autoUploadCrashDumps);
bridge_ = new Bridge(wrapper, appId, appName, rootProfileDir, autoUploadCrashDumps, altInstanceId);
return bridge_;
}

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

@@ -6,6 +6,7 @@ import * as exportDeletionLog from './exportDeletionLog';
import * as exportFolders from './exportFolders';
import * as exportNotes from './exportNotes';
import * as focusElement from './focusElement';
import * as newAppInstance from './newAppInstance';
import * as openNoteInNewWindow from './openNoteInNewWindow';
import * as openProfileDirectory from './openProfileDirectory';
import * as replaceMisspelling from './replaceMisspelling';
@@ -18,6 +19,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,
@@ -27,6 +29,7 @@ const index: any[] = [
exportFolders,
exportNotes,
focusElement,
newAppInstance,
openNoteInNewWindow,
openProfileDirectory,
replaceMisspelling,
@@ -39,6 +42,7 @@ const index: any[] = [
switchProfile3,
toggleExternalEditing,
toggleSafeMode,
toggleTabMovesFocus,
];
export default index;

View File

@@ -0,0 +1,19 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import bridge from '../services/bridge';
import Setting from '@joplin/lib/models/Setting';
export const declaration: CommandDeclaration = {
name: 'newAppInstance',
label: () => _('New application instance...'),
};
export const runtime = (): CommandRuntime => {
return {
execute: async (_context: CommandContext) => {
await bridge().launchNewAppInstance(Setting.value('env'));
},
enabledCondition: '!isAltInstance',
};
};

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

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

@@ -24,6 +24,7 @@ class ClipperConfigScreenComponent extends React.Component {
}
private enableClipperServer_click() {
if (!ClipperServer.instance().enabled()) return;
Setting.setValue('clipperServer.autoStart', true);
void ClipperServer.instance().start();
}
@@ -70,6 +71,8 @@ class ClipperConfigScreenComponent extends React.Component {
const webClipperStatusComps = [];
const clipperEnabled = ClipperServer.instance().enabled();
if (this.props.clipperServerAutoStart) {
webClipperStatusComps.push(
<p key="text_1" style={theme.textStyle}>
@@ -95,13 +98,22 @@ class ClipperConfigScreenComponent extends React.Component {
</button>,
);
} else {
if (!clipperEnabled) {
webClipperStatusComps.push(
<p key="text_4" style={theme.textStyle}>
{_('The web clipper service cannot be enabled in this instance of Joplin.')}
</p>,
);
} else {
webClipperStatusComps.push(
<p key="text_4" style={theme.textStyle}>
{_('The web clipper service is not enabled.')}
</p>,
);
}
webClipperStatusComps.push(
<p key="text_4" style={theme.textStyle}>
{_('The web clipper service is not enabled.')}
</p>,
);
webClipperStatusComps.push(
<button key="enable_button" style={buttonStyle} onClick={this.enableClipperServer_click}>
<button key="enable_button" style={buttonStyle} onClick={this.enableClipperServer_click} disabled={!clipperEnabled}>
{_('Enable Web Clipper Service')}
</button>,
);

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

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

@@ -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;
@@ -82,6 +83,7 @@ interface Props {
notesColumns: NoteListColumns;
showInvalidJoplinCloudCredential: boolean;
toast: Toast;
shouldSwitchToAppleSiliconVersion: boolean;
}
interface ShareFolderDialogOptions {
@@ -121,6 +123,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
@@ -465,6 +479,10 @@ class MainScreenComponent extends React.Component<Props, State> {
});
};
const onDisableSync = () => {
Setting.setValue('sync.target', null);
};
const onViewSyncSettingsScreen = () => {
this.props.dispatch({
type: 'NAV_GO',
@@ -475,6 +493,11 @@ class MainScreenComponent extends React.Component<Props, State> {
});
};
const onDownloadAppleSiliconVersion = () => {
// The website should redirect to the correct version
shim.openUrl('https://joplinapp.org/download/');
};
const onRestartAndUpgrade = async () => {
Setting.setValue('sync.upgradeState', Setting.SYNC_UPGRADE_STATE_MUST_DO);
await Setting.saveAll();
@@ -557,17 +580,32 @@ class MainScreenComponent extends React.Component<Props, State> {
);
} else if (this.props.mustUpgradeAppMessage) {
msg = this.renderNotificationMessage(this.props.mustUpgradeAppMessage);
} else if (this.props.shouldSwitchToAppleSiliconVersion) {
msg = this.renderNotificationMessage(
_('You are running the Intel version of Joplin on an Apple Silicon processor. Download the Apple Silicon one for better performance.'),
_('Download it now'),
onDownloadAppleSiliconVersion,
);
} else if (this.props.showInvalidJoplinCloudCredential) {
msg = this.renderNotificationMessage(
_('Your Joplin Cloud credentials are invalid, please login.'),
_('Login to Joplin Cloud.'),
onViewJoplinCloudLoginScreen,
_('Disable synchronisation'),
onDisableSync,
);
}
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>
);
}
@@ -585,7 +623,8 @@ class MainScreenComponent extends React.Component<Props, State> {
this.showShareInvitationNotification(props) ||
this.props.needApiAuth ||
!!this.props.mustUpgradeAppMessage ||
props.showInvalidJoplinCloudCredential;
props.showInvalidJoplinCloudCredential ||
props.shouldSwitchToAppleSiliconVersion;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -728,6 +767,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 +789,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.')}
/>
@@ -808,6 +852,7 @@ const mapStateToProps = (state: AppState) => {
notesColumns: validateColumns(state.settings['notes.columns']),
showInvalidJoplinCloudCredential: state.settings['sync.target'] === 10 && state.mustAuthenticate,
toast: state.toast,
shouldSwitchToAppleSiliconVersion: shim.isAppleSilicon() && process.arch !== 'arm64',
};
};

View File

@@ -165,12 +165,14 @@ 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
pluginMenus: any[];
['spellChecker.enabled']: boolean;
['spellChecker.languages']: string[];
markdownEditorVisible: boolean;
plugins: PluginStates;
customCss: string;
locale: string;
@@ -256,6 +258,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 +279,8 @@ 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.markdownEditorVisible,
props.tabMovesFocus,
props.noteListRendererId,
props.showNoteCounts,
props.uncompletedTodosOnTop,
@@ -476,6 +481,8 @@ function useMenu(props: Props) {
menuItemDic.focusElementNoteList,
menuItemDic.focusElementNoteTitle,
menuItemDic.focusElementNoteBody,
menuItemDic.focusElementNoteViewer,
menuItemDic.focusElementToolbar,
];
const importItems = [];
@@ -548,6 +555,7 @@ function useMenu(props: Props) {
const newFolderItem = menuItemDic.newFolder;
const newSubFolderItem = menuItemDic.newSubFolder;
const printItem = menuItemDic.print;
const newAppInstance = menuItemDic.newAppInstance;
const switchProfileItem = {
label: _('Switch profile'),
submenu: switchProfileMenuItems,
@@ -711,8 +719,11 @@ function useMenu(props: Props) {
}, {
type: 'separator',
},
printItem,
printItem, {
type: 'separator',
},
switchProfileItem,
newAppInstance,
],
};
@@ -785,6 +796,7 @@ function useMenu(props: Props) {
shim.isMac() ? noItem : menuItemDic.toggleMenuBar,
menuItemDic.toggleNoteList,
menuItemDic.toggleVisiblePanes,
menuItemDic.toggleEditorPlugin,
{
label: _('Layout button sequence'),
submenu: layoutButtonSequenceMenuItems,
@@ -824,6 +836,12 @@ function useMenu(props: Props) {
},
},
separator(),
{
...menuItemDic['toggleTabMovesFocus'],
label: Setting.settingMetadata('editor.tabMovesFocus').label(),
type: 'checkbox',
},
separator(),
{
label: _('Actual Size'),
click: () => {
@@ -877,7 +895,9 @@ function useMenu(props: Props) {
note: {
label: _('&Note'),
submenu: [
menuItemDic.openNoteInNewWindow,
menuItemDic.toggleExternalEditing,
separator(),
menuItemDic.setTags,
menuItemDic.showShareNoteDialog,
separator(),
@@ -987,6 +1007,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) {
@@ -1126,7 +1147,7 @@ function MenuBar(props: Props): any {
const mapStateToProps = (state: AppState): Partial<Props> => {
const whenClauseContext = stateToWhenClauseContext(state);
const whenClauseContext = stateToWhenClauseContext(state, { windowId: state.windowId });
const secondaryWindowFocused = state.windowId !== defaultWindowId;
@@ -1143,6 +1164,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,
@@ -1151,6 +1173,7 @@ const mapStateToProps = (state: AppState): Partial<Props> => {
pluginMenus: stateUtils.selectArrayShallow({ array: pluginUtils.viewsByType(state.pluginService.plugins, 'menu') }, 'menuBar.pluginMenus'),
['spellChecker.languages']: state.settings['spellChecker.languages'],
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
markdownEditorVisible: whenClauseContext.markdownEditorVisible,
plugins: state.pluginService.plugins,
customCss: state.customViewerCss,
profileConfig: state.profileConfig,

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 };

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

@@ -129,6 +129,14 @@ const useEditorCommands = (props: Props) => {
props.webviewRef.current.send('focus');
}
},
'viewer.focus': () => {
if (props.visiblePanes.includes('viewer')) {
const editorCursorLine = editorRef.current.getCursor().line;
props.webviewRef.current.focusLine(editorCursorLine);
} else {
logger.info('Viewer not focused (not visible).');
}
},
search: () => {
return editorRef.current.execCommand(EditorCommandType.ShowSearch);
},

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' },
@@ -720,6 +738,24 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
code: { inline: 'code', remove: 'all', attributes: { spellcheck: 'false' } },
forecolor: { inline: 'span', styles: { color: '%value' } },
},
text_patterns: props.enableTextPatterns ? [
// See https://www.tiny.cloud/docs/tinymce/latest/content-behavior-options/#text_patterns
// for the default value
{ start: '==', end: '==', format: 'joplinHighlight' },
{ start: '`', end: '`', format: 'code' },
{ start: '*', end: '*', format: 'italic' },
{ start: '**', end: '**', format: 'bold' },
{ start: '#', format: 'h1' },
{ start: '##', format: 'h2' },
{ start: '###', format: 'h3' },
{ start: '####', format: 'h4' },
{ start: '#####', format: 'h5' },
{ start: '######', format: 'h6' },
{ start: '1.', cmd: 'InsertOrderedList' },
{ start: '*', cmd: 'InsertUnorderedList' },
{ start: '-', cmd: 'InsertUnorderedList' },
] : [],
setup: (editor: Editor) => {
editor.addCommand('joplinAttach', () => {
insertResourcesIntoContentRef.current();
@@ -739,7 +775,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 +783,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 +842,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 +1067,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 +1084,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 () => {};
@@ -1090,6 +1123,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
};
}, [editor]);
useEffect(() => {
if (!editor) return;
// Meta+P is bound by default to print by TinyMCE. It can be unbound, but it seems necessary
// to do so after the editor loads. Meta+P should be able to trigger Joplin built-in shortcuts.
editor.shortcuts.remove('Meta+P');
}, [editor]);
// -----------------------------------------------------------------------------------------
// Handle onChange event
// -----------------------------------------------------------------------------------------
@@ -1205,9 +1245,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 +1377,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 +1422,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,11 +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');
@@ -74,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);
@@ -92,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'];
@@ -116,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;
@@ -180,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
@@ -200,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) {
@@ -440,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 = {
@@ -488,12 +451,15 @@ function NoteEditorContent(props: NoteEditorProps) {
searchMarkers: searchMarkers,
visiblePanes: props.noteVisiblePanes || ['editor', 'viewer'],
keyboardMode: Setting.value('editor.keyboardMode'),
enableTextPatterns: Setting.value('editor.enableTextPatterns'),
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.
@@ -538,6 +504,7 @@ function NoteEditorContent(props: NoteEditorProps) {
verticalAlign: 'top',
boxSizing: 'border-box',
flex: 1,
overflowX: 'scroll',
};
return (
@@ -688,10 +655,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>
@@ -747,6 +715,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,22 @@
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import { FocusElementOptions } from '../../../commands/focusElement';
import { WindowCommandDependencies } from '../utils/types';
export const declaration: CommandDeclaration = {
name: 'focusElementNoteViewer',
label: () => _('Note viewer'),
parentLabel: () => _('Focus'),
};
export const runtime = (dependencies: WindowCommandDependencies): CommandRuntime => {
return {
execute: async (_context: unknown, options?: FocusElementOptions) => {
await dependencies.editorRef.current.execCommand({
name: 'viewer.focus',
value: options,
});
},
enabledCondition: 'markdownEditorVisible',
};
};

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,8 @@
// AUTO-GENERATED using `gulp buildScriptIndexes`
import * as focusElementNoteBody from './focusElementNoteBody';
import * as focusElementNoteTitle from './focusElementNoteTitle';
import * as focusElementNoteViewer from './focusElementNoteViewer';
import * as focusElementToolbar from './focusElementToolbar';
import * as pasteAsText from './pasteAsText';
import * as showLocalSearch from './showLocalSearch';
import * as showRevisions from './showRevisions';
@@ -8,6 +10,8 @@ import * as showRevisions from './showRevisions';
const index: any[] = [
focusElementNoteBody,
focusElementNoteTitle,
focusElementNoteViewer,
focusElementToolbar,
pasteAsText,
showLocalSearch,
showRevisions,

View File

@@ -163,6 +163,9 @@ const declarations: CommandDeclaration[] = [
{
name: 'editor.execCommand',
},
{
name: 'viewer.focus',
},
];
export default declarations;

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,8 @@ export interface NoteBodyEditorProps {
searchMarkers: SearchMarkers;
visiblePanes: string[];
keyboardMode: string;
tabMovesFocus: boolean;
enableTextPatterns: boolean;
resourceInfos: ResourceInfos;
resourceDirectory: string;
locale: string;
@@ -281,3 +275,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

@@ -97,7 +97,15 @@ const useRefreshFormNoteOnChange = (formNoteRef: RefObject<FormNote>, editorId:
await initNoteState(n, false);
if (event.cancelled) return;
setFormNoteRefreshScheduled(0);
setFormNoteRefreshScheduled(oldValue => {
// If a new refresh was scheduled between initNoteState
// and now:
if (oldValue !== formNoteRefreshScheduled) {
return oldValue;
}
// A refresh is no longer scheduled
return 0;
});
};
await loadNote();

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

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