1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-08 23:07:32 +02:00

Compare commits

...

277 Commits

Author SHA1 Message Date
Laurent Cozic
2dbba27357 Plugin Repo CLI v3.5.3 2025-10-18 10:12:33 +01:00
Laurent Cozic
8713cd2fd8 CLI v3.5.1 2025-10-18 10:11:50 +01:00
Laurent Cozic
d0fc4ea21b Lock file 2025-10-18 10:11:18 +01:00
Laurent Cozic
8bd62800ef Releasing sub-packages 2025-10-18 10:10:42 +01:00
Laurent Cozic
00f9e932e6 Desktop release v3.5.5 2025-10-18 09:53:34 +01:00
Helmut K. C. Tessarek
b8b55e4a55 Chore: Fix generated file line ending (#13459) 2025-10-18 09:50:37 +01:00
renovate[bot]
ef5be2ded3 Update dependency @types/node to v18.19.130 (#13463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 09:49:19 +01:00
Henry Heino
00702dde00 Desktop: OneNote importer: Improve file header validation (#13467) 2025-10-18 09:48:51 +01:00
Henry Heino
2a6af9bed9 Desktop: Accessibility: Allow jumping to notebooks by typing the initial letter or Home/End (#13469) 2025-10-18 09:48:44 +01:00
Henry Heino
c26fe0960b Web: Show sync wizard on first start (#13470) 2025-10-18 09:48:13 +01:00
Henry Heino
ab9d36fc08 Chore: Windows: Tests: Fix Rust OneNote importer tests fail (#13471) 2025-10-18 09:48:03 +01:00
Henry Heino
28eb53bd9f Desktop: OneNote importer: Support directly importing .one files and, on Windows, .onepkg files (#13474)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-18 09:47:35 +01:00
Laurent Cozic
3097c3e589 Desktop, Cli: Correctly import Evernote resources that do not have the encoding specified 2025-10-18 09:44:35 +01:00
renovate[bot]
08371ef718 Update dependency @types/serviceworker to v0.0.144 (#13475)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 08:19:27 +00:00
Laurent Cozic
561716efea Desktop, Cli: Fixed importing certain Evernote images that have invalid dimension attributes (#13472) 2025-10-18 09:17:22 +01:00
renovate[bot]
0d457d1bde Update dependency esbuild to v0.25.7 (#13461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 09:18:57 +01:00
renovate[bot]
8c11f17c93 Update dependency @types/node to v18.19.120 (#13460)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 09:18:50 +01:00
renovate[bot]
f7a90ee1d2 Update dependency @types/serviceworker to v0.0.143 (#13449)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-17 09:18:08 +01:00
Eric Duarte
8822409f7c All: Translation: Update ca.po (#13462) 2025-10-17 03:54:42 -04:00
Eric Duarte
cd3e7f485a All: Translation: Update es_ES.po (#13454) 2025-10-16 17:25:46 -04:00
summoner
8d42b01d4f All: Translation: Update hu_HU.po (#13451) 2025-10-16 17:25:34 -04:00
Henry Heino
2c37197641 Desktop: Resolves #520: Save and restore the cursor position when switching between notes (#13447) 2025-10-16 14:56:38 +01:00
Henry Heino
c2c37b3741 Desktop: Fixes #13411: Fix header links only work if the note viewer is visible (#13442) 2025-10-16 12:10:01 +01:00
Henry Heino
3e770300dc Chore: Desktop: Add tool for resolving stack traces based on source maps (#13427) 2025-10-16 12:09:02 +01:00
Henry Heino
683291d5df Chore: Transcribe: Make logic for starting transcription workers safer (#13425) 2025-10-16 12:08:52 +01:00
Henry Heino
d239035417 Mobile: Resolves #13067: Rich Text Editor: Improve table support (#13413)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-16 12:08:44 +01:00
Henry Heino
5ef37d9de0 Desktop: Support importing .one files from OneNote 2016 (#13391) 2025-10-16 12:08:35 +01:00
mrjo118
1111bde017 All: Fixes #6517: Prevent Joplin from missing changes when syncing with file system or WebDAV (#13054) 2025-10-16 12:06:48 +01:00
Laurent Cozic
468cf00d77 Chore: Clean-up exclusion list in buildTranslation 2025-10-16 11:20:05 +01:00
Laurent Cozic
3c5b41b992 Chore: Fixed CI 2025-10-16 11:19:44 +01:00
Helmut K. C. Tessarek
5f66c51dba All: Update translations 2025-10-15 23:34:40 -04:00
k-santos
bfeaa67ec4 All: Translation: Update pt_BR.po (#13448) 2025-10-15 23:30:16 -04:00
renovate[bot]
348fd0333f Update dependency react-native-share to v12.1.0 (#13446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 21:19:13 +01:00
Henry Heino
51c4d6d6ef Desktop: Upgrade to Electron v37.7.0 (#13445) 2025-10-15 20:39:26 +01:00
Laurent Cozic
09d77a65e8 Plugin Repo CLI v3.5.1 2025-10-15 20:03:29 +01:00
Laurent Cozic
d1aec4a9f7 Plugin Generator release v3.5.1 2025-10-15 20:02:14 +01:00
Henry Heino
cab1525589 Chore: Plugin repository script: Fix certain plugins are not being published (#13443) 2025-10-15 20:00:29 +01:00
Henry Heino
a52f3fea9e Mobile: Resolves: #12823: Disable auto-search for 1-2 character searches (#13444)
Co-authored-by: pedr <pedr@users.noreply.github.com>
2025-10-15 20:00:02 +01:00
renovate[bot]
dfbd5eb8ed Update dependency expo to v53.0.20 (#13441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 14:18:13 +01:00
Laurent Cozic
3131f36033 Chore: Trying to fix random CI failure 2025-10-15 12:55:40 +01:00
renovate[bot]
dc5b2cfa21 Update dependency form-data to v4.0.4 (#13439)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 09:42:15 +00:00
renovate[bot]
cad0f35fcc Update dependency expo-camera to v16.1.11 (#13438)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 02:43:03 +00:00
renovate[bot]
38ea92ff57 Update dependency axios to v1.10.0 (#13431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 14:35:53 +01:00
renovate[bot]
830deada22 Update dependency @types/serviceworker to v0.0.142 (#13434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 18:47:47 +01:00
renovate[bot]
38cd4033ea Update dependency @types/node to v18.19.119 (#13435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 13:36:57 +00:00
Shania
02900752d9 Doc: Missing hashtag in rich_text_editor.md (#13418) 2025-10-11 12:56:26 +01:00
renovate[bot]
091e9813b5 Update dependency @react-native/babel-preset to v0.80.1 (#13426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 01:23:33 +01:00
renovate[bot]
e61e5ac32a Update dependency @react-native/babel-preset to v0.80.0 (#13423)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 21:28:24 +01:00
Joplin Bot
414970c9a1 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-10-10 18:39:00 +00:00
Laurent Cozic
d4ed49ff23 Doc: Clarify how to disable spellchecking on Markdown files 2025-10-10 17:48:37 +01:00
Laurent Cozic
8751d5d152 Doc: Add documentation for LDAP and SAML support in Joplin Server 2025-10-10 17:47:28 +01:00
Laurent Cozic
2e846fe15d Desktop release v3.5.4 2025-10-10 15:48:13 +01:00
Laurent Cozic
e54b7696d9 Chore: Prevent sign tool from being added to the Windows app 2025-10-10 15:48:01 +01:00
Laurent Cozic
553c61d628 Desktop release v3.5.3 2025-10-10 12:12:32 +01:00
Laurent Cozic
6a15db3a36 Chore: Implement SSL eSigner for Windows app signing (#13397) 2025-10-10 11:18:43 +01:00
Laurent Cozic
6f1d0a4b90 Chore: Disable time drift check on Joplin Server tests (#13420) 2025-10-10 11:18:18 +01:00
renovate[bot]
33b995672c Update dependency @playwright/test to v1.53.2 (#13421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 11:18:04 +01:00
mrjo118
8ee46bb4e7 All: Avoid excessive data usage when automatically triggering another sync (#13261) 2025-10-10 09:36:42 +01:00
renovate[bot]
b35d9a64cf Update dependency @playwright/test to v1.53.0 (#13410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 22:36:48 +01:00
renovate[bot]
64ef74dd01 Update dependency @types/node to v18.19.118 (#13412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 22:36:39 +01:00
mrjo118
53035839a5 Desktop, Mobile: Fix historic issue whereby the first revision created for a note does not contain the original contents (#12674)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-09 22:35:08 +01:00
Henry Heino
af5287de99 Desktop: OCR: Fully disable the handwriting transcription backend when disabled in settings (#13072) 2025-10-09 22:21:49 +01:00
mrjo118
45a7554774 All: Fixes #11902: Ensure notebook conflicts do not delete child notes and notebooks when resolved (#13167)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-09 22:21:14 +01:00
Henry Heino
b06ffe3d25 Mobile,Desktop: Resolves #12343: Markdown editor search: Auto-scroll to the next match when the search changes (#13242)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-09 22:20:34 +01:00
mrjo118
53ea51b758 All: Fixes 12810: Ensure the sync shows an error when the server is down, when using a local WebDAV server (#13301) 2025-10-09 21:59:58 +01:00
Tom Chedmail
820acdc1f0 All: Fixes #13328: Implement the config check for Joplin Server with SAML enabled (#13360) 2025-10-09 21:49:18 +01:00
Henry Heino
ef0a79666e Desktop: OneNote importer: Simplify reporting import issues to the forum (#13409) 2025-10-09 21:47:31 +01:00
Henry Heino
d096a90c0e Chore: shim.mobilePlatform: Use a stronger return type (#13415) 2025-10-09 21:46:04 +01:00
renovate[bot]
191775310e Update dependency react-select to v5.10.2 (#13417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 20:42:35 +00:00
renovate[bot]
4fc351b861 Update dependency @react-native-documents/picker to v10.1.5 (#13416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 04:55:42 +00:00
renovate[bot]
396decd26c Update dependency sharp to v0.34.3 (#13404)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-08 13:45:43 +01:00
yingli-lab
01f8fa7bef Desktop: Fixes #13267: Fixed image load failure when path contains '#' (13267) (#13375) 2025-10-08 09:34:52 +01:00
Henry Heino
c40856ac7e Docs: Mobile: Add documentation for the mobile document scanner (#13387) 2025-10-08 09:33:45 +01:00
Henry Heino
d869cce413 Mobile: Document scanner: Add "Recognise text" checkbox (#13398) 2025-10-08 09:33:05 +01:00
Henry Heino
a83e8311d8 Server: Fixes #13400: Fix password fields are always disabled (#13401) 2025-10-08 09:32:24 +01:00
renovate[bot]
aa884fcb39 Update dependency @react-native-documents/picker to v10.1.4 (#13403)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-08 06:58:50 +00:00
Henry Heino
be2a4c3e24 Chore: Correct license information for packages/onenote-converter (#13392) 2025-10-07 10:01:52 +01:00
renovate[bot]
520eec555b Update dependency @types/node to v18.19.117 (#13395)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 05:08:10 +00:00
renovate[bot]
1281fdb9d2 Update dependency @types/node to v18.19.116 (#13394)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 00:55:38 +01:00
renovate[bot]
6029353fd1 Update dependency react-native-webview to v13.15.0 (#13388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 17:49:23 +01:00
Laurent Cozic
8d1d1be79e Doc: Resolves #13370: Add documentation for user profiles (#13377) 2025-10-06 09:45:22 +01:00
bwat47
fd180ae0b4 Desktop: Add write() method to Plugin Clipboard API (#13348) 2025-10-06 09:31:27 +01:00
Laurent Cozic
6fdfd6eae6 Desktop: Resolves #13371: Open the Joplin Plugin web page when clicking on a plugin name (#13376) 2025-10-06 09:30:04 +01:00
Laurent Cozic
cd5bb575c8 Server: Resolves #13369: SAML users cannot modify their own profile at all (#13378) 2025-10-06 09:29:53 +01:00
Laurent Cozic
2df56530ae All: Remove Beta mention for Joplin Server (#13367) 2025-10-06 09:28:24 +01:00
Laurent Cozic
7987137470 Chore: Trying to migrate to macOS 15 on CI (#13366) 2025-10-06 09:28:13 +01:00
Manu Erwin
a1dcd2fd8f Doc: Update trash.md (#13383) 2025-10-06 08:40:10 +01:00
renovate[bot]
7826dc064a Update dependency @types/serviceworker to v0.0.141 (#13385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 08:39:30 +01:00
renovate[bot]
eedf083bfd Update dependency esbuild to v0.25.6 (#13380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-05 20:53:13 +00:00
renovate[bot]
d4aa1f8f8d Update dependency pg-boss to v10.3.2 (#13353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-05 17:57:51 +01:00
Henry Heino
738e749d51 Desktop: Fixes #13346: Fix startup error when a non-English locale is selected (#13347) 2025-10-04 16:08:23 +01:00
renovate[bot]
8fe818c0b0 Update dependency samlify to v2.10.1 (#13362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 09:25:20 +01:00
maggie897
e603452fad Desktop: Fixes #13088: Hide 'Start application minimised' unless tray icon is enabled (#13340) 2025-10-03 14:42:45 +01:00
mrjo118
3827637b54 Mobile: Make the conflicts folder text use the error colour, like is done on desktop (#13343) 2025-10-03 14:41:55 +01:00
Henry Heino
1da7c54e5f Chore: Add test for joplinServerConnected condition (#13352) 2025-10-03 14:32:57 +01:00
Henry Heino
e24ebffba6 Desktop: Resolves #12803: Upgrade tesseract.js to v6 (#13345)
Co-authored-by: pedr <pedr@users.noreply.github.com>
2025-10-03 14:32:15 +01:00
Laurent Cozic
e5bd77836a Desktop release v3.5.2 2025-10-02 09:14:44 +01:00
Henry Heino
8f5e628303 Chore: Fix CI (#13344) 2025-10-02 09:14:14 +01:00
Laurent Cozic
6850c8128b Desktop release v3.5.1 2025-10-02 09:13:48 +01:00
Laurent Cozic
8a797fdf23 Server: Enable publish and share notebook for SAML login 2025-10-02 09:13:29 +01:00
Laurent Cozic
1ae550c0aa Chore: Setup new release 3.5 2025-10-02 09:12:11 +01:00
maggie897
e7e0529f52 Desktop: Resolves #12292: Add hover + expanded arrow behavior for Notebook/Tags header (#13190)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-01 23:20:33 +01:00
Henry Heino
2381e44c7f Chore: OneNote importer: Upgrade to Rust 2024 (#13298)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-10-01 10:22:33 +01:00
renovate[bot]
a59e975f73 Update dependency sharp to v0.34.2 (#13336)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 09:35:07 +01:00
Henry Heino
2d703b6292 Mobile: Resolves #13123: Add sync wizard (#13234) 2025-10-01 09:34:18 +01:00
renovate[bot]
b8db70f707 Update eslint (#13337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 04:17:48 +00:00
Filbert Wijaya
c91513b6b5 All: Translation: Update ja_JP.po (#13339) 2025-10-01 00:15:56 -04:00
Joplin Bot
a57ada97ef Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-10-01 01:51:34 +00:00
Laurent Cozic
d8677a70dd Transcribe: Resolves #12874: Downscale images before storing (#13333) 2025-09-30 23:17:01 +01:00
renovate[bot]
15839a19fd Update dependency rate-limiter-flexible to v6.2.1 (#13334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-30 23:10:14 +01:00
renovate[bot]
8f1d55c1fc Update dependency rate-limiter-flexible to v6 (#13325)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-30 20:03:59 +01:00
JZou-Code
98c18711f7 Desktop: Fixes #12531: Fix the order of attached images (#12531) (#12868) 2025-09-30 17:44:38 +01:00
Henry Heino
24ff4612fb Mobile: Resolves #13104: Accessibility: Allow changing the ALT text of images from the mobile Rich Text Editor (#13169)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-30 17:34:03 +01:00
Henry Heino
f832eb38ff Mobile: Improve inline search performance in large documents (#13259) 2025-09-30 17:22:55 +01:00
Henry Heino
91dc23c23f Android: Plugins: Fix inspecting note editor WebViews (#13272) 2025-09-30 17:17:34 +01:00
Henry Heino
d1913493ab Desktop: Fix "insecure content security policy" warning (#13288)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-30 17:08:48 +01:00
trap000d
fd2b22ed68 Desktop: Resolves #12572: Click on systray icon will show/hide Joplin main window (#13299)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-30 17:05:44 +01:00
Henry Heino
14b56f19df Chore: OneNote converter: Refactor to allow debugging the import process, reduce use of "unsafe" (#13300) 2025-09-30 17:03:38 +01:00
renovate[bot]
0b082a985b Update dependency @react-native-clipboard/clipboard to v1.16.3 (#13304)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-30 16:50:29 +01:00
mrjo118
53dcac22d0 Mobile: For notes over 100,000 characters, make the share note function share them as a file (#13305) 2025-09-30 16:49:54 +01:00
Henry Heino
2c721a76b7 Chore: Fix transcription server build (#13310) 2025-09-30 16:45:16 +01:00
yingli-lab
b68cfd6d9e Desktop: Fixes #13196: Fixed red close button not working on macOS 26 (#13311) 2025-09-30 16:43:58 +01:00
JZou-Code
affebedc4b Desktop: Fixes #12763: skip copy event in TinyMCE if no content is selected. (#13313) 2025-09-30 16:37:44 +01:00
JZou-Code
a714ef4807 Desktop: Fixes #13314: skip cut action in TinyMCE editor if no content is selected. (#13315) 2025-09-30 16:35:38 +01:00
renovate[bot]
596f99aad3 Update dependency @types/node to v18.19.115 (#13332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-30 16:32:47 +01:00
renovate[bot]
c530d35b36 Update dependency @react-native/metro-config to v0.79.5 (#13331)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-30 02:49:10 +00:00
renovate[bot]
5a5c734e2a Update dependency @react-native/babel-preset to v0.79.5 (#13329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 21:56:41 +00:00
renovate[bot]
f7eb483d9a Update dependency react-native-webview to v13.14.2 (#13327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 17:10:00 +01:00
Laurent Cozic
7f3c7e807c Desktop: Use plugin repository URL when homepage URL is not available in config screen (#13318) 2025-09-29 14:35:55 +01:00
renovate[bot]
a50fc02b32 Update dependency react-native-webview to v13.14.0 (#13324)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 14:35:39 +01:00
renovate[bot]
63702e9e34 Update dependency @types/serviceworker to v0.0.140 (#13321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 02:48:55 +00:00
renovate[bot]
92c67aab4e Update dependency @types/node to v18.19.113 (#13320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 02:46:26 +00:00
renovate[bot]
91535870a2 Update dependency react-native-localize to v3.4.2 (#13289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-28 21:48:19 +01:00
renovate[bot]
d4bb277417 Update dependency pg to v8.16.3 (#13296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-28 21:48:06 +01:00
krevad
90f87d1496 All: Translation: Update sv.po (#13316) 2025-09-28 14:44:42 -04:00
Joplin Bot
b07752b3ab Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-28 12:47:37 +00:00
Laurent Cozic
98effef4c5 Server: Improve SAML login error handling and add doc regarding email and displayName attributes 2025-09-28 12:48:56 +01:00
Laurent Cozic
32a919eb81 Chore: Remove obsolete "version" parameter from docker-compose files 2025-09-28 12:44:00 +01:00
Laurent Cozic
e124fd5c9f Chore: Set URLs to absolute ones on release notes and tweak title 2025-09-28 10:23:56 +01:00
Joplin Bot
c5f9290402 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-27 10:41:21 +00:00
Henry Heino
c80cdadc99 Doc: Add release notes for v3.4 (#13273) 2025-09-27 10:20:31 +01:00
Laurent Cozic
d96dcef109 Chore: Trying to fix GitHub Actions size issue (#13290) 2025-09-25 23:33:02 +01:00
Henry Heino
33b889ca38 Chore: Fix APK alignment check in CI (#13294) 2025-09-25 20:48:56 +01:00
Laurent Cozic
fa78ea0173 Server v3.4.4 2025-09-25 14:19:26 +01:00
Laurent Cozic
6705712f80 Server: Use "lax" cookies when using external authentication like SAML or LDAP 2025-09-25 12:12:14 +01:00
renovate[bot]
2785b7f7d9 Update dependency @react-native-community/datetimepicker to v8.4.2 (#13285)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-24 14:10:36 +01:00
Laurent Cozic
f04831406e Server: Trying to make logging more robust 2025-09-24 12:37:49 +01:00
renovate[bot]
fdffc81834 Update dependency @react-native-community/datetimepicker to v8.4.1 (#13279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-24 11:13:51 +01:00
Henry Heino
6f113df2d6 Chore: Sync fuzzer: Fix failures related to publishing/unpublishing notes (#13282) 2025-09-24 11:12:58 +01:00
Henry Heino
8b8b6fbe36 Desktop: Accessibility: Disable sync icon animation when reduce motion is enabled (#13283) 2025-09-24 11:12:38 +01:00
Laurent Cozic
1ef8fd529b Server: Provide more logging information for each request to help debugging issues 2025-09-24 10:46:24 +01:00
Marcus Kida
9547a459cb All: Translation: Update de_DE.po (#13275) 2025-09-23 11:11:34 -04:00
Joplin Bot
be1d092cab Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-23 01:32:34 +00:00
Laurent Cozic
517669ee27 Doc: Update sponsors 2025-09-22 22:29:54 +01:00
Laurent Cozic
72fc97116f Chore: Update renovate.json5 - ignore @react-native-community/cli-platform-ios package 2025-09-22 17:46:53 +01:00
Laurent Cozic
77ca6b3447 Chore: Update renovate.json5 - ignore @react-native-community/cli* packages 2025-09-22 17:46:21 +01:00
renovate[bot]
b227d337d0 Update dependency sass to v1.93.0 (#13255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 17:44:44 +01:00
Henry Heino
a6e671d45b Android: Improve location permission request (#13248) 2025-09-20 10:00:15 +01:00
Henry Heino
47c82a7e75 Chore: Mobile: Fix expo-related warnings in tests (#13260) 2025-09-20 01:25:49 +01:00
summoner
bafa1576f2 All: Translations: Update hu_HU.po (#13256) 2025-09-19 16:11:45 -04:00
Henry Heino
48956df439 Chore: Fix web app build (#13257) 2025-09-19 17:37:28 +01:00
renovate[bot]
4716065295 Update dependency pg to v8.16.2 (#13236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-19 09:24:41 +01:00
renovate[bot]
f801bbfb27 Update dependency sass to v1.89.2 (#13237)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-19 09:24:24 +01:00
Henry Heino
4a043f68ad Chore: Web: Fix build (#13250) 2025-09-19 09:22:22 +01:00
renovate[bot]
cac93e9f9c Update dependency react-native-paper to v5.14.5 (#13230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-18 14:25:08 +01:00
mrjo118
e1e5c9aeb0 Mobile: Remove expandable title field on Web client and fix icon alignment (#13240)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-18 14:24:49 +01:00
Henry Heino
382cb257ab Web: Fix multi-page document creation action fails for non-JPEG images (#13244)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-18 14:23:57 +01:00
Laurent Cozic
6f375be8b9 Server: Pin pm2-logrotate version to prevent supply chain attacks (#13235) 2025-09-18 12:03:57 +01:00
Laurent Cozic
a118615e06 Chore: Remove bitnami repository (#13239) 2025-09-18 10:34:01 +01:00
Joplin Bot
912bf7463f Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-17 18:38:33 +00:00
Laurent Cozic
cfc29832a2 Doc: Update sponsors 2025-09-17 18:07:34 +01:00
renovate[bot]
737fd132e3 Update dependency sass to v1.89.0 (#13231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 16:53:56 +01:00
renovate[bot]
9fc76f4e4c Update dependency pg to v8.16.0 (#13223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 16:27:13 +01:00
renovate[bot]
981f15d85c Update dependency node-mocks-http to v1.17.2 (#13221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 16:26:43 +01:00
renovate[bot]
a59594db3b Update dependency nodejs to v23.11.0 (#13222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 16:26:35 +01:00
JZou-Code
8c8190e2e9 Desktop: Fixes #12239: Prevent the default cut action handler to avoid double deletion (#13208) 2025-09-16 13:22:26 +01:00
Henry Heino
d7e7ff77e8 Chore: Mobile: Add additional logging to help debug toolbar issue (#13224)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-16 13:18:12 +01:00
Henry Heino
e33c142c5a Chore: Web: Skip secondary single-instance check in dev mode (#13225)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-16 12:52:43 +01:00
Laurent Cozic
97d3a8243d Chore: Fix CI (#13227) 2025-09-16 08:50:28 +01:00
renovate[bot]
f1716a3edb Update dependency @pmmmwh/react-refresh-webpack-plugin to ^0.6.0 (#13219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 18:39:20 +01:00
renovate[bot]
1436f5867d Update dependency androidx.documentfile:documentfile to v1.1.0 (#13220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 18:37:53 +01:00
renovate[bot]
d754b8fe0c Update dependency @rollup/plugin-commonjs to v28.0.6 (#13218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 17:05:58 +01:00
renovate[bot]
4f58055cc1 Update dependency @react-native/metro-config to v0.79.4 (#13217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 16:38:15 +01:00
renovate[bot]
98697e1db4 Update dependency style-to-js to v1.1.17 (#13209)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-15 16:30:10 +01:00
renovate[bot]
8ac65a08c1 Update dependency @react-native/babel-preset to v0.79.4 (#13213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 15:06:33 +01:00
renovate[bot]
2b86d83290 Update dependency @babel/plugin-transform-export-namespace-from to v7.27.1 (#13212)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 13:18:34 +01:00
renovate[bot]
09cafe99d1 Update bitnami/postgresql Docker tag to v17.4.0 (#13211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 11:33:22 +01:00
renovate[bot]
6fce844cbf Update dependency webpack-dev-server to v5.2.2 (#13210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 09:01:07 +01:00
renovate[bot]
52de8c071f Update dependency glob to v11.0.3 (#13205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-15 09:00:01 +01:00
renovate[bot]
537543cc8a Update dependency react-native-zip-archive to v7.0.2 (#13207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 00:29:44 +00:00
renovate[bot]
ff16453299 Update dependency js-draw to v1.30.1 (#13206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 00:27:57 +00:00
renovate[bot]
210deec495 Update dependency form-data to v4.0.3 (#13203)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 19:03:16 +01:00
renovate[bot]
e96baea005 Update dependency @types/tar-stream to v3.1.4 (#13202)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 19:03:05 +01:00
renovate[bot]
ae24b91f25 Update dependency @types/node to v18.19.112 (#13204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 19:02:56 +01:00
renovate[bot]
f2e5118bf5 Update dependency @types/serviceworker to v0.0.139 (#13201)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 12:36:40 +00:00
renovate[bot]
72698ec573 Update dependency @react-native/metro-config to v0.79.3 (#13198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-14 09:35:05 +01:00
renovate[bot]
68abc27c6a Update dependency @types/react to v18.3.23 (#13200)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-14 09:34:56 +01:00
renovate[bot]
1acb3d0726 Update dependency @rollup/plugin-commonjs to v28.0.5 (#13199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-14 09:34:48 +01:00
Joplin Bot
5bf97dc3b8 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-14 01:38:57 +00:00
Laurent Cozic
e0e04fbc91 Chore: Fixed type error 2025-09-14 00:44:36 +01:00
Laurent Cozic
625cd1221c Doc: Update sponsors 2025-09-14 00:41:03 +01:00
Henry Heino
110d5bde2d Desktop: Fix error dialogs fail to appear in certain cases (#13179) 2025-09-13 14:21:38 +01:00
renovate[bot]
93a85b3207 Update dependency @types/node to v18.19.111 (#13165)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-13 14:18:04 +01:00
renovate[bot]
ff305f42fd Update dependency @js-draw/material-icons to v1.30.1 (#13164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-13 14:17:50 +01:00
Henry Heino
99ba854ee1 Chore: Cli: Fix CLI app integration tests (#13089) 2025-09-13 14:17:40 +01:00
mrjo118
38b368e997 Mobile: Resolves #12936: Allow expanding and collapsing the title field across multiple lines (#13016) 2025-09-13 14:08:31 +01:00
Henry Heino
f9ffe6c4e6 Desktop,Mobile,Cli: Fix notes are moved to the conflict folder when a folder is unshared (#12993)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-13 14:08:19 +01:00
pedr
5adc0170fc All: Resolves #8718: Delete all note revisions when the note is permanently deleted (#12609) 2025-09-13 14:06:56 +01:00
mrjo118
f54c364b4d Desktop, Mobile: Automatically retrigger the sync if there are more unsynced outgoing changes when sync completes (#12989) 2025-09-13 14:05:31 +01:00
mrjo118
9f541b9b9d Desktop, Mobile: Add support for mixed case tags (#12931)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2025-09-13 14:01:33 +01:00
Henry Heino
bd0af08c57 Docs: REST API: Add descriptions for "is_shared" and "share_id" (#13186) 2025-09-13 13:52:46 +01:00
Henry Heino
ac06c6750d Android: Fixes #13113: Fix compatibility with 16-KB-page-size devices: Remove Vosk (#13189) 2025-09-13 13:52:12 +01:00
renovate[bot]
23b07094b7 Update dependency @react-native/babel-preset to v0.79.3 (#13195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 13:48:57 +01:00
Joplin Bot
7eefc016de Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-13 12:45:33 +00:00
Laurent Cozic
c002be76cd Doc: Update sponsors 2025-09-13 12:00:32 +01:00
renovate[bot]
2cd29aaaea Update dependency react-native-image-picker to v8.2.1 (#13002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2025-09-12 22:48:44 +01:00
Laurent Cozic
4cb6b01c71 Server: Clean-up SAML login section 2025-09-12 15:14:22 +01:00
Joplin Bot
91c79b9488 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-10 12:42:24 +00:00
Henry Heino
fc516d05b3 Docs: Update the Rich Text Editor documentation (#13171) 2025-09-10 10:44:29 +01:00
Henry Heino
2769c9586c Mobile: Fixes #13138: Rich Text Editor: Fix image size lost on change (#13172) 2025-09-10 10:06:40 +01:00
Henry Heino
fd15d5a6d3 Mobile: Rich Text Editor: Accessibility: Fix font size setting not respected (#13174) 2025-09-10 10:05:51 +01:00
krevad
7237d7faa7 All: Translation: Update sv.po (#13170)
Co-authored-by: Helmut K. C. Tessarek <tessarek@evermeet.cx>
2025-09-09 19:37:19 -04:00
Henry Heino
3025d62568 Chore: Fix CI (#13168) 2025-09-09 22:31:55 +01:00
renovate[bot]
5b5dcf34a1 Update dependency @axe-core/playwright to v4.10.2 (#13162)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 09:54:36 +01:00
Laurent Cozic
9e8500c148 Server v3.4.3 2025-09-09 09:47:29 +01:00
Laurent Cozic
4f1999f921 Merge branch 'release-3.4' into dev 2025-09-09 09:46:39 +01:00
krevad
10663b1494 All: Translation: Update sv.po (#13163) 2025-09-09 04:15:15 -04:00
renovate[bot]
eac995a209 Update dependency esbuild to v0.25.5 (#13040)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2025-09-09 00:32:19 +01:00
Henry Heino
15c973e885 Chore: Mobile: Add additional plugin panel integration tests (#13152) 2025-09-09 00:31:12 +01:00
Henry Heino
1762f9485f Web: Fixes #13153: Fix installing certain plugins (#13154) 2025-09-09 00:30:48 +01:00
Henry Heino
7777f8428f Desktop: Upgrade to Electron 37.4.0 (#13156) 2025-09-09 00:30:06 +01:00
Henry Heino
948aa9db4f Mobile: Upgrade react-native-quick-crypto to v0.7.17 (#13155) 2025-09-09 00:07:29 +01:00
Henry Heino
fdde04ee85 Desktop,Mobile,Cli: Support accepting shares with a new key format (#12829) 2025-09-08 23:56:40 +01:00
Laurent Cozic
f77a20f5d5 Merge branch 'release-3.4' into dev 2025-09-08 23:55:24 +01:00
Henry Heino
d43aa2a3e6 Web: Update the beta notice (#13150) 2025-09-08 23:47:19 +01:00
Henry Heino
04d5ce13c2 Chore: Android: Compile Whisper with support for 16 KB pages (#13118) 2025-09-08 16:50:48 +01:00
Laurent Cozic
3b764ba06a Server: Remove the need to install pm2-logrotate on startup so that image can work in a closed environment (#13149) 2025-09-08 16:37:35 +01:00
Henry Heino
5492ce55fa Server: Fixes #12984: Improve handling of concurrent deletion requests for the same item (#13092) 2025-09-08 12:03:20 +01:00
Henry Heino
f6b3f9860c Cli: Fix last change sometimes lost when not in TUI mode (#13090) 2025-09-08 12:03:13 +01:00
Henry Heino
88f687ba6a Chore: Sync fuzzer: Add actions for publishing and unpublishing notes (#13062) 2025-09-08 12:02:53 +01:00
Henry Heino
1f0a98999f Desktop, Mobile: Fixes #12987: Fix images rendered in the Markdown editor don't reload when downloaded (#13045) 2025-09-08 12:01:54 +01:00
mrjo118
69135c3bea Mobile: Fixes #12956: Resize the notes menu to the viewport when the keyboard is open (#13035) 2025-09-08 12:01:24 +01:00
pedr
c27d542a4b Desktop: Fixes #12049: Fix files without extension not being imported properly (#12974)
Co-authored-by: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
2025-09-08 11:46:36 +01:00
mrjo118
bd1c2534c5 Mobile: Fixes #13095: Fix long note title doesn’t wrap properly for To Do type note (#13099) 2025-09-08 11:05:19 +01:00
mrjo118
72513b520c Android: Fixes #13079: Fix dropdown menus are offset on Android 15+ (#13106) 2025-09-08 11:04:46 +01:00
Henry Heino
ec0f9ef9bc Server: Fix unique constraint error when multiple createSharedFolderUserItems are run concurrently (#13112) 2025-09-08 11:03:28 +01:00
Henry Heino
818bc3218a Mobile: Improve tag dialog performance with long tags and many tags (#13117) 2025-09-08 11:03:01 +01:00
Henry Heino
82760a5b6a Web: Show a "Give feedback" banner and link to a survey (#13125) 2025-09-08 10:59:40 +01:00
mrjo118
5ba9a16cfd Mobile: Fixes #13116: Fix tag association screen no longer searches case insensitively or searches tag endings (#13128) 2025-09-08 10:59:01 +01:00
Henry Heino
68fc91fdc7 Desktop: Resolves #13096: Prefer user-specified CSS page sizing when printing to PDF (#13130) 2025-09-08 10:58:16 +01:00
Henry Heino
bdc4687327 Chore: Refactor WebViewController (#13133) 2025-09-08 10:56:51 +01:00
summoner
5e1909cee0 All: Translation: Update hu_HU.po (#13142) 2025-09-08 00:34:54 -04:00
pplulee
2e7b312415 All: Translation: Update zh_CN.po (#13137) 2025-09-06 17:02:31 -04:00
Joplin Bot
7735a59fc1 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-04 18:26:55 +00:00
Laurent Cozic
41d6e912a7 Doc: Updated sponsors 2025-09-04 17:43:49 +02:00
Joplin Bot
4c2fae8423 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-03 01:00:02 +00:00
Laurent Cozic
b72c134890 Doc: Update sponsors 2025-09-02 23:01:06 +02:00
Joplin Bot
58a9c229bb Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-01 18:25:20 +00:00
Laurent Cozic
d8c203bb8a Merge branch 'release-3.4' into dev 2025-09-01 14:48:55 +02:00
Laurent Cozic
9020c07825 lock files 2025-09-01 14:48:51 +02:00
Joplin Bot
d134ea8bfe Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-01 12:33:37 +00:00
Henry Heino
faa44468f3 Mobile: Plugins: Improve handling of invalid toolbar button enabled conditions (#13076) 2025-09-01 13:50:59 +02:00
Joplin Bot
b9c5b8f187 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-09-01 01:12:51 +00:00
Henry Heino
da8e638359 Chore: Mobile: Add test to verify that content scripts load in the note viewer (#13093) 2025-08-31 00:32:10 +02:00
Henry Heino
56ed471a2f Chore: Rich Text Editor: Refactor editor dialog to simplify toggling the dialog from external commands (#13082) 2025-08-29 23:28:11 +02:00
Henry Heino
650594ecea Chore: Sync fuzzer: Add action for deleting notes (#13083) 2025-08-29 23:28:00 +02:00
Eric Duarte
78fb07d4c7 All: Translation: Update ca.po (#13065) 2025-08-28 17:50:34 -04:00
Henry Heino
78c5c4d7c3 Android: Accessibility: Fix tag search input loses focus when submitted by pressing "enter" (#13070) 2025-08-28 09:20:10 +03:00
Henry Heino
57093b35ea Android: Fixes #12960: Rich Text Editor: Fix pressing enter does nothing in some cases (#13075) 2025-08-28 09:03:37 +03:00
Henry Heino
bc2832e78f Chore: Desktop: Allow access to more Joplin APIs from the desktop development tools in dev mode (#13052) 2025-08-27 22:05:52 +03:00
Henry Heino
424cc96d36 Chore: Sync fuzzer: Fix incorrect expected state after removing the last user from a share (#13061) 2025-08-27 22:03:17 +03:00
Henry Heino
56fd5d828f Android: Fixes #12952: External keyboard: Fix adding tags by pressing enter on certain Android devices (#13069) 2025-08-27 22:02:48 +03:00
Henry Heino
03843b087a Desktop: Fixes #12816: Accessibility: Fix dismissing the alarm dialog by pressing escape (#13068) 2025-08-27 22:02:34 +03:00
Joplin Bot
f6851314d2 Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-08-27 18:26:06 +00:00
Laurent Cozic
eaec45cb3f Doc: Update sponsors 2025-08-27 18:38:56 +03:00
Laurent Cozic
9be954496c Doc: Update sponsors 2025-08-27 17:45:40 +03:00
Joplin Bot
98ef5e619b Doc: Auto-update documentation
Auto-updated using release-website.sh
2025-08-27 12:33:13 +00:00
720 changed files with 49996 additions and 34964 deletions

View File

@@ -9,6 +9,7 @@ API_KEY=random-string
QUEUE_TTL=900000
QUEUE_RETRY_COUNT=2
QUEUE_MAINTENANCE_INTERVAL=30000
IMAGE_MAX_DIMENSION=400
HTR_CLI_DOCKER_IMAGE=joplin/htr-cli:latest
# Fullpath to images folder e.g.:

View File

@@ -90,12 +90,13 @@ plugin_types/
readme/
packages/react-native-vosk/lib/
packages/lib/countable/Countable.js
packages/onenote-converter/pkg/onenote_converter.js
packages/onenote-converter/renderer/pkg/*
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-cli/app/LinkSelector.js
packages/app-cli/app/app.js
packages/app-cli/app/base-command.js
packages/app-cli/app/cli-integration-tests.js
packages/app-cli/app/command-apidoc.js
packages/app-cli/app/command-attach.js
packages/app-cli/app/command-batch.js
@@ -269,6 +270,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/localisation.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useRefocusOnVisiblePaneChange.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useSyncEditorValue.js
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
@@ -279,6 +281,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
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/useCursorPositioning.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
@@ -320,6 +323,7 @@ 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/useInitialCursorLocation.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
@@ -603,6 +607,7 @@ packages/app-desktop/tools/generateLatestArm64Yml.js
packages/app-desktop/tools/githubReleasesUtils.js
packages/app-desktop/tools/modifyReleaseAssets.js
packages/app-desktop/tools/notarizeMacApp.js
packages/app-desktop/tools/resolveSourceMap.js
packages/app-desktop/utils/7zip/getPathToExecutable7Zip.js
packages/app-desktop/utils/7zip/pathToBundled7Zip.js
packages/app-desktop/utils/checkForUpdatesUtils.test.js
@@ -676,6 +681,8 @@ packages/app-mobile/components/ExtendedWebView/index.js
packages/app-mobile/components/ExtendedWebView/index.web.js
packages/app-mobile/components/ExtendedWebView/types.js
packages/app-mobile/components/ExtendedWebView/utils/useCss.js
packages/app-mobile/components/FeedbackBanner.test.js
packages/app-mobile/components/FeedbackBanner.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/Icon.js
packages/app-mobile/components/IconButton.js
@@ -722,6 +729,8 @@ packages/app-mobile/components/SearchInput.js
packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenuContentNote.js
packages/app-mobile/components/SyncWizard/JoplinCloudIcon.js
packages/app-mobile/components/SyncWizard/SyncWizard.js
packages/app-mobile/components/TagEditor.test.js
packages/app-mobile/components/TagEditor.js
packages/app-mobile/components/TextInput.js
@@ -738,6 +747,7 @@ packages/app-mobile/components/base-screen.js
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/buttons/CardButton.js
packages/app-mobile/components/buttons/FloatingActionButton.js
packages/app-mobile/components/buttons/LabelledIconButton.js
packages/app-mobile/components/buttons/MultiTouchableOpacity.js
@@ -813,7 +823,6 @@ packages/app-mobile/components/screens/ConfigScreen/plugins/buttons/InstallButto
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/WrappedPluginStates.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/mockRepositoryApiConstructor.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/newRepoApi.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/pluginServiceSetup.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/openWebsiteForPlugin.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/usePluginCallbacks.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/usePluginItem.js
@@ -839,6 +848,7 @@ packages/app-mobile/components/screens/NoteTagsDialog.js
packages/app-mobile/components/screens/Notes/NewNoteButton.test.js
packages/app-mobile/components/screens/Notes/NewNoteButton.js
packages/app-mobile/components/screens/Notes/Notes.js
packages/app-mobile/components/screens/SearchScreen/SearchResults.test.js
packages/app-mobile/components/screens/SearchScreen/SearchResults.js
packages/app-mobile/components/screens/SearchScreen/index.js
packages/app-mobile/components/screens/ShareManager/AcceptedShareItem.js
@@ -901,14 +911,13 @@ packages/app-mobile/services/AlarmServiceDriver.web.js
packages/app-mobile/services/BackButtonService.js
packages/app-mobile/services/commands/stateToWhenClauseContext.js
packages/app-mobile/services/e2ee/RSA.react-native.js
packages/app-mobile/services/e2ee/RSA.react-native.web.js
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/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
@@ -922,6 +931,7 @@ packages/app-mobile/utils/ShareUtils.test.js
packages/app-mobile/utils/ShareUtils.js
packages/app-mobile/utils/TlsUtils.js
packages/app-mobile/utils/appDefaultState.js
packages/app-mobile/utils/appReducer.js
packages/app-mobile/utils/autodetectTheme.js
packages/app-mobile/utils/buildStartupTasks.js
packages/app-mobile/utils/checkPermissions.js
@@ -961,6 +971,7 @@ packages/app-mobile/utils/pickDocument.js
packages/app-mobile/utils/polyfills/bufferPolyfill.js
packages/app-mobile/utils/polyfills/crypto-polyfill/index.js
packages/app-mobile/utils/polyfills/index.js
packages/app-mobile/utils/polyfills/index.web.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareFile.js
packages/app-mobile/utils/shareHandler.js
@@ -971,6 +982,7 @@ packages/app-mobile/utils/shim-init-react/shimInitShared.js
packages/app-mobile/utils/testing/createMockReduxStore.js
packages/app-mobile/utils/testing/getWebViewDomById.js
packages/app-mobile/utils/testing/getWebViewWindowById.js
packages/app-mobile/utils/testing/mockPluginServiceSetup.js
packages/app-mobile/utils/testing/setupGlobalStore.js
packages/app-mobile/utils/testing/testingLibrary.js
packages/app-mobile/utils/types.js
@@ -1042,6 +1054,7 @@ packages/editor/CodeMirror/extensions/rendering/types.js
packages/editor/CodeMirror/extensions/rendering/utils/makeBlockReplaceExtension.js
packages/editor/CodeMirror/extensions/rendering/utils/makeInlineReplaceExtension.js
packages/editor/CodeMirror/extensions/rendering/utils/nodeIntersectsSelection.js
packages/editor/CodeMirror/extensions/searchExtension.test.js
packages/editor/CodeMirror/extensions/searchExtension.js
packages/editor/CodeMirror/extensions/selectedNoteIdExtension.js
packages/editor/CodeMirror/getScrollFraction.js
@@ -1088,12 +1101,16 @@ packages/editor/CodeMirror/utils/markdown/renumberSelectedLists.js
packages/editor/CodeMirror/utils/markdown/stripBlockquote.js
packages/editor/CodeMirror/utils/markdown/toggleCheckboxAt.js
packages/editor/CodeMirror/utils/setupVim.js
packages/editor/ProseMirror/commands.test.js
packages/editor/ProseMirror/commands.js
packages/editor/CodeMirror/vendor/announceSearchMatch.js
packages/editor/ProseMirror/commands/commands.test.js
packages/editor/ProseMirror/commands/commands.js
packages/editor/ProseMirror/commands/focusEditor.js
packages/editor/ProseMirror/createEditor.js
packages/editor/ProseMirror/index.js
packages/editor/ProseMirror/plugins/detailsPlugin.test.js
packages/editor/ProseMirror/plugins/detailsPlugin.js
packages/editor/ProseMirror/plugins/imagePlugin.test.js
packages/editor/ProseMirror/plugins/imagePlugin.js
packages/editor/ProseMirror/plugins/inputRulesPlugin.js
packages/editor/ProseMirror/plugins/joplinEditablePlugin/createEditorDialog.js
packages/editor/ProseMirror/plugins/joplinEditablePlugin/joplinEditablePlugin.test.js
@@ -1105,12 +1122,16 @@ packages/editor/ProseMirror/plugins/linkTooltipPlugin.test.js
packages/editor/ProseMirror/plugins/linkTooltipPlugin.js
packages/editor/ProseMirror/plugins/listPlugin.js
packages/editor/ProseMirror/plugins/originalMarkupPlugin.js
packages/editor/ProseMirror/plugins/resourcePlaceholderPlugin.js
packages/editor/ProseMirror/plugins/searchPlugin.js
packages/editor/ProseMirror/plugins/tablePlugin.js
packages/editor/ProseMirror/plugins/utils/createExternalEditorPlugin.js
packages/editor/ProseMirror/plugins/utils/createFloatingButtonPlugin.js
packages/editor/ProseMirror/schema.js
packages/editor/ProseMirror/styles.js
packages/editor/ProseMirror/testing/createTestEditor.js
packages/editor/ProseMirror/testing/createTestEditorWithSerializer.js
packages/editor/ProseMirror/types.js
packages/editor/ProseMirror/utils/SelectableNodeView.js
packages/editor/ProseMirror/utils/UndoStackSynchronizer.js
packages/editor/ProseMirror/utils/canReplaceSelectionWith.js
packages/editor/ProseMirror/utils/computeSelectionFormatting.js
@@ -1118,6 +1139,7 @@ packages/editor/ProseMirror/utils/dom/createButton.js
packages/editor/ProseMirror/utils/dom/createTextArea.js
packages/editor/ProseMirror/utils/dom/createTextNode.js
packages/editor/ProseMirror/utils/dom/createUniqueId.js
packages/editor/ProseMirror/utils/dom/showModal.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
packages/editor/ProseMirror/utils/forEachHeading.js
@@ -1128,8 +1150,15 @@ packages/editor/ProseMirror/utils/postprocessEditorOutput.js
packages/editor/ProseMirror/utils/preprocessEditorInput.test.js
packages/editor/ProseMirror/utils/preprocessEditorInput.js
packages/editor/ProseMirror/utils/sanitizeHtml.js
packages/editor/ProseMirror/utils/selectFirstInstanceOfNode.js
packages/editor/ProseMirror/utils/trimEmptyParagraphs.js
packages/editor/ProseMirror/vendor/changedDescendants.js
packages/editor/ProseMirror/vendor/icons/addColumnRight.js
packages/editor/ProseMirror/vendor/icons/addRowBelow.js
packages/editor/ProseMirror/vendor/icons/icon.js
packages/editor/ProseMirror/vendor/icons/removeColumn.js
packages/editor/ProseMirror/vendor/icons/removeRow.js
packages/editor/ProseMirror/vendor/icons/types.js
packages/editor/ProseMirror/vendor/splitBlockAs.js
packages/editor/SelectionFormatting.js
packages/editor/events.js
@@ -1398,20 +1427,26 @@ packages/lib/services/database/migrations/45.js
packages/lib/services/database/migrations/46.js
packages/lib/services/database/migrations/47.js
packages/lib/services/database/migrations/48.js
packages/lib/services/database/migrations/49.js
packages/lib/services/database/migrations/index.js
packages/lib/services/database/sqlStringToLines.js
packages/lib/services/database/types.js
packages/lib/services/debug/populateDatabase.js
packages/lib/services/e2ee/EncryptionService.test.js
packages/lib/services/e2ee/EncryptionService.js
packages/lib/services/e2ee/RSA.node.js
packages/lib/services/e2ee/crypto.test.js
packages/lib/services/e2ee/crypto.js
packages/lib/services/e2ee/cryptoShared.js
packages/lib/services/e2ee/cryptoTestUtils.js
packages/lib/services/e2ee/ppk.test.js
packages/lib/services/e2ee/ppk.js
packages/lib/services/e2ee/ppkTestUtils.js
packages/lib/services/e2ee/ppk/RSA.node.js
packages/lib/services/e2ee/ppk/ppk.test.js
packages/lib/services/e2ee/ppk/ppk.js
packages/lib/services/e2ee/ppk/ppkTestUtils.js
packages/lib/services/e2ee/ppk/webCrypto/LongDataWrapper.js
packages/lib/services/e2ee/ppk/webCrypto/StringToBufferWrapper.js
packages/lib/services/e2ee/ppk/webCrypto/WebCryptoRsa.js
packages/lib/services/e2ee/ppk/webCrypto/buildRsaCryptoProvider.test.js
packages/lib/services/e2ee/ppk/webCrypto/buildRsaCryptoProvider.js
packages/lib/services/e2ee/types.js
packages/lib/services/e2ee/utils.test.js
packages/lib/services/e2ee/utils.js
@@ -1516,6 +1551,7 @@ packages/lib/services/plugins/utils/createViewHandle.js
packages/lib/services/plugins/utils/executeSandboxCall.js
packages/lib/services/plugins/utils/getActivePluginEditorView.js
packages/lib/services/plugins/utils/getActivePluginEditorViews.js
packages/lib/services/plugins/utils/getPluginHelpUrl.js
packages/lib/services/plugins/utils/getPluginIssueReportUrl.test.js
packages/lib/services/plugins/utils/getPluginIssueReportUrl.js
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
@@ -1720,6 +1756,7 @@ packages/plugin-repo-cli/lib/gitCompareUrl.test.js
packages/plugin-repo-cli/lib/gitCompareUrl.js
packages/plugin-repo-cli/lib/overrideUtils.test.js
packages/plugin-repo-cli/lib/overrideUtils.js
packages/plugin-repo-cli/lib/searchPlugins.js
packages/plugin-repo-cli/lib/types.js
packages/plugin-repo-cli/lib/updateReadme.test.js
packages/plugin-repo-cli/lib/updateReadme.js
@@ -1835,6 +1872,8 @@ packages/tools/updateMarkdownDoc.js
packages/tools/utils/discourse.test.js
packages/tools/utils/discourse.js
packages/tools/utils/loadSponsors.js
packages/tools/utils/parsePluralLocalizationForm.js
packages/tools/utils/parsePlurallLocalizationForm.test.js
packages/tools/utils/translation.js
packages/tools/validateFilenames.js
packages/tools/website/build.js

View File

@@ -40,4 +40,29 @@ jobs:
cd packages/app-mobile/android
sed -i -- 's/signingConfig signingConfigs.release/signingConfig signingConfigs.debug/' app/build.gradle
./gradlew assembleRelease
- name: Verify alignment
run: |
cd packages/app-mobile/android/app
APK_FILE="./build/outputs/apk/release/app-release.apk"
if test ! -f "$APK_FILE" ; then
echo "APK file not found."
exit 1
else
echo "APK file found at: $APK_FILE"
fi
BUILD_TOOLS_PATH="$ANDROID_HOME/build-tools/"
if test ! -d "$BUILD_TOOLS_PATH" ; then
echo "Build tools not found at $BUILD_TOOLS_PATH ($ANDROID_HOME, $BUILD_TOOLS_VERSION)"
exit 1
fi
# The build-tools/ directory contains different subdirectories
# for each build tools version. As a result, there may be multiple
# zipalign tools. Select the most recent (biggest two-digit version number):
ZIPALIGN_PATH="$(find $BUILD_TOOLS_PATH -name "zipalign" -print | sort | tail -n1)"
if test ! -x "$ZIPALIGN_PATH" ; then
echo "zipalign not found (searching in $BUILD_TOOLS_PATH, candidate: $ZIPALIGN_PATH)"
exit 1
fi
"$ZIPALIGN_PATH" -c -P 16 -v 4 "$APK_FILE"

View File

@@ -9,7 +9,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: [macos-13, ubuntu-22.04, windows-2025, ubuntu-22.04-arm]
os: [macos-15-intel, ubuntu-22.04, windows-2025, ubuntu-22.04-arm]
steps:
- uses: actions/checkout@v4
@@ -31,6 +31,16 @@ jobs:
sudo apt-get update || true
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
- name: Free disk space
if: runner.os == 'Linux'
run: |
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /opt/ghc || true
docker system prune -af || true
docker builder prune -af || true
sudo rm -rf /var/lib/docker/tmp/* || true
# 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
@@ -40,6 +50,22 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# - name: Test Windows app signing
# if: runner.os == 'Windows'
# env:
# GH_TOKEN: ${{ secrets.GH_TOKEN }}
# IS_CONTINUOUS_INTEGRATION: 1
# BUILD_SEQUENCIAL: 1
# SSL_ESIGNER_USER_NAME: ${{ secrets.SSL_ESIGNER_USER_NAME }}
# SSL_ESIGNER_USER_PASSWORD: ${{ secrets.SSL_ESIGNER_USER_PASSWORD }}
# SSL_ESIGNER_CREDENTIAL_ID: ${{ secrets.SSL_ESIGNER_CREDENTIAL_ID }}
# SSL_ESIGNER_USER_TOTP: ${{ secrets.SSL_ESIGNER_USER_TOTP }}
# SIGN_APPLICATION: 1
# # To ensure that the operations stop on failure, all commands
# # should be on one line with "&&" in between.
# run: |
# yarn install && cd packages/app-desktop && yarn dist
- name: Run tests, build and publish Linux and macOS apps
if: runner.os == 'Linux' || runner.os == 'macOs'
env:
@@ -61,11 +87,14 @@ jobs:
- name: Build and publish Windows app
if: runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/v')
env:
CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.WINDOWS_CSC_LINK }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
IS_CONTINUOUS_INTEGRATION: 1
BUILD_SEQUENCIAL: 1
SSL_ESIGNER_USER_NAME: ${{ secrets.SSL_ESIGNER_USER_NAME }}
SSL_ESIGNER_USER_PASSWORD: ${{ secrets.SSL_ESIGNER_USER_PASSWORD }}
SSL_ESIGNER_CREDENTIAL_ID: ${{ secrets.SSL_ESIGNER_CREDENTIAL_ID }}
SSL_ESIGNER_USER_TOTP: ${{ secrets.SSL_ESIGNER_USER_TOTP }}
SIGN_APPLICATION: 1
# To ensure that the operations stop on failure, all commands
# should be on one line with "&&" in between.
run: |
@@ -122,6 +151,16 @@ jobs:
with:
node-version: '18'
- name: Free disk space
if: runner.os == 'Linux'
run: |
sudo rm -rf /usr/local/lib/android || true
sudo rm -rf /usr/share/dotnet || true
sudo rm -rf /opt/ghc || true
docker system prune -af || true
docker builder prune -af || true
sudo rm -rf /var/lib/docker/tmp/* || true
- name: Install Yarn
run: |
# https://yarnpkg.com/getting-started/install
@@ -149,7 +188,7 @@ jobs:
- name: Check HTTP request
run: |
# Need to pass environment variables:
docker run -p 22300:22300 joplin/server:$(dpkg --print-architecture)-0.0.0 node dist/app.js --env dev &
docker run --env MAX_TIME_DRIFT=0 --publish 22300:22300 joplin/server:$(dpkg --print-architecture)-0.0.0 node dist/app.js --env dev &
# Wait for server to start
sleep 120
@@ -175,5 +214,4 @@ jobs:
if [[ "$actual_body" != "$expected_body" ]]; then
echo 'Failed while checking the body response after request to /api/ping'
exit 1;
fi
fi

59
.gitignore vendored
View File

@@ -69,6 +69,7 @@ docs/**/*.mustache
packages/app-cli/app/LinkSelector.js
packages/app-cli/app/app.js
packages/app-cli/app/base-command.js
packages/app-cli/app/cli-integration-tests.js
packages/app-cli/app/command-apidoc.js
packages/app-cli/app/command-attach.js
packages/app-cli/app/command-batch.js
@@ -242,6 +243,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/useEditorCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/localisation.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useKeymap.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useRefocusOnVisiblePaneChange.js
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useSyncEditorValue.js
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
@@ -252,6 +254,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
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/useCursorPositioning.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
@@ -293,6 +296,7 @@ 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/useInitialCursorLocation.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
@@ -576,6 +580,7 @@ packages/app-desktop/tools/generateLatestArm64Yml.js
packages/app-desktop/tools/githubReleasesUtils.js
packages/app-desktop/tools/modifyReleaseAssets.js
packages/app-desktop/tools/notarizeMacApp.js
packages/app-desktop/tools/resolveSourceMap.js
packages/app-desktop/utils/7zip/getPathToExecutable7Zip.js
packages/app-desktop/utils/7zip/pathToBundled7Zip.js
packages/app-desktop/utils/checkForUpdatesUtils.test.js
@@ -649,6 +654,8 @@ packages/app-mobile/components/ExtendedWebView/index.js
packages/app-mobile/components/ExtendedWebView/index.web.js
packages/app-mobile/components/ExtendedWebView/types.js
packages/app-mobile/components/ExtendedWebView/utils/useCss.js
packages/app-mobile/components/FeedbackBanner.test.js
packages/app-mobile/components/FeedbackBanner.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/Icon.js
packages/app-mobile/components/IconButton.js
@@ -695,6 +702,8 @@ packages/app-mobile/components/SearchInput.js
packages/app-mobile/components/SelectDateTimeDialog.js
packages/app-mobile/components/SideMenu.js
packages/app-mobile/components/SideMenuContentNote.js
packages/app-mobile/components/SyncWizard/JoplinCloudIcon.js
packages/app-mobile/components/SyncWizard/SyncWizard.js
packages/app-mobile/components/TagEditor.test.js
packages/app-mobile/components/TagEditor.js
packages/app-mobile/components/TextInput.js
@@ -711,6 +720,7 @@ packages/app-mobile/components/base-screen.js
packages/app-mobile/components/biometrics/BiometricPopup.js
packages/app-mobile/components/biometrics/biometricAuthenticate.js
packages/app-mobile/components/biometrics/sensorInfo.js
packages/app-mobile/components/buttons/CardButton.js
packages/app-mobile/components/buttons/FloatingActionButton.js
packages/app-mobile/components/buttons/LabelledIconButton.js
packages/app-mobile/components/buttons/MultiTouchableOpacity.js
@@ -786,7 +796,6 @@ packages/app-mobile/components/screens/ConfigScreen/plugins/buttons/InstallButto
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/WrappedPluginStates.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/mockRepositoryApiConstructor.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/newRepoApi.js
packages/app-mobile/components/screens/ConfigScreen/plugins/testUtils/pluginServiceSetup.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/openWebsiteForPlugin.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/usePluginCallbacks.js
packages/app-mobile/components/screens/ConfigScreen/plugins/utils/usePluginItem.js
@@ -812,6 +821,7 @@ packages/app-mobile/components/screens/NoteTagsDialog.js
packages/app-mobile/components/screens/Notes/NewNoteButton.test.js
packages/app-mobile/components/screens/Notes/NewNoteButton.js
packages/app-mobile/components/screens/Notes/Notes.js
packages/app-mobile/components/screens/SearchScreen/SearchResults.test.js
packages/app-mobile/components/screens/SearchScreen/SearchResults.js
packages/app-mobile/components/screens/SearchScreen/index.js
packages/app-mobile/components/screens/ShareManager/AcceptedShareItem.js
@@ -874,14 +884,13 @@ packages/app-mobile/services/AlarmServiceDriver.web.js
packages/app-mobile/services/BackButtonService.js
packages/app-mobile/services/commands/stateToWhenClauseContext.js
packages/app-mobile/services/e2ee/RSA.react-native.js
packages/app-mobile/services/e2ee/RSA.react-native.web.js
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/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
@@ -895,6 +904,7 @@ packages/app-mobile/utils/ShareUtils.test.js
packages/app-mobile/utils/ShareUtils.js
packages/app-mobile/utils/TlsUtils.js
packages/app-mobile/utils/appDefaultState.js
packages/app-mobile/utils/appReducer.js
packages/app-mobile/utils/autodetectTheme.js
packages/app-mobile/utils/buildStartupTasks.js
packages/app-mobile/utils/checkPermissions.js
@@ -934,6 +944,7 @@ packages/app-mobile/utils/pickDocument.js
packages/app-mobile/utils/polyfills/bufferPolyfill.js
packages/app-mobile/utils/polyfills/crypto-polyfill/index.js
packages/app-mobile/utils/polyfills/index.js
packages/app-mobile/utils/polyfills/index.web.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareFile.js
packages/app-mobile/utils/shareHandler.js
@@ -944,6 +955,7 @@ packages/app-mobile/utils/shim-init-react/shimInitShared.js
packages/app-mobile/utils/testing/createMockReduxStore.js
packages/app-mobile/utils/testing/getWebViewDomById.js
packages/app-mobile/utils/testing/getWebViewWindowById.js
packages/app-mobile/utils/testing/mockPluginServiceSetup.js
packages/app-mobile/utils/testing/setupGlobalStore.js
packages/app-mobile/utils/testing/testingLibrary.js
packages/app-mobile/utils/types.js
@@ -1015,6 +1027,7 @@ packages/editor/CodeMirror/extensions/rendering/types.js
packages/editor/CodeMirror/extensions/rendering/utils/makeBlockReplaceExtension.js
packages/editor/CodeMirror/extensions/rendering/utils/makeInlineReplaceExtension.js
packages/editor/CodeMirror/extensions/rendering/utils/nodeIntersectsSelection.js
packages/editor/CodeMirror/extensions/searchExtension.test.js
packages/editor/CodeMirror/extensions/searchExtension.js
packages/editor/CodeMirror/extensions/selectedNoteIdExtension.js
packages/editor/CodeMirror/getScrollFraction.js
@@ -1061,12 +1074,16 @@ packages/editor/CodeMirror/utils/markdown/renumberSelectedLists.js
packages/editor/CodeMirror/utils/markdown/stripBlockquote.js
packages/editor/CodeMirror/utils/markdown/toggleCheckboxAt.js
packages/editor/CodeMirror/utils/setupVim.js
packages/editor/ProseMirror/commands.test.js
packages/editor/ProseMirror/commands.js
packages/editor/CodeMirror/vendor/announceSearchMatch.js
packages/editor/ProseMirror/commands/commands.test.js
packages/editor/ProseMirror/commands/commands.js
packages/editor/ProseMirror/commands/focusEditor.js
packages/editor/ProseMirror/createEditor.js
packages/editor/ProseMirror/index.js
packages/editor/ProseMirror/plugins/detailsPlugin.test.js
packages/editor/ProseMirror/plugins/detailsPlugin.js
packages/editor/ProseMirror/plugins/imagePlugin.test.js
packages/editor/ProseMirror/plugins/imagePlugin.js
packages/editor/ProseMirror/plugins/inputRulesPlugin.js
packages/editor/ProseMirror/plugins/joplinEditablePlugin/createEditorDialog.js
packages/editor/ProseMirror/plugins/joplinEditablePlugin/joplinEditablePlugin.test.js
@@ -1078,12 +1095,16 @@ packages/editor/ProseMirror/plugins/linkTooltipPlugin.test.js
packages/editor/ProseMirror/plugins/linkTooltipPlugin.js
packages/editor/ProseMirror/plugins/listPlugin.js
packages/editor/ProseMirror/plugins/originalMarkupPlugin.js
packages/editor/ProseMirror/plugins/resourcePlaceholderPlugin.js
packages/editor/ProseMirror/plugins/searchPlugin.js
packages/editor/ProseMirror/plugins/tablePlugin.js
packages/editor/ProseMirror/plugins/utils/createExternalEditorPlugin.js
packages/editor/ProseMirror/plugins/utils/createFloatingButtonPlugin.js
packages/editor/ProseMirror/schema.js
packages/editor/ProseMirror/styles.js
packages/editor/ProseMirror/testing/createTestEditor.js
packages/editor/ProseMirror/testing/createTestEditorWithSerializer.js
packages/editor/ProseMirror/types.js
packages/editor/ProseMirror/utils/SelectableNodeView.js
packages/editor/ProseMirror/utils/UndoStackSynchronizer.js
packages/editor/ProseMirror/utils/canReplaceSelectionWith.js
packages/editor/ProseMirror/utils/computeSelectionFormatting.js
@@ -1091,6 +1112,7 @@ packages/editor/ProseMirror/utils/dom/createButton.js
packages/editor/ProseMirror/utils/dom/createTextArea.js
packages/editor/ProseMirror/utils/dom/createTextNode.js
packages/editor/ProseMirror/utils/dom/createUniqueId.js
packages/editor/ProseMirror/utils/dom/showModal.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
packages/editor/ProseMirror/utils/forEachHeading.js
@@ -1101,8 +1123,15 @@ packages/editor/ProseMirror/utils/postprocessEditorOutput.js
packages/editor/ProseMirror/utils/preprocessEditorInput.test.js
packages/editor/ProseMirror/utils/preprocessEditorInput.js
packages/editor/ProseMirror/utils/sanitizeHtml.js
packages/editor/ProseMirror/utils/selectFirstInstanceOfNode.js
packages/editor/ProseMirror/utils/trimEmptyParagraphs.js
packages/editor/ProseMirror/vendor/changedDescendants.js
packages/editor/ProseMirror/vendor/icons/addColumnRight.js
packages/editor/ProseMirror/vendor/icons/addRowBelow.js
packages/editor/ProseMirror/vendor/icons/icon.js
packages/editor/ProseMirror/vendor/icons/removeColumn.js
packages/editor/ProseMirror/vendor/icons/removeRow.js
packages/editor/ProseMirror/vendor/icons/types.js
packages/editor/ProseMirror/vendor/splitBlockAs.js
packages/editor/SelectionFormatting.js
packages/editor/events.js
@@ -1371,20 +1400,26 @@ packages/lib/services/database/migrations/45.js
packages/lib/services/database/migrations/46.js
packages/lib/services/database/migrations/47.js
packages/lib/services/database/migrations/48.js
packages/lib/services/database/migrations/49.js
packages/lib/services/database/migrations/index.js
packages/lib/services/database/sqlStringToLines.js
packages/lib/services/database/types.js
packages/lib/services/debug/populateDatabase.js
packages/lib/services/e2ee/EncryptionService.test.js
packages/lib/services/e2ee/EncryptionService.js
packages/lib/services/e2ee/RSA.node.js
packages/lib/services/e2ee/crypto.test.js
packages/lib/services/e2ee/crypto.js
packages/lib/services/e2ee/cryptoShared.js
packages/lib/services/e2ee/cryptoTestUtils.js
packages/lib/services/e2ee/ppk.test.js
packages/lib/services/e2ee/ppk.js
packages/lib/services/e2ee/ppkTestUtils.js
packages/lib/services/e2ee/ppk/RSA.node.js
packages/lib/services/e2ee/ppk/ppk.test.js
packages/lib/services/e2ee/ppk/ppk.js
packages/lib/services/e2ee/ppk/ppkTestUtils.js
packages/lib/services/e2ee/ppk/webCrypto/LongDataWrapper.js
packages/lib/services/e2ee/ppk/webCrypto/StringToBufferWrapper.js
packages/lib/services/e2ee/ppk/webCrypto/WebCryptoRsa.js
packages/lib/services/e2ee/ppk/webCrypto/buildRsaCryptoProvider.test.js
packages/lib/services/e2ee/ppk/webCrypto/buildRsaCryptoProvider.js
packages/lib/services/e2ee/types.js
packages/lib/services/e2ee/utils.test.js
packages/lib/services/e2ee/utils.js
@@ -1489,6 +1524,7 @@ packages/lib/services/plugins/utils/createViewHandle.js
packages/lib/services/plugins/utils/executeSandboxCall.js
packages/lib/services/plugins/utils/getActivePluginEditorView.js
packages/lib/services/plugins/utils/getActivePluginEditorViews.js
packages/lib/services/plugins/utils/getPluginHelpUrl.js
packages/lib/services/plugins/utils/getPluginIssueReportUrl.test.js
packages/lib/services/plugins/utils/getPluginIssueReportUrl.js
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
@@ -1693,6 +1729,7 @@ packages/plugin-repo-cli/lib/gitCompareUrl.test.js
packages/plugin-repo-cli/lib/gitCompareUrl.js
packages/plugin-repo-cli/lib/overrideUtils.test.js
packages/plugin-repo-cli/lib/overrideUtils.js
packages/plugin-repo-cli/lib/searchPlugins.js
packages/plugin-repo-cli/lib/types.js
packages/plugin-repo-cli/lib/updateReadme.test.js
packages/plugin-repo-cli/lib/updateReadme.js
@@ -1808,6 +1845,8 @@ packages/tools/updateMarkdownDoc.js
packages/tools/utils/discourse.test.js
packages/tools/utils/discourse.js
packages/tools/utils/loadSponsors.js
packages/tools/utils/parsePluralLocalizationForm.js
packages/tools/utils/parsePlurallLocalizationForm.test.js
packages/tools/utils/translation.js
packages/tools/validateFilenames.js
packages/tools/website/build.js

View File

@@ -0,0 +1,36 @@
# Patch to remove eval. This allows using depd in an environment with
# a strict Content-Security-Policy.
# Ref: https://github.com/dougwilson/nodejs-depd/pull/33
diff --git a/index.js b/index.js
index d758d3c8f58a60bf27ef377ad77639bf10ce7854..2bad40d4eeba553d3bcfb206873eac059067ae3b 100644
--- a/index.js
+++ b/index.js
@@ -399,19 +399,20 @@ function wrapfunction (fn, message) {
throw new TypeError('argument fn must be a function')
}
- var args = createArgumentsString(fn.length)
- var deprecate = this // eslint-disable-line no-unused-vars
var stack = getStack()
var site = callSiteLocation(stack[1])
site.name = fn.name
- // eslint-disable-next-line no-eval
- var deprecatedfn = eval('(function (' + args + ') {\n' +
- '"use strict"\n' +
- 'log.call(deprecate, message, site)\n' +
- 'return fn.apply(this, arguments)\n' +
- '})')
+ var deprecatedfn
+ var self = this
+ deprecatedfn = function () {
+ 'use strict'
+ log.call(self, message, site)
+ return fn.apply(this, arguments)
+ }
+ Object.defineProperty(deprecatedfn, 'length', { value: fn.length })
+ Object.defineProperty(deprecatedfn, 'name', { value: fn.name })
return deprecatedfn
}

View File

@@ -0,0 +1,35 @@
# Patch to remove eval. This allows using depd in an environment with
# a strict Content-Security-Policy.
# Ref: https://github.com/dougwilson/nodejs-depd/pull/33
diff --git a/index.js b/index.js
index 1bf2fcfdeffc984e5ad792eec08744c29d4a4590..1b24aa2414458bc651abfdded81b103c131efeaa 100644
--- a/index.js
+++ b/index.js
@@ -415,19 +415,19 @@ function wrapfunction (fn, message) {
throw new TypeError('argument fn must be a function')
}
- var args = createArgumentsString(fn.length)
var stack = getStack()
var site = callSiteLocation(stack[1])
site.name = fn.name
- // eslint-disable-next-line no-new-func
- var deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site',
- '"use strict"\n' +
- 'return function (' + args + ') {' +
- 'log.call(deprecate, message, site)\n' +
- 'return fn.apply(this, arguments)\n' +
- '}')(fn, log, this, message, site)
+ var self = this
+ var deprecatedfn = function () {
+ 'use strict'
+ log.call(self, message, site)
+ return fn.apply(this, arguments)
+ }
+ Object.defineProperty(deprecatedfn, 'length', { value: fn.length })
+ Object.defineProperty(deprecatedfn, 'name', { value: fn.name })
return deprecatedfn
}

View File

@@ -1,209 +0,0 @@
diff --git a/android/build.gradle b/android/build.gradle
index 6afcbbf0cc8ca2d69dd78077d61e59a90b2136bb..9f8d72b4ec5b2b3d290975d6a255917c95300854 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -67,19 +67,19 @@ repositories {
}
// Generate UUIDs for each models contained in android/src/main/assets/
-tasks.register('genUUID') {
- doLast {
- fileTree(dir: "$rootDir/app/src/main/assets", exclude: ['*/*']).visit { fileDetails ->
- if (fileDetails.directory) {
- def odir = file("$rootDir/app/src/main/assets/$fileDetails.relativePath")
- def ofile = file("$odir/uuid")
- mkdir odir
- ofile.text = UUID.randomUUID().toString()
- }
- }
- }
-}
-preBuild.dependsOn genUUID
+// tasks.register('genUUID') {
+// doLast {
+// fileTree(dir: "$rootDir/app/src/main/assets", exclude: ['*/*']).visit { fileDetails ->
+// if (fileDetails.directory) {
+// def odir = file("$rootDir/app/src/main/assets/$fileDetails.relativePath")
+// def ofile = file("$odir/uuid")
+// mkdir odir
+// ofile.text = UUID.randomUUID().toString()
+// }
+// }
+// }
+// }
+// preBuild.dependsOn genUUID
def kotlin_version = getExtOrDefault('kotlinVersion')
diff --git a/android/src/main/java/com/reactnativevosk/VoskModule.kt b/android/src/main/java/com/reactnativevosk/VoskModule.kt
index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..5a8539b9cce8951967640dba755e29a4e3ff404a 100644
--- a/android/src/main/java/com/reactnativevosk/VoskModule.kt
+++ b/android/src/main/java/com/reactnativevosk/VoskModule.kt
@@ -19,13 +19,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
return "Vosk"
}
+ @ReactMethod
+ fun addListener(type: String?) {
+ // Keep: Required for RN built in Event Emitter Calls.
+ }
+
+ @ReactMethod
+ fun removeListeners(type: Int?) {
+ // Keep: Required for RN built in Event Emitter Calls.
+ }
+
override fun onResult(hypothesis: String) {
// Get text data from string object
val text = getHypothesisText(hypothesis)
// Stop recording if data found
if (text != null && text.isNotEmpty()) {
- cleanRecognizer();
+ // Don't auto-stop the recogniser - we want to do that when the user
+ // presses on "stop" only.
+ // cleanRecognizer();
sendEvent("onResult", text)
}
}
@@ -93,12 +105,11 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@ReactMethod
fun loadModel(path: String, promise: Promise) {
cleanModel();
- StorageService.unpack(context, path, "models",
- { model: Model? ->
- this.model = model
- promise.resolve("Model successfully loaded")
- }
- ) { e: IOException ->
+
+ try {
+ this.model = Model(path);
+ promise.resolve("Model successfully loaded")
+ } catch (e: IOException) {
this.model = null
promise.reject(e)
}
@@ -153,6 +164,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
cleanRecognizer();
}
+ @ReactMethod
+ fun stopOnly() {
+ if (speechService != null) {
+ speechService!!.stop()
+ }
+ }
+
+ @ReactMethod
+ fun cleanup() {
+ if (speechService != null) {
+ speechService!!.shutdown();
+ speechService = null
+ }
+ if (recognizer != null) {
+ recognizer!!.close();
+ recognizer = null;
+ }
+ }
+
@ReactMethod
fun unload() {
cleanRecognizer();
diff --git a/lib/typescript/index.d.ts b/lib/typescript/index.d.ts
index 441e41cc402cca3a60b34978ef4fea976076259c..a173acebb4b314402550442ad471e0f7c706e3c4 100644
--- a/lib/typescript/index.d.ts
+++ b/lib/typescript/index.d.ts
@@ -10,6 +10,8 @@ export default class Vosk {
currentRegisteredEvents: EmitterSubscription[];
start: (grammar?: string[] | null) => Promise<String>;
stop: () => void;
+ stopOnly: () => void;
+ cleanup: () => void;
unload: () => void;
onResult: (onResult: (e: VoskEvent) => void) => EventSubscription;
onFinalResult: (onFinalResult: (e: VoskEvent) => void) => EventSubscription;
diff --git a/package.json b/package.json
index 707eddb8d68007f93071ac659c5b087c935c5f01..90ebe20f224eeec472c377df1fef9b15f2ff8200 100644
--- a/package.json
+++ b/package.json
@@ -11,12 +11,9 @@
"src",
"lib",
"android",
- "ios",
"cpp",
- "react-native-vosk.podspec",
"!lib/typescript/example",
"!android/build",
- "!ios/build",
"!**/__tests__",
"!**/__fixtures__",
"!**/__mocks__"
diff --git a/react-native-vosk.podspec b/react-native-vosk.podspec
deleted file mode 100644
index e3d41b90c5eef890c7a5108aaf16ac07d34a698b..0000000000000000000000000000000000000000
--- a/react-native-vosk.podspec
+++ /dev/null
@@ -1,41 +0,0 @@
-require "json"
-
-package = JSON.parse(File.read(File.join(__dir__, "package.json")))
-folly_version = '2021.06.28.00-v2'
-folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
-
-Pod::Spec.new do |s|
- s.name = "react-native-vosk"
- s.version = package["version"]
- s.summary = package["description"]
- s.homepage = package["homepage"]
- s.license = package["license"]
- s.authors = package["author"]
-
- s.platforms = { :ios => "10.0" }
- s.source = { :git => "https://github.com/riderodd/react-native-vosk.git", :tag => "#{s.version}" }
-
- s.source_files = "ios/**/*.{h,m,mm,swift}"
- s.resource_bundles = { 'Vosk' => ['ios/Vosk/*'] }
-
- s.dependency "React-Core"
- s.frameworks = "Accelerate"
- s.library = "c++"
- s.vendored_frameworks = "ios/libvosk.xcframework"
- s.requires_arc = true
-
- # Don't install the dependencies when we run `pod install` in the old architecture.
- if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
- s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
- s.pod_target_xcconfig = {
- "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
- }
-
- s.dependency "React-Codegen"
- s.dependency "RCT-Folly", folly_version
- s.dependency "RCTRequired"
- s.dependency "RCTTypeSafety"
- s.dependency "ReactCommon/turbomodule/core"
- end
-end
diff --git a/src/index.tsx b/src/index.tsx
index d9f90c921d89b1b4d85e145443ed3376546a368a..29e4068dbd7500828a73145bd25497a52c9bf638 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -69,6 +69,15 @@ export default class Vosk {
VoskModule.stop();
};
+ stopOnly = () => {
+ VoskModule.stopOnly();
+ };
+
+ cleanup = () => {
+ this.cleanListeners();
+ VoskModule.cleanup();
+ };
+
unload = () => {
this.cleanListeners();
VoskModule.unload();

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -1,4 +1,77 @@
<?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>Mon, 28 Apr 2025 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Mon, 28 Apr 2025 00:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 3.3]]></title><description><![CDATA[<h2>Desktop application<a name="desktop-application" href="#desktop-application" class="heading-anchor">🔗</a></h2>
<?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>Mon, 22 Sep 2025 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 3.4]]></title><description><![CDATA[<p>Joplin 3.4 includes many bug fixes and improvements, with a focus on the mobile app.</p>
<h2>Mobile<a name="mobile" href="#mobile" class="heading-anchor">🔗</a></h2>
<h3>Rich Text Editor<a name="rich-text-editor" href="#rich-text-editor" class="heading-anchor">🔗</a></h3>
<p>The mobile app now includes a beta <a href="https://joplinapp.org/help/apps/rich_text_editor">Rich Text Editor</a>! The new editor renders formatting/math/images within the editor:</p>
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250922-mobile-rte.png" width="400" alt="screenshot: Mobile Rich Text Editor editing the welcome notes. Images, headings, etc are rendering."/>
<p>To try it, 1) open a note in the default Markdown editor 2) open the note actions menu (the three vertical dots) for the note and 3) click “Edit as Rich Text”.</p>
<p>Be aware that this editor is still in active development and <a href="https://github.com/laurent22/joplin/issues/12840">has a number of known limitations and issues</a>. The Rich Text editor is based on <a href="https://prosemirror.net/">ProseMirror</a> and will behave differently from the desktop Rich Text Editor in many cases.</p>
<h3>Support for publishing notes with Joplin Cloud and Server<a name="support-for-publishing-notes-with-joplin-cloud-and-server" href="#support-for-publishing-notes-with-joplin-cloud-and-server" class="heading-anchor">🔗</a></h3>
<p>It's now possible to <a href="https://joplinapp.org/help/apps/publish_note">publish notes</a> from the mobile app! To do so, open the “Properties” menu for a note, then click “Publish/unpublish”:</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250922-publish-notes.png" alt="screenshot: A Publish/unpublish note action is shown in the &quot;Note properties&quot; sidebar, just below a &quot;Previous versions&quot; button"></p>
<p>Next, in the “publish note” dialog, click “Copy shareable link”. Notes can later be unpublished by clicking &quot;Unpublish&quot; in the publication dialog.</p>
<h3>Viewing note history<a name="viewing-note-history" href="#viewing-note-history" class="heading-anchor">🔗</a></h3>
<p>It is now possible to view and restore previous note versions from the mobile app. Like the &quot;publish note&quot; feature, previous note versions can be accessed from the note properties menu.</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250922-note-history.png" alt="screenshot: The note history page"></p>
<p>As on desktop, the note history feature can be configured from the “Note History” tab in settings.</p>
<h3>Updated tag dialog<a name="updated-tag-dialog" href="#updated-tag-dialog" class="heading-anchor">🔗</a></h3>
<p>The tag dialog has been redesigned, with a new UI for adding, removing, and creating new tags:<br>
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250922-tag-editor.png" width="500" alt="screenshot: Tag dialog now consists of three sections: Added tags, Add new tags, Actions."/></p>
<h3>Android: Improved voice typing<a name="android-improved-voice-typing" href="#android-improved-voice-typing" class="heading-anchor">🔗</a></h3>
<p>The voice typing feature on Android has been updated with <a href="https://github.com/laurent22/joplin/pull/12404">improved silence detection</a> and a new “<a href="https://github.com/laurent22/joplin/pull/12370">custom glossary</a>” setting. Voice typing also now <a href="https://github.com/laurent22/joplin/pull/12352">defaults to a more accurate (but somewhat slower) model</a>.</p>
<h3>Quickly creating a note from multiple photos<a name="quickly-creating-a-note-from-multiple-photos" href="#quickly-creating-a-note-from-multiple-photos" class="heading-anchor">🔗</a></h3>
<p>A “scan notebook” action has been added to the “New note” menu:</p>
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250922-scan-notebook.png" width="500"/>
<p>This action allows quickly creating a new note with multiple pictures taken from the camera.</p>
<h2>Desktop<a name="desktop" href="#desktop" class="heading-anchor">🔗</a></h2>
<h3>More Markdown Editor settings<a name="more-markdown-editor-settings" href="#more-markdown-editor-settings" class="heading-anchor">🔗</a></h3>
<p>The &quot;Note&quot; tab in settings now includes new settings for the Markdown editor, including:</p>
<ul>
<li>An option to render headers, lists, and certain other formatting within the editor.</li>
<li>An option to render images in the editor.</li>
</ul>
<p>When enabled, these settings bring the Markdown editor closer to the Rich Text Editor, without <a href="https://joplinapp.org/help/apps/rich_text_editor">some of the Rich Text Editor's limitations</a>.</p>
<p>These settings are also available on mobile.</p>
<h3>Smaller application size and faster startup<a name="smaller-application-size-and-faster-startup" href="#smaller-application-size-and-faster-startup" class="heading-anchor">🔗</a></h3>
<p>We've made the desktop application roughly 33% smaller! In addition to faster application startup, this means that the desktop app should be faster to download take up less space.</p>
<table class="table">
<thead>
<tr>
<th>Joplin version</th>
<th>Previous size (v3.3.13)</th>
<th>New size (v3.4.12)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Joplin for MacOS (ARM)</td>
<td>211 MB</td>
<td>141 MB</td>
</tr>
<tr>
<td>Joplin for Windows (installer)</td>
<td>321 MB</td>
<td>219 MB</td>
</tr>
<tr>
<td>Joplin for Windows (portable)</td>
<td>320 MB</td>
<td>219 MB</td>
</tr>
<tr>
<td>Joplin for Linux (AppImage)</td>
<td>219 MB</td>
<td>147 MB</td>
</tr>
</tbody>
</table>
<h2>Terminal app<a name="terminal-app" href="#terminal-app" class="heading-anchor">🔗</a></h2>
<h3>Collapsible folders<a name="collapsible-folders" href="#collapsible-folders" class="heading-anchor">🔗</a></h3>
<p>The <a href="https://joplinapp.org/help/apps/terminal/">terminal application</a> now supports expanding and collapsing folders by pressing <kbd>z</kbd>. For additional information, see <a href="https://github.com/laurent22/joplin/pull/12718">the original pull request</a>.</p>
<h3>Managing shared notebooks and published notes<a name="managing-shared-notebooks-and-published-notes" href="#managing-shared-notebooks-and-published-notes" class="heading-anchor">🔗</a></h3>
<p>New commands have been added to the terminal app, including <code>publish</code>, <code>unpublish</code>, and <code>share</code>. This allows the terminal app to manage shared folders and published notes.</p>
<h2>Bug fixes<a name="bug-fixes" href="#bug-fixes" class="heading-anchor">🔗</a></h2>
<p>For the full list of changes, see <a href="https://joplinapp.org/help/about/changelog/desktop/">the desktop changelog</a> and <a href="https://joplinapp.org/help/about/changelog/android/">the mobile changelog</a>.</p>
]]></description><link>https://joplinapp.org/news/20250922-release-3-4</link><guid isPermaLink="false">20250922-release-3-4</guid><pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[What's new in Joplin 3.3]]></title><description><![CDATA[<h2>Desktop application<a name="desktop-application" href="#desktop-application" class="heading-anchor">🔗</a></h2>
<h3>Accessibility improvements<a name="accessibility-improvements" href="#accessibility-improvements" class="heading-anchor">🔗</a></h3>
<p>The Joplin 3.3 release introduces significant accessibility enhancements designed to make the application more inclusive and user-friendly. Users can now benefit from improved keyboard navigation, thanks to newly added shortcuts and clearer labels that streamline interaction across the interface. We've also added a &quot;go to viewer&quot; menu item that moves focus from the note editor to the note viewer. Focus is moved to the location in the viewer corresponding to the location of the cursor in the editor.</p>
<p>Screen reader support has been bolstered, ensuring elements like the note list and sidebar are easier to toggle and interact with. These updates make the application more usable for individuals relying on assistive technologies.</p>
@@ -446,10 +519,4 @@ sys 0m38.013s</p>
<p>Unfortunately we cannot publish the Android version because it is based on a framework version that Google does not accept. To upgrade the app a lot of changes are needed and another round of pre-releases, and therefore there will not be a 2.9 version for Google Play. You may however download the official APK directly from there: <a href="https://github.com/laurent22/joplin-android/releases/tag/android-v2.9.8">Android 2.9 Official Release</a></p>
<p>This is the reality of app stores in general - small developers being imposed never ending new requirements by all-powerful companies, and by the time a version is finally ready we can't even publish it because yet more requirements are in place.</p>
<p>For the record the current 2.9 app works perfectly fine. It targets Android 11, which is only 2 years old and is still supported (and installed on millions of phones). Google requires us to target Android 12 which only came out last year.</p>
]]></description><link>https://joplinapp.org/news/20221216-release-2-9</link><guid isPermaLink="false">20221216-release-2-9</guid><pubDate>Fri, 16 Dec 2022 00:00:00 GMT</pubDate><twitter-text>What&apos;s new in Joplin 2.9</twitter-text></item><item><title><![CDATA[Joplin is hiring!]]></title><description><![CDATA[<p>Joplin is an open source note-taking app. Capture your thoughts and securely access them from any device.</p>
<p>We are looking to hire two JavaScript software developers to work on the desktop, mobile, and server applications. All those are built using modern technologies, including React, React Native and Electron with a strong focus on test units.</p>
<p>You need to demonstrate some experience with at least some of these technologies, and willing to learn more and touch various different projects.</p>
<p>You will be part of a small team, so you will have an opportunity for a high-impact role, targeting hundreds of thousands of users.</p>
<p>If you're interested please contact us at job-AT-joplin.cloud</p>
<p>No agencies please.</p>
]]></description><link>https://joplinapp.org/news/20221209-job</link><guid isPermaLink="false">20221209-job</guid><pubDate>Fri, 09 Dec 2022 00:00:00 GMT</pubDate><twitter-text>Joplin is hiring!</twitter-text></item></channel></rss>
]]></description><link>https://joplinapp.org/news/20221216-release-2-9</link><guid isPermaLink="false">20221216-release-2-9</guid><pubDate>Fri, 16 Dec 2022 00:00:00 GMT</pubDate><twitter-text>What&apos;s new in Joplin 2.9</twitter-text></item></channel></rss>

View File

@@ -63,11 +63,24 @@ FROM node:18-slim
ARG user=joplin
RUN useradd --create-home --shell /bin/bash $user
# Install PM2 and set home directory. Setting the PM2 data dir so modules/config persist regardless
# of user home.
RUN npm i -g pm2@5.4.3 && mkdir -p /opt/pm2 && chown -R $user:$user /opt/pm2
ENV PM2_HOME=/opt/pm2
USER $user
COPY --chown=$user:$user --from=builder /build/packages /home/$user/packages
COPY --chown=$user:$user --from=builder /usr/bin/tini /usr/local/bin/tini
# We download a specific version of the plugin to prevent pm2 from fetching the latest, since it may
# not have been properly audited (that fact was used to spread malware at some point). Ref:
# https://github.com/laurent22/joplin/issues/12754
RUN pm2 install https://registry.npmjs.org/pm2-logrotate/-/pm2-logrotate-3.0.0.tgz \
&& pm2 set pm2-logrotate:max_size 100MB \
&& pm2 set pm2-logrotate:retain 5 \
&& pm2 set pm2-logrotate:compress true
ENV NODE_ENV=production
ENV RUNNING_IN_DOCKER=1
EXPOSE ${APP_PORT}

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://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://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="topagency"/></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> <a href="https://essayservice.com"><img title="quick and reliable service to write my paper for me" width="256" src="https://joplinapp.org/images/sponsors/EssayService.png" alt="quick and reliable service to write my paper for me"/></a> <a href="https://writepaper.com/"><img title="best service to write my paper for me" width="256" src="https://joplinapp.org/images/sponsors/WritePaper.png" alt="best service to write my paper for me"/></a> <a href="https://paperwriter.com/"><img title="high-quality paper writing service PaperWriter" width="256" src="https://joplinapp.org/images/sponsors/PaperWriter.png" alt="high-quality paper writing service PaperWriter"/></a> <a href="https://www.bestetf.net/"><img title="BestETF" width="256" src="https://joplinapp.org/images/sponsors/BestEtf.png" alt="BestETF"/></a> <a href="https://freespinny.io/free-spins-no-deposit/"><img title="Freespinny.io Free Spins Bonus site" width="256" src="https://joplinapp.org/images/sponsors/Freespinny.png" alt="Freespinny.io Free Spins Bonus site"/></a> <a href="https://damangameplay.in"><img title="Daman Game" width="256" src="https://joplinapp.org/images/sponsors/DamanGame.png" alt="Daman Game"/></a> <a href="https://essayshark.com"><img title="EssayShark - essay writers for hire" width="256" src="https://joplinapp.org/images/sponsors/EssayShark.png" alt="EssayShark - essay writers for hire"/></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://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://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="topagency"/></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> <a href="https://writepaper.com/"><img title="best service to write my paper for me" width="256" src="https://joplinapp.org/images/sponsors/WritePaper.png" alt="best service to write my paper for me"/></a> <a href="https://paperwriter.com/"><img title="high-quality paper writing service PaperWriter" width="256" src="https://joplinapp.org/images/sponsors/PaperWriter.png" alt="high-quality paper writing service PaperWriter"/></a> <a href="https://www.bestetf.net/"><img title="BestETF" width="256" src="https://joplinapp.org/images/sponsors/BestEtf.png" alt="BestETF"/></a> <a href="https://freespinny.io/free-spins-no-deposit/"><img title="Freespinny.io Free Spins Bonus site" width="256" src="https://joplinapp.org/images/sponsors/Freespinny.png" alt="Freespinny.io Free Spins Bonus site"/></a> <a href="https://essayshark.com"><img title="EssayShark - essay writers for hire" width="256" src="https://joplinapp.org/images/sponsors/EssayShark.png" alt="EssayShark - essay writers for hire"/></a> <a href="https://pokieslab1.com/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/PokiesLab.png" alt="Australian Real Money Pokies"/></a> <a href="https://pokiesman1.net/real-money-pokies/"><img title="Australian Real Money Pokies" width="256" src="https://joplinapp.org/images/sponsors/Pokiesman.png" alt="Australian Real Money Pokies"/></a> <a href="https://domyessay.com"><img title="Essay writers DoMyEssay are dedicated to providing top-notch, custom-written papers that meet your academic requirements" width="256" src="https://joplinapp.org/images/sponsors/DoMyEssay.png" alt="DoMyEssay"/></a> <a href="https://essaypro.com/"><img title="best essay writing service" width="256" src="https://joplinapp.org/images/sponsors/EssayPro.png" alt="best essay writing service"/></a> <a href="https://socialkings.online"><img title="Boost your reach and buy real followers" width="256" src="https://joplinapp.org/images/sponsors/SocialKings.png" alt="Boost your reach and buy real followers"/></a> <a href="https://uk.notgamstop.com/bonuses/free-spins-no-deposit-no-gamstop/"><img title="free spins no deposit at NotGamstop" width="256" src="https://joplinapp.org/images/sponsors/NotGamStop.jpg" alt="free spins no deposit at NotGamstop"/></a> <a href="https://www.writemyessay.com/"><img title="writing service for students WriteMyEssay" width="256" src="https://joplinapp.org/images/sponsors/WriteMyEssay.png" alt="writing service for students WriteMyEssay"/></a>
<!-- SPONSORS-ORG -->
* * *

View File

@@ -9,7 +9,7 @@
"vips.dev": {
"platforms": ["aarch64-darwin"],
},
"nodejs": "23.10.0",
"nodejs": "23.11.0",
"pkg-config": "latest",
"darwin.apple_sdk.frameworks.Foundation": { // satisfies missing CoreText/CoreText.h
// https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/darwin/apple-sdk/default.nix

View File

@@ -16,12 +16,10 @@
# SLAVE_POSTGRES_PORT=5433
# SLAVE_POSTGRES_HOST=localhost
version: '2'
services:
postgresql-master:
image: 'bitnami/postgresql:17.3.0'
image: 'bitnamilegacy/postgresql:17.4.0'
ports:
- '5432:5432'
environment:
@@ -38,7 +36,7 @@ services:
- POSTGRESQL_EXTRA_FLAGS=-c work_mem=100000 -c log_statement=all
postgresql-slave:
image: 'bitnami/postgresql:17.3.0'
image: 'bitnamilegacy/postgresql:17.4.0'
ports:
- '5433:5432'
depends_on:

View File

@@ -1,8 +1,6 @@
# This compose file can be used in development to run both the database and app
# within Docker.
version: '3'
services:
app:
build:

View File

@@ -15,8 +15,6 @@
# - This would typically be mapped to port to 443 (TLS) with a reverse proxy.
# - If Joplin Server does not need to be accessible over the internet, the port can be mapped to 22300.
version: '3'
networks:
app-network:
transcribe-network:

View File

@@ -79,17 +79,17 @@
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jest": "27.9.0",
"eslint-plugin-promise": "6.6.0",
"eslint-plugin-react": "7.37.4",
"eslint-plugin-react": "7.37.5",
"execa": "5.1.1",
"fs-extra": "11.2.0",
"glob": "11.0.2",
"glob": "11.0.3",
"gulp": "4.0.2",
"husky": "9.1.7",
"lerna": "3.22.1",
"lint-staged": "15.5.2",
"madge": "8.0.0",
"npm-package-json-lint": "8.0.0",
"typescript": "5.8.2"
"typescript": "5.8.3"
},
"dependencies": {
"@types/fs-extra": "11.0.4",
@@ -101,7 +101,6 @@
"packageManager": "yarn@4.9.2",
"resolutions": {
"react-native-camera@4.2.1": "patch:react-native-camera@npm%3A4.2.1#./.yarn/patches/react-native-camera-npm-4.2.1-24b2600a7e.patch",
"react-native-vosk@0.1.12": "patch:react-native-vosk@npm%3A0.1.12#./.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch",
"eslint": "patch:eslint@8.57.1#./.yarn/patches/eslint-npm-8.39.0-d92bace04d.patch",
"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",
@@ -118,6 +117,12 @@
"pdfjs-dist@*": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
"pdfjs-dist@3.11.174": "patch:pdfjs-dist@npm%3A3.11.174#./.yarn/patches/pdfjs-dist-npm-3.11.174-67f2fee6d6.patch",
"canvas@npm:^2.11.2": "link:./.yarn/joplin-empty-package/",
"node-gyp@npm:^9.0.0": "11.2.0"
"node-gyp@npm:^9.0.0": "11.2.0",
"depd@npm:^2.0.0": "patch:depd@npm%3A2.0.0#~/.yarn/patches/depd-npm-2.0.0-b6c51a4b43.patch",
"depd@npm:~2.0.0": "patch:depd@npm%3A2.0.0#~/.yarn/patches/depd-npm-2.0.0-b6c51a4b43.patch",
"depd@npm:~1.1.2": "patch:depd@npm%3A2.0.0#~/.yarn/patches/depd-npm-2.0.0-b6c51a4b43.patch",
"depd@npm:2.0.0": "patch:depd@npm%3A2.0.0#~/.yarn/patches/depd-npm-2.0.0-b6c51a4b43.patch",
"depd@npm:^1.1.2": "patch:depd@npm%3A2.0.0#~/.yarn/patches/depd-npm-2.0.0-b6c51a4b43.patch",
"depd@npm:^1.1.0": "patch:depd@npm%3A2.0.0#~/.yarn/patches/depd-npm-2.0.0-b6c51a4b43.patch"
}
}

View File

@@ -434,6 +434,7 @@ class Application extends BaseApplication {
}
await Setting.saveAll();
await this.database_.close();
// Need to call exit() explicitly, otherwise Node wait for any timeout to complete
// https://stackoverflow.com/questions/18050095

View File

@@ -2,33 +2,44 @@
/* eslint-disable no-console */
const fs = require('fs-extra');
const Logger = require('@joplin/utils/Logger').default;
const { dirname } = require('@joplin/lib/path-utils');
import * as fs from 'fs-extra';
import Logger, { TargetType } from '@joplin/utils/Logger';
import { dirname } from '@joplin/lib/path-utils';
const { DatabaseDriverNode } = require('@joplin/lib/database-driver-node.js');
const JoplinDatabase = require('@joplin/lib/JoplinDatabase').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Setting = require('@joplin/lib/models/Setting').default;
import JoplinDatabase from '@joplin/lib/JoplinDatabase';
import BaseModel from '@joplin/lib/BaseModel';
import Folder from '@joplin/lib/models/Folder';
import Note from '@joplin/lib/models/Note';
import Setting from '@joplin/lib/models/Setting';
const { sprintf } = require('sprintf-js');
const exec = require('child_process').exec;
const nodeSqlite = require('sqlite3');
const { loadKeychainServiceAndSettings } = require('@joplin/lib/services/SettingUtils');
const { default: shimInitCli } = require('./utils/shimInitCli');
const baseDir = `${dirname(__dirname)}/tests/cli-integration`;
const joplinAppPath = `${__dirname}/main.js`;
shimInitCli({ nodeSqlite, appVersion: () => require('../package.json').version, keytar: null });
require('@joplin/lib/testing/test-utils');
const logger = new Logger();
logger.addTarget('console');
logger.addTarget(TargetType.Console);
logger.setLevel(Logger.LEVEL_ERROR);
const dbLogger = new Logger();
dbLogger.addTarget('console');
dbLogger.addTarget(TargetType.Console);
dbLogger.setLevel(Logger.LEVEL_INFO);
const db = new JoplinDatabase(new DatabaseDriverNode());
db.setLogger(dbLogger);
function createClient(id) {
interface Client {
id: number;
profileDir: string;
}
function createClient(id: number): Client {
return {
id: id,
profileDir: `${baseDir}/client${id}`,
@@ -37,13 +48,13 @@ function createClient(id) {
const client = createClient(1);
function execCommand(client, command) {
function execCommand(client: Client, command: string) {
const exePath = `node ${joplinAppPath}`;
const cmd = `${exePath} --update-geolocation-disabled --env dev --profile ${client.profileDir} ${command}`;
logger.info(`${client.id}: ${command}`);
return new Promise((resolve, reject) => {
exec(cmd, (error, stdout, stderr) => {
return new Promise<string>((resolve, reject) => {
exec(cmd, (error: string, stdout: string, stderr: string) => {
if (error) {
logger.error(stderr);
reject(error);
@@ -54,17 +65,17 @@ function execCommand(client, command) {
});
}
function assertTrue(v) {
function assertTrue(v: unknown) {
if (!v) throw new Error(sprintf('Expected "true", got "%s"."', v));
process.stdout.write('.');
}
function assertFalse(v) {
function assertFalse(v: unknown) {
if (v) throw new Error(sprintf('Expected "false", got "%s"."', v));
process.stdout.write('.');
}
function assertEquals(expected, real) {
function assertEquals(expected: unknown, real: unknown) {
if (expected !== real) throw new Error(sprintf('Expecting "%s", got "%s"', expected, real));
process.stdout.write('.');
}
@@ -73,7 +84,7 @@ async function clearDatabase() {
await db.transactionExecBatch(['DELETE FROM folders', 'DELETE FROM notes', 'DELETE FROM tags', 'DELETE FROM note_tags', 'DELETE FROM resources', 'DELETE FROM deleted_items']);
}
const testUnits = {};
const testUnits: Record<string, ()=> Promise<void>> = {};
testUnits.testFolders = async () => {
await execCommand(client, 'mkbook nb1');
@@ -85,10 +96,16 @@ testUnits.testFolders = async () => {
await execCommand(client, 'mkbook nb1');
folders = await Folder.all();
assertEquals(1, folders.length);
assertEquals(2, folders.length);
assertEquals('nb1', folders[0].title);
assertEquals('nb1', folders[1].title);
await execCommand(client, 'rm -r -f nb1');
await execCommand(client, 'rmbook -p -f nb1');
folders = await Folder.all();
assertEquals(1, folders.length);
await execCommand(client, 'rmbook -p -f nb1');
folders = await Folder.all();
assertEquals(0, folders.length);
@@ -102,7 +119,7 @@ testUnits.testNotes = async () => {
assertEquals(1, notes.length);
assertEquals('n1', notes[0].title);
await execCommand(client, 'rm -f n1');
await execCommand(client, 'rmnote -p -f n1');
notes = await Note.all();
assertEquals(0, notes.length);
@@ -112,12 +129,19 @@ testUnits.testNotes = async () => {
notes = await Note.all();
assertEquals(2, notes.length);
await execCommand(client, 'rm -f \'blabla*\'');
// Should fail to delete a non-existent note
let failed = false;
try {
await execCommand(client, 'rmnote -f \'blabla*\'');
} catch (error) {
failed = true;
}
assertEquals(failed, true);
notes = await Note.all();
assertEquals(2, notes.length);
await execCommand(client, 'rm -f \'n*\'');
await execCommand(client, 'rmnote -f -p \'n*\'');
notes = await Note.all();
assertEquals(0, notes.length);
@@ -140,10 +164,12 @@ testUnits.testCat = async () => {
testUnits.testConfig = async () => {
await execCommand(client, 'config editor vim');
await Setting.reset();
await Setting.load();
assertEquals('vim', Setting.value('editor'));
await execCommand(client, 'config editor subl');
await Setting.reset();
await Setting.load();
assertEquals('subl', Setting.value('editor'));
@@ -201,15 +227,47 @@ testUnits.testMv = async () => {
await execCommand(client, 'mknote note2');
await execCommand(client, 'mknote note3');
await execCommand(client, 'mknote blabla');
await execCommand(client, 'mv \'note*\' nb2');
notes1 = await Note.previews(f1.id);
notes2 = await Note.previews(f2.id);
assertEquals(4, notes1.length);
assertEquals(1, notes2.length);
await execCommand(client, 'mv \'note*\' nb2');
notes2 = await Note.previews(f2.id);
notes1 = await Note.previews(f1.id);
assertEquals(1, notes1.length);
assertEquals(4, notes2.length);
};
testUnits.testUse = async () => {
await execCommand(client, 'mkbook nb1');
await execCommand(client, 'mkbook nb2');
await execCommand(client, 'mknote n1');
await execCommand(client, 'mknote n2');
const f1 = await Folder.loadByTitle('nb1');
const f2 = await Folder.loadByTitle('nb2');
let notes1 = await Note.previews(f1.id);
let notes2 = await Note.previews(f2.id);
assertEquals(0, notes1.length);
assertEquals(2, notes2.length);
await execCommand(client, 'use nb1');
await execCommand(client, 'mknote note2');
await execCommand(client, 'mknote note3');
notes1 = await Note.previews(f1.id);
notes2 = await Note.previews(f2.id);
assertEquals(2, notes1.length);
assertEquals(2, notes2.length);
};
async function main() {
await fs.remove(baseDir);
@@ -217,7 +275,9 @@ async function main() {
await db.open({ name: `${client.profileDir}/database.sqlite` });
BaseModel.setDb(db);
await Setting.load();
Setting.setConstant('rootProfileDir', client.profileDir);
Setting.setConstant('profileDir', client.profileDir);
await loadKeychainServiceAndSettings([]);
let onlyThisTest = 'testMv';
onlyThisTest = '';
@@ -234,7 +294,7 @@ async function main() {
}
}
main(process.argv).catch(error => {
main().catch(error => {
console.info('');
logger.error(error);
});

View File

@@ -35,15 +35,15 @@
],
"owner": "Laurent Cozic"
},
"version": "3.4.1",
"version": "3.5.1",
"bin": "./main.js",
"engines": {
"node": ">=10.0.0"
},
"dependencies": {
"@joplin/lib": "~3.4",
"@joplin/renderer": "~3.4",
"@joplin/utils": "~3.4",
"@joplin/lib": "~3.5",
"@joplin/renderer": "~3.5",
"@joplin/utils": "~3.5",
"aws-sdk": "2.1340.0",
"chalk": "4.1.2",
"compare-version": "0.1.2",
@@ -57,7 +57,7 @@
"proper-lockfile": "4.1.2",
"redux": "4.2.1",
"server-destroy": "1.0.1",
"sharp": "0.34.2",
"sharp": "0.34.3",
"sprintf-js": "1.1.3",
"sqlite3": "5.1.6",
"string-padding": "1.0.2",
@@ -70,14 +70,14 @@
"yargs-parser": "21.1.1"
},
"devDependencies": {
"@joplin/tools": "~3.4",
"@joplin/tools": "~3.5",
"@types/fs-extra": "11.0.4",
"@types/jest": "29.5.14",
"@types/node": "18.19.103",
"@types/node": "18.19.130",
"@types/proper-lockfile": "^4.1.2",
"gulp": "4.0.2",
"jest": "29.7.0",
"temp": "0.9.4",
"typescript": "5.8.2"
"typescript": "5.8.3"
}
}

View File

@@ -0,0 +1,8 @@
<en-note>
<div>
<en-media style="--en-viewerProps:{};" type="image/jpeg" hash="e2d4887c5a32ab1686276c7c5ae733ef" width="1.125in" />
</div>
<div>
<br />
</div>
</en-note>

View File

@@ -0,0 +1,8 @@
<en-note>
<div>
<img src=":/e2d4887c5a32ab1686276c7c5ae733ef" style="--en-viewerProps:{};" type="image/jpeg" hash="e2d4887c5a32ab1686276c7c5ae733ef" width="108" alt="attachment-image" />
</div>
<div>
<br/>
</div>
</en-note>

View File

@@ -1,6 +1,8 @@
<en-note>
<div><a href=":/21ca2b948f222a38802940ec7e2e5de3" hash="21ca2b948f222a38802940ec7e2e5de3" type="application/pdf" style="cursor:pointer;" alt="attachment-1">attachment-1</a></div>
<div>
<br>
<a href=':/21ca2b948f222a38802940ec7e2e5de3' hash="21ca2b948f222a38802940ec7e2e5de3" type="application/pdf" style="cursor:pointer;" alt="attachment-1"> attachment-1</a>
</div>
<div>
<br/>
</div>
</en-note>

View File

@@ -1,16 +1,11 @@
<en-note>
<div>
<p>For example, consider an exported Evernote list with todo checkboxes like this:</p>
<ul>
<li>
<div><input checked="checked" type="checkbox" onclick="return false;">Foo</div>
</li>
<li>
<div><input type="checkbox" onclick="return false;"><b>Bar</b></div>
</li>
<li>
<div><input type="checkbox" onclick="return false;"><i>Baz</i></div>
</li>
<li><div><input checked="checked" type="checkbox" onclick="return false;" />Foo</div></li>
<li><div><input type="checkbox" onclick="return false;" /><b>Bar</b></div></li>
<li><div><input type="checkbox" onclick="return false;" /><i>Baz</i></div></li>
</ul>
</div>
</en-note>

View File

@@ -1,19 +1,11 @@
<en-note>
<div>
<p>In Evernote a checklist is not the same as a list with checkboxes.</p>
<ul style="--en-todo:true;">
<li style="--en-checked:false;">
<input type="checkbox" onclick="return false;">
<div>One</div>
</li>
<li style="--en-checked:true;">
<input checked="checked" type="checkbox" onclick="return false;">
<div>Two</div>
</li>
<li style="--en-checked:false;">
<input type="checkbox" onclick="return false;">
<div>Three</div>
</li>
<ul STYLE="--en-todo:true;">
<li STYLE="--en-checked:false;"> <input type="checkbox" onclick="return false;" /><div>One</div></li>
<li STYLE="--en-checked:true;"> <input checked="checked" type="checkbox" onclick="return false;" /><div>Two</div>
</li><li STYLE="--en-checked:false;"> <input type="checkbox" onclick="return false;" /><div>Three</div></li>
</ul>
</div>
</en-note>

View File

@@ -1,12 +1 @@
<en-note>
<div>
<audio controls="" preload="none" style="width:480px;">
<source src=":/9168ee833d03c5ea7c730ac6673978c1" type="audio/mp4">
<p>Your browser does not support HTML5 audio.</p>
</audio>
<p><a href=":/9168ee833d03c5ea7c730ac6673978c1">audio test</a></p>
</div>
<div>
<br>
</div>
</en-note>
<en-note><div><audio controls preload="none" style="width:480px;"><source src=":/9168ee833d03c5ea7c730ac6673978c1" type="audio/mp4" /><p>Your browser does not support HTML5 audio.</p></audio><p><a href=":/9168ee833d03c5ea7c730ac6673978c1">audio test</a></p></div><div><br/></div></en-note>

View File

@@ -1,12 +1 @@
<en-note>
<div><input type="checkbox" onclick="return false;">This is a test</div>
<div><input type="checkbox" onclick="return false;">A test for <span style="font-weight: bold;">bold</span></div>
<div>
<input type="checkbox" onclick="return false;">A test for <i>italic</i>
<br>
</div>
<div>
<br>
</div>
<div><i><img src=":/89ce7da62c6b2832929a6964237e98e9" hash="89ce7da62c6b2832929a6964237e98e9" type="image/jpeg" alt=""></i></div>
</en-note>
<en-note><div><input type="checkbox" onclick="return false;" />This is a test</div><div><input type="checkbox" onclick="return false;" />A test for <span STYLE="font-weight: bold;">bold</span></div><div><input type="checkbox" onclick="return false;" />A test for <i>italic</i><br/></div><div><br/></div><div><i><img src=":/89ce7da62c6b2832929a6964237e98e9" hash="89ce7da62c6b2832929a6964237e98e9" type="image/jpeg" alt="" /></i></div></en-note>

View File

@@ -1,3 +1,3 @@
<en-note>
<h1 style="box-sizing:inherit;font-family:&quot;Guardian TextSans Web&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;margin-top:0.2em;margin-bottom:0.35em;font-size:2.125em;font-weight:600;line-height:1.3;">Association Between mRNA Vaccination and COVID-19 Hospitalization and Disease Severity</h1>
<h1 STYLE="box-sizing:inherit;font-family:&quot;Guardian TextSans Web&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;margin-top:0.2em;margin-bottom:0.35em;font-size:2.125em;font-weight:600;line-height:1.3;">Association Between mRNA Vaccination and COVID-19 Hospitalization and Disease Severity</h1>
</en-note>

View File

@@ -1,3 +1,5 @@
<en-note>
<div><img style="margin:0px;padding:0px;outline:0px;width:74px;height:36px;position:absolute;bottom:-5px;left:0px;transform:translate(0px, 100%);stroke-dasharray:90;transition:stroke-dashoffset 0.5s cubic-bezier(0.97, 0.16, 0.62, 0.76) 0s;stroke-dashoffset:0;" src="data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' data-evernote-id='97' class='js-evernote-checked'%3e%3cuse xlink:href='https://wordminds.com/wp-content/themes/wordminds/assets/img/hint_left.svg%23hint_left' data-evernote-id='98' class='js-evernote-checked'%3e%3c/use%3e%3c/svg%3e"></div>
<div>
<img STYLE="margin:0px;padding:0px;outline:0px;width:74px;height:36px;position:absolute;bottom:-5px;left:0px;transform:translate(0px, 100%);stroke-dasharray:90;transition:stroke-dashoffset 0.5s cubic-bezier(0.97, 0.16, 0.62, 0.76) 0s;stroke-dashoffset:0;" SRC="data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' data-evernote-id='97' class='js-evernote-checked'%3e%3cuse xlink:href='https://wordminds.com/wp-content/themes/wordminds/assets/img/hint_left.svg%23hint_left' data-evernote-id='98' class='js-evernote-checked'%3e%3c/use%3e%3c/svg%3e"/>
</div>
</en-note>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export4.dtd">
<en-export export-date="20230724T173816Z" application="Evernote" version="10.58.8">
<note>
<title>test.json</title>
<content><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note><en-media hash="ac91cc691d21261b222681dd38c1e4ad" type="application/json"/></en-note>
]]>
</content>
<created>20191002T075850Z</created>
<updated>20191002T075850Z</updated>
<note-attributes><latitude>48.79547119140625</latitude><longitude>9.809423921920198</longitude><altitude>398.0</altitude><author>Laurent</author><source>desktop.mac</source></note-attributes>
<resource><data>eyAidGVzdCI6IDEyMyB9</data><mime>application/json</mime><width>0</width><height>0</height><resource-attributes><file-name>test.json</file-name><attachment>false</attachment></resource-attributes></resource></note></en-export>

View File

@@ -2,7 +2,7 @@ import PluginRunner from '../../../app/services/plugins/PluginRunner';
import PluginService, { PluginSettings, defaultPluginSetting } from '@joplin/lib/services/plugins/PluginService';
import { ContentScriptType } from '@joplin/lib/services/plugins/api/types';
import MdToHtml from '@joplin/renderer/MdToHtml';
import shim from '@joplin/lib/shim';
import shim, { MobilePlatform } from '@joplin/lib/shim';
import Setting from '@joplin/lib/models/Setting';
import * as fs from 'fs-extra';
import Note from '@joplin/lib/models/Note';
@@ -310,7 +310,7 @@ describe('services_PluginService', () => {
let resetPlatformMock = () => {};
if (!isDesktop) {
resetPlatformMock = mockMobilePlatform('android').reset;
resetPlatformMock = mockMobilePlatform(MobilePlatform.Android).reset;
}
try {

View File

@@ -0,0 +1,3 @@
1. File without extension and leading `./`: [file1](./file1). Gets imported, but filename is converted to extension, like `<internal_id>.file1`
2. File without extension: [file2](file2). Not imported at all.
3. File with extension: [file3](file3.text). Gets imported properly.

View File

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

View File

@@ -407,7 +407,17 @@ export default class ElectronAppWrapper {
isGoingToExit = true;
} else {
event.preventDefault();
this.hide();
const w = this.win_;
if (!w) return;
if (w.isFullScreen()) {
// leave fullscreen, then hide
w.once('leave-full-screen', () => w.hide());
w.setFullScreen(false);
} else {
w.hide();
}
}
} else {
const hasBackgroundWindows = this.secondaryWindows_.size > 0;
@@ -612,7 +622,11 @@ export default class ElectronAppWrapper {
console.warn('The window object was not available during the click event from tray icon');
return;
}
this.mainWindow().show();
if (!this.mainWindow().isVisible()) {
this.mainWindow().show();
} else {
this.mainWindow().hide();
}
});
} catch (error) {
console.error('Cannot create tray', error);

View File

@@ -86,8 +86,14 @@ export default class InteropServiceHelper {
// pdfs.
// https://github.com/laurent22/joplin/issues/6254.
await win.webContents.executeJavaScript('document.querySelectorAll(\'details\').forEach(el=>el.setAttribute(\'open\',\'\'))');
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const data = await win.webContents.printToPDF(options as any);
const data = await win.webContents.printToPDF({
...options,
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partially refactored old code before rule was applied
pageSize: options.pageSize as any,
// Allows users to override the CSS page size.
// See https://github.com/laurent22/joplin/issues/13096
preferCSSPageSize: true,
});
resolve(data);
} catch (error) {
reject(error);

View File

@@ -52,7 +52,7 @@ describe('app.reducer', () => {
...createAppDefaultState({}),
backgroundWindows: {
testWindow: {
...createAppDefaultWindowState(),
...createAppDefaultWindowState(null),
windowId: 'testWindow',
visibleDialogs: {

View File

@@ -26,10 +26,21 @@ export interface AppStateDialog {
props: Record<string, any>;
}
export interface EditorScrollPercents {
export interface NoteIdToScrollPercent {
[noteId: string]: number;
}
type RichTextEditorSelectionBookmark = unknown;
export interface EditorCursorLocations {
readonly richText?: RichTextEditorSelectionBookmark;
readonly markdown?: number;
}
export interface NoteIdToEditorCursorLocations {
[noteId: string]: EditorCursorLocations;
}
export interface VisibleDialogs {
[dialogKey: string]: boolean;
}
@@ -42,6 +53,9 @@ export interface AppWindowState extends WindowState {
devToolsVisible: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
watchedResources: any;
lastEditorScrollPercents: NoteIdToScrollPercent;
lastEditorCursorLocations: NoteIdToEditorCursorLocations;
}
interface BackgroundWindowStates {
@@ -55,7 +69,6 @@ export interface AppState extends State, AppWindowState {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
navHistory: any[];
watchedNoteFiles: string[];
lastEditorScrollPercents: EditorScrollPercents;
focusedField: string;
layoutMoveMode: boolean;
startupPluginsLoaded: boolean;
@@ -66,7 +79,7 @@ export interface AppState extends State, AppWindowState {
isResettingLayout: boolean;
}
export const createAppDefaultWindowState = (): AppWindowState => {
export const createAppDefaultWindowState = (globalState: AppState|null): AppWindowState => {
return {
...defaultWindowState,
visibleDialogs: {},
@@ -75,6 +88,12 @@ export const createAppDefaultWindowState = (): AppWindowState => {
editorCodeView: true,
devToolsVisible: false,
watchedResources: {},
// Maintain the scroll and cursor location for secondary windows separate from the
// main window. This prevents scrolling in a secondary window from changing/resetting
// the default scroll position in the main window:
lastEditorCursorLocations: globalState?.lastEditorCursorLocations ?? {},
lastEditorScrollPercents: globalState?.lastEditorScrollPercents ?? {},
};
};
@@ -82,7 +101,7 @@ export const createAppDefaultWindowState = (): AppWindowState => {
export function createAppDefaultState(resourceEditWatcherDefaultState: any): AppState {
return {
...defaultState,
...createAppDefaultWindowState(),
...createAppDefaultWindowState(null),
route: {
type: 'NAV_GO',
routeName: 'Main',
@@ -90,7 +109,6 @@ export function createAppDefaultState(resourceEditWatcherDefaultState: any): App
},
navHistory: [],
watchedNoteFiles: [],
lastEditorScrollPercents: {},
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
focusedField: null,
layoutMoveMode: false,
@@ -299,6 +317,18 @@ export default function(state: AppState, action: any) {
}
break;
case 'EDITOR_CURSOR_POSITION_SET':
{
newState = { ...state };
const newCursorLocations = { ...newState.lastEditorCursorLocations };
newCursorLocations[action.noteId] = {
...(newCursorLocations[action.noteId] ?? {}),
...action.location,
};
newState.lastEditorCursorLocations = newCursorLocations;
}
break;
case 'NOTE_DEVTOOLS_TOGGLE':
newState = { ...state };
newState.devToolsVisible = !newState.devToolsVisible;

View File

@@ -63,6 +63,8 @@ import { refreshFolders } from '@joplin/lib/folders-screen-utils';
import initializeCommandService from './utils/initializeCommandService';
import OcrDriverBase from '@joplin/lib/services/ocr/OcrDriverBase';
import PerformanceLogger from '@joplin/lib/PerformanceLogger';
import Note from '@joplin/lib/models/Note';
import Resource from '@joplin/lib/models/Resource';
const perfLogger = PerformanceLogger.create();
@@ -683,6 +685,11 @@ class Application extends BaseApplication {
debug: new DebugService(reg.db()),
resourceService: ResourceService.instance(),
searchEngine: SearchEngine.instance(),
shim,
Note,
Folder,
Resource,
Setting,
ocrService: () => this.ocrService_,
};
});

View File

@@ -2,7 +2,7 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
import { _ } from '@joplin/lib/locale';
import { stateUtils } from '@joplin/lib/reducer';
import Note from '@joplin/lib/models/Note';
import { createAppDefaultWindowState } from '../app.reducer';
import { AppState, createAppDefaultWindowState } from '../app.reducer';
import Setting from '@joplin/lib/models/Setting';
export const declaration: CommandDeclaration = {
@@ -25,7 +25,7 @@ export const runtime = (): CommandRuntime => {
folderId: note.parent_id,
windowId: `window-${noteId}-${idCounter++}`,
defaultAppWindowState: {
...createAppDefaultWindowState(),
...createAppDefaultWindowState(context.state as AppState),
noteVisiblePanes: Setting.value('noteVisiblePanes'),
editorCodeView: Setting.value('editor.codeView'),
},

View File

@@ -225,7 +225,7 @@ const Button = React.forwardRef(({
animation={iconAnimation}
mr={iconOnly ? '0' : '6px'}
color={color}
className={iconName}
className={`${iconName} icon`}
role='img'
/>;
}

View File

@@ -8,6 +8,7 @@ import { PluginManifest } from '@joplin/lib/services/plugins/utils/types';
import bridge from '../../../../services/bridge';
import { ItemEvent, PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import getPluginHelpUrl from '@joplin/lib/services/plugins/utils/getPluginHelpUrl';
export enum InstallState {
NotInstalled = 1,
@@ -150,8 +151,7 @@ export default function(props: Props) {
const onNameClick = useCallback(() => {
const manifest = item.manifest;
if (!manifest.homepage_url) return;
void bridge().openExternal(manifest.homepage_url);
void bridge().openExternal(getPluginHelpUrl(manifest.id));
}, [item]);
const onRecommendedClick = useCallback(() => {

View File

@@ -15,7 +15,7 @@ import { connect } from 'react-redux';
import { AppState } from '../../app.reducer';
import Setting from '@joplin/lib/models/Setting';
import CommandService from '@joplin/lib/services/CommandService';
import { PublicPrivateKeyPair } from '@joplin/lib/services/e2ee/ppk';
import { PublicPrivateKeyPair } from '@joplin/lib/services/e2ee/ppk/ppk';
import ToggleAdvancedSettingsButton from '../ConfigScreen/controls/ToggleAdvancedSettingsButton';
import MacOSMissingPasswordHelpLink from '../ConfigScreen/controls/MissingPasswordHelpLink';

View File

@@ -30,6 +30,8 @@ import useEditorSearchHandler from '../utils/useEditorSearchHandler';
import CommandService from '@joplin/lib/services/CommandService';
import useRefocusOnVisiblePaneChange from './utils/useRefocusOnVisiblePaneChange';
import { WindowIdContext } from '../../../../NewWindowOrIFrame';
import eventManager, { EventName, ResourceChangeEvent } from '@joplin/lib/eventManager';
import useSyncEditorValue from './utils/useSyncEditorValue';
const logger = Logger.create('CodeMirror6');
const logDebug = (message: string) => logger.debug(message);
@@ -166,9 +168,8 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
},
scrollTo: (options: ScrollOptions) => {
if (options.type === ScrollOptionTypes.Hash) {
if (!webviewRef.current) return;
const hash: string = options.value;
webviewRef.current.send('scrollToHash', hash);
webviewRef.current?.send('scrollToHash', hash);
editorRef.current.jumpToHash(hash);
} else if (options.type === ScrollOptionTypes.Percent) {
const percent = options.value as number;
@@ -272,6 +273,17 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
props.noteId, props.useCustomPdfViewer,
]);
useEffect(() => {
const listener = (event: ResourceChangeEvent) => {
editorRef.current?.onResourceChanged(event.id);
};
eventManager.on(EventName.ResourceChange, listener);
return () => {
eventManager.off(EventName.ResourceChange, listener);
};
}, [props.resourceInfos]);
useEffect(() => {
if (!webviewReady) return;
@@ -330,6 +342,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
} else if (event.kind === EditorEventType.Change) {
codeMirror_change(event.value);
} else if (event.kind === EditorEventType.SelectionRangeChange) {
props.onCursorMotion({ markdown: event.from });
setSelectionRange({ from: event.from, to: event.to });
} else if (event.kind === EditorEventType.UpdateSearchDialog) {
if (lastSearchState.current?.searchText !== event.searchState.searchText) {
@@ -343,7 +356,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
} else if (event.kind === EditorEventType.FollowLink) {
void CommandService.instance().execute('openItem', event.link);
}
}, [editor_scroll, codeMirror_change, props.setLocalSearch, props.setShowLocalSearch]);
}, [editor_scroll, codeMirror_change, props.setLocalSearch, props.setShowLocalSearch, props.onCursorMotion]);
const onSelectPastBeginning = useCallback(() => {
void CommandService.instance().execute('focusElement', 'noteTitle');
@@ -388,15 +401,17 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
props.tabMovesFocus,
]);
// Update the editor's value
useEffect(() => {
// 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.noteId]);
const initialCursorLocationRef = useRef(0);
initialCursorLocationRef.current = props.initialCursorLocation.markdown ?? 0;
useSyncEditorValue({
content: props.content,
visiblePanes: props.visiblePanes,
onMessage: props.onMessage,
editorRef,
noteId: props.noteId,
initialCursorLocationRef,
});
const renderEditor = () => {
return (
@@ -404,6 +419,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
<Editor
style={styles.editor}
initialText={props.content}
initialSelectionRef={initialCursorLocationRef}
initialNoteId={props.noteId}
ref={editorRef}
settings={editorSettings}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { ForwardedRef } from 'react';
import { ForwardedRef, RefObject } from 'react';
import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react';
import { EditorProps, LogMessageCallback, OnEventCallback, ContentScriptData } from '@joplin/editor/types';
import createEditor from '@joplin/editor/CodeMirror/createEditor';
@@ -23,6 +23,7 @@ import getResourceBaseUrl from '../../../utils/getResourceBaseUrl';
interface Props extends EditorProps {
style: React.CSSProperties;
pluginStates: PluginStates;
initialSelectionRef: RefObject<number>;
onEditorPaste: (event: Event)=> void;
externalSearch: SearchMarkers;
@@ -110,12 +111,12 @@ const Editor = (props: Props, ref: ForwardedRef<CodeMirrorControl>) => {
const editor = createEditor(editorContainerRef.current, {
...editorProps,
resolveImageSrc: async src => {
resolveImageSrc: async (src, reloadCounter) => {
const url = parseResourceUrl(src);
if (!url.itemId) return null;
const item = await Resource.load(url.itemId);
if (!item) return null;
return `${getResourceBaseUrl()}/${resourceFilename(item)}`;
return `${getResourceBaseUrl()}/${resourceFilename(item)}${reloadCounter ? `?r=${reloadCounter}` : ''}`;
},
});
editor.addStyles({
@@ -127,6 +128,9 @@ const Editor = (props: Props, ref: ForwardedRef<CodeMirrorControl>) => {
direction: 'unset',
},
});
const cursor = props.initialSelectionRef.current;
editor.select(cursor, cursor);
setEditor(editor);
return () => {

View File

@@ -0,0 +1,50 @@
import { useEffect, useRef, RefObject } from 'react';
import { OnMessage } from '../../../../utils/types';
import CodeMirrorControl from '@joplin/editor/CodeMirror/CodeMirrorControl';
interface Props {
content: string;
visiblePanes: string[];
onMessage: OnMessage;
editorRef: RefObject<CodeMirrorControl>;
noteId: string;
initialCursorLocationRef: RefObject<number>;
}
// Updates the editor's value as necessary
const useSyncEditorValue = ({ content, visiblePanes, onMessage, editorRef, noteId, initialCursorLocationRef }: Props) => {
const visiblePanesRef = useRef(visiblePanes);
visiblePanesRef.current = visiblePanes;
const onMessageRef = useRef(onMessage);
onMessageRef.current = onMessage;
const lastNoteIdRef = useRef(noteId);
useEffect(() => {
// Include the noteId in the update props to give plugins access
// to the current note ID.
const updateProps = { noteId: noteId };
if (editorRef.current?.updateBody(content, updateProps)) {
editorRef.current?.clearHistory();
// Only reset the cursor location when switching notes. If, for example,
// the note is updated from a secondary window, the cursor location shouldn't
// reset.
const noteChanged = lastNoteIdRef.current !== noteId;
if (noteChanged) {
const cursorLocation = initialCursorLocationRef.current;
editorRef.current?.select(cursorLocation, cursorLocation);
}
lastNoteIdRef.current = noteId;
// If the viewer isn't visible, the content should be considered rendered
// after the editor has finished updating:
if (!visiblePanesRef.current.includes('viewer')) {
onMessageRef.current({ channel: 'noteRenderComplete' });
}
}
}, [content, noteId, editorRef, initialCursorLocationRef]);
};
export default useSyncEditorValue;

View File

@@ -23,7 +23,7 @@ import { themeStyle } from '@joplin/lib/theme';
import { loadScript } from '../../../utils/loadScript';
import bridge from '../../../../services/bridge';
import { TinyMceEditorEvents } from './utils/types';
import type { Editor, EditorEvent } from 'tinymce';
import type { Bookmark, Editor, EditorEvent } from 'tinymce';
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
import shouldPasteResources from './utils/shouldPasteResources';
import lightTheme from '@joplin/lib/themes/light';
@@ -47,6 +47,7 @@ import Setting from '@joplin/lib/models/Setting';
import useTextPatternsLookup, { TextPatternContext } from './utils/useTextPatternsLookup';
import { toFileProtocolPath } from '@joplin/utils/path';
import { RenderResultPluginAsset } from '@joplin/renderer/types';
import useCursorPositioning from './utils/useCursorPositioning';
const logger = Logger.create('TinyMCE');
@@ -1046,6 +1047,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
return true;
}
const { onInitialContentSet } = useCursorPositioning({
initialCursorLocation: props.initialCursorLocation.richText as Bookmark,
onCursorUpdate: props.onCursorMotion,
editor,
});
const lastNoteIdRef = useRef(props.noteId);
useEffect(() => {
if (!editor) return () => {};
@@ -1136,6 +1143,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
await loadDocumentAssets(props.themeId, editor, allAssets);
dispatchDidUpdate(editor);
onInitialContentSet();
};
void loadContent();
@@ -1387,16 +1395,18 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
async function onCopy(event: any) {
const copiedContent = editor.selection.getContent();
if (!copiedContent) return;
copyHtmlToClipboard(copiedContent);
event.preventDefault();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
async function onCut(event: any) {
event.preventDefault();
const selectedContent = editor.selection.getContent();
if (!selectedContent) return;
copyHtmlToClipboard(selectedContent);
editor.insertContent('');
event.preventDefault();
onChangeHandler();
}
@@ -1444,7 +1454,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
// `compositionend` means that a user has finished entering a Chinese
// (or other languages that require IME) character.
editor.on(TinyMceEditorEvents.CompositionEnd, onChangeHandler);
editor.on(TinyMceEditorEvents.Cut, onCut);
editor.on(TinyMceEditorEvents.Cut, onCut, true);
editor.on(TinyMceEditorEvents.JoplinChange, onChangeHandler);
editor.on(TinyMceEditorEvents.Undo, onChangeHandler);
editor.on(TinyMceEditorEvents.Redo, onChangeHandler);

View File

@@ -0,0 +1,55 @@
import { useCallback, useEffect, useRef } from 'react';
import { Bookmark, Editor } from 'tinymce';
import { OnCursorMotion } from '../../../utils/types';
interface Props {
initialCursorLocation: Bookmark;
editor: Editor;
onCursorUpdate: OnCursorMotion;
}
const useCursorPositioning = ({ initialCursorLocation, editor, onCursorUpdate }: Props) => {
const initialCursorLocationRef = useRef(initialCursorLocation);
initialCursorLocationRef.current = initialCursorLocation;
const appliedInitialCursorLocationRef = useRef(false);
const onInitialContentSet = useCallback(() => {
if (editor) {
if (initialCursorLocationRef.current) {
editor.selection.moveToBookmark(initialCursorLocationRef.current);
}
appliedInitialCursorLocationRef.current = true;
}
}, [editor]);
useEffect(() => {
if (!editor) return () => {};
editor.on('ContentSet', onInitialContentSet);
const onSelectionChange = () => {
// Wait until the initial cursor position has been set. This avoids resetting
// the initial cursor position to zero when the editor first loads.
if (!appliedInitialCursorLocationRef.current) return;
// Use an offset bookmark -- the default bookmark type is not preserved after unloading
// and reloading the editor.
const offsetBookmarkId = 2;
onCursorUpdate({
richText: editor.selection.getBookmark(offsetBookmarkId, true),
});
};
editor.on('SelectionChange', onSelectionChange);
return () => {
editor.off('ContentSet', onInitialContentSet);
editor.off('SelectionChange', onSelectionChange);
};
}, [editor, onCursorUpdate, onInitialContentSet]);
return { onInitialContentSet };
};
export default useCursorPositioning;

View File

@@ -18,7 +18,7 @@ import { NoteEditorProps, FormNote, OnChangeEvent, AllAssetsOptions, NoteBodyEdi
import CommandService from '@joplin/lib/services/CommandService';
import Button, { ButtonLevel } from '../Button/Button';
import eventManager, { EventName } from '@joplin/lib/eventManager';
import { AppState } from '../../app.reducer';
import { AppState, EditorCursorLocations } from '../../app.reducer';
import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import { _, _n } from '@joplin/lib/locale';
import NoteTitleBar from './NoteTitle/NoteTitleBar';
@@ -57,6 +57,7 @@ import StatusBar from './StatusBar';
import useVisiblePluginEditorViewIds from '@joplin/lib/hooks/plugins/useVisiblePluginEditorViewIds';
import useConnectToEditorPlugin from './utils/useConnectToEditorPlugin';
import getResourceBaseUrl from './utils/getResourceBaseUrl';
import useInitialCursorLocation from './utils/useInitialCursorLocation';
const debounce = require('debounce');
@@ -329,13 +330,13 @@ function NoteEditorContent(props: NoteEditorProps) {
});
}, [formNote, setFormNote, handleProvisionalFlag, props.dispatch]);
const { scrollWhenReady, clearScrollWhenReady } = useScrollWhenReadyOptions({
const { scrollWhenReadyRef, clearScrollWhenReady } = useScrollWhenReadyOptions({
noteId: formNote.id,
selectedNoteHash: props.selectedNoteHash,
lastEditorScrollPercents: props.lastEditorScrollPercents,
editorRef,
});
const onMessage = useMessageHandler(scrollWhenReady, clearScrollWhenReady, windowId, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
const onMessage = useMessageHandler(scrollWhenReadyRef, clearScrollWhenReady, windowId, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
useResourceUnwatcher({ noteId: formNote.id, windowId });
@@ -409,6 +410,14 @@ function NoteEditorContent(props: NoteEditorProps) {
});
}, [props.dispatch]);
const onCursorMotion = useCallback((location: EditorCursorLocations) => {
props.dispatch({
type: 'EDITOR_CURSOR_POSITION_SET',
noteId: formNoteRef.current.id,
location,
});
}, [props.dispatch]);
function renderNoNotes(rootStyle: React.CSSProperties) {
const emptyDivStyle = {
backgroundColor: 'black',
@@ -419,6 +428,9 @@ function NoteEditorContent(props: NoteEditorProps) {
}
const searchMarkers = useSearchMarkers(showLocalSearch, localSearchMarkerOptions, props.searches, props.selectedSearchId, props.highlightedWords);
const initialCursorLocation = useInitialCursorLocation({
lastEditorCursorLocations: props.lastEditorCursorLocations, noteId: props.noteId,
});
const markupLanguage = formNote.markup_language;
const editorProps: NoteBodyEditorPropsAndRef = {
@@ -432,6 +444,7 @@ function NoteEditorContent(props: NoteEditorProps) {
content: formNote.body,
contentMarkupLanguage: markupLanguage,
contentOriginalCss: formNote.originalCss,
initialCursorLocation,
resourceInfos: resourceInfos,
resourceDirectory: Setting.value('resourceDir'),
htmlToMarkdown: htmlToMarkdown,
@@ -442,6 +455,7 @@ function NoteEditorContent(props: NoteEditorProps) {
dispatch: props.dispatch,
noteToolbar: null,
onScroll: onScroll,
onCursorMotion,
setLocalSearchResultCount: setLocalSearchResultCount,
setLocalSearch: localSearch_change,
setShowLocalSearch,
@@ -729,6 +743,7 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
notesParentType: windowState.notesParentType,
selectedNoteTags: windowState.selectedNoteTags,
lastEditorScrollPercents: state.lastEditorScrollPercents,
lastEditorCursorLocations: state.lastEditorCursorLocations,
selectedNoteHash: windowState.selectedNoteHash,
searches: state.searches,
selectedSearchId: windowState.selectedSearchId,

View File

@@ -15,6 +15,7 @@ const joplinRendererUtils = require('@joplin/renderer').utils;
const { clipboard } = require('electron');
import * as mimeUtils from '@joplin/lib/mime-utils';
import bridge from '../../../services/bridge';
import { getCollator, getCollatorLocale } from '@joplin/lib/models/utils/getCollator';
const md5 = require('md5');
const path = require('path');
@@ -43,22 +44,30 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
if (!filePaths || !filePaths.length) return null;
}
const collatorLocale = getCollatorLocale();
const collator = getCollator(collatorLocale);
filePaths = filePaths.sort((a, b) => {
return collator.compare(a, b);
});
let pos = options.position ?? 0;
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
const beforeLen = body.length;
try {
logger.info(`Attaching ${filePath}`);
const newBody = await shim.attachFileToNoteBody(body, filePath, options.position, {
const newBody = await shim.attachFileToNoteBody(body, filePath, pos, {
createFileURL: options.createFileURL,
resizeLargeImages: Setting.value('imageResizing'),
markupLanguage: options.markupLanguage,
resourceSuffix: i > 0 ? ' ' : '',
resourcePrefix: i > 0 ? ' ' : '',
});
if (!newBody) {
logger.info('File attachment was cancelled');
return null;
}
pos += newBody.length - beforeLen;
body = newBody;
logger.info('File was attached.');
} catch (error) {
@@ -66,7 +75,6 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
bridge().showErrorMessageBox(error.message);
}
}
return body;
}

View File

@@ -13,6 +13,8 @@ import { MarkupToHtmlOptions } from '../../hooks/useMarkupToHtml';
import { ScrollbarSize } from '@joplin/lib/models/settings/builtInMetadata';
import { RefObject, SetStateAction } from 'react';
import * as React from 'react';
import { ResourceEntity, ResourceLocalStateEntity } from '@joplin/lib/services/database/types';
import { EditorCursorLocations, NoteIdToEditorCursorLocations, NoteIdToScrollPercent } from '../../../app.reducer';
export interface AllAssetsOptions {
contentMaxWidthTarget?: string;
@@ -39,8 +41,8 @@ export interface NoteEditorProps {
notesParentType: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
selectedNoteTags: any[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
lastEditorScrollPercents: any;
lastEditorScrollPercents: NoteIdToScrollPercent;
lastEditorCursorLocations: NoteIdToEditorCursorLocations;
selectedNoteHash: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
searches: any[];
@@ -82,6 +84,14 @@ export interface NoteBodyEditorRef {
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>;
export type OnCursorMotion = (event: EditorCursorLocations)=> void;
export interface MessageEvent {
channel: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partially refactored old code before rule was applied
args?: any[];
}
export type OnMessage = (event: MessageEvent)=> void;
export interface NoteBodyEditorProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -101,12 +111,13 @@ export interface NoteBodyEditorProps {
contentKey: string;
contentMarkupLanguage: number;
contentOriginalCss: string;
initialCursorLocation: EditorCursorLocations;
onChange(event: OnChangeEvent): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
onWillChange(event: any): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
onMessage(event: any): void;
onMessage: OnMessage;
onScroll(event: { percent: number }): void;
onCursorMotion: OnCursorMotion;
markupToHtml: MarkupToHtmlHandler;
htmlToMarkdown: HtmlToMarkdownHandler;
allAssets: (markupLanguage: MarkupLanguage, options: AllAssetsOptions)=> Promise<RenderResultPluginAsset[]>;
@@ -214,10 +225,8 @@ export function defaultFormNote(): FormNote {
}
export interface ResourceInfo {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
localState: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
item: any;
localState: ResourceLocalStateEntity;
item: ResourceEntity;
}
export interface ResourceInfos {

View File

@@ -0,0 +1,17 @@
import { useMemo } from 'react';
import { EditorCursorLocations, NoteIdToEditorCursorLocations } from '../../../app.reducer';
interface Props {
lastEditorCursorLocations: NoteIdToEditorCursorLocations;
noteId: string;
}
const useInitialCursorLocation = ({ noteId, lastEditorCursorLocations }: Props) => {
const lastCursorLocation = lastEditorCursorLocations[noteId];
return useMemo((): EditorCursorLocations => {
return lastCursorLocation ?? { };
}, [lastCursorLocation]);
};
export default useInitialCursorLocation;

View File

@@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler, ScrollOptions } from './types';
import { RefObject, useCallback } from 'react';
import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler, ScrollOptions, MessageEvent } from './types';
import contextMenu from './contextMenu';
import CommandService from '@joplin/lib/services/CommandService';
import PostMessageService from '@joplin/lib/services/PostMessageService';
@@ -8,7 +8,7 @@ import { reg } from '@joplin/lib/registry';
import bridge from '../../../services/bridge';
export default function useMessageHandler(
scrollWhenReady: ScrollOptions|null,
scrollWhenReadyRef: RefObject<ScrollOptions|null>,
clearScrollWhenReady: ()=> void,
windowId: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@@ -21,8 +21,7 @@ export default function useMessageHandler(
htmlToMd: HtmlToMarkdownHandler,
mdToHtml: MarkupToHtmlHandler,
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
return useCallback(async (event: any) => {
return useCallback(async (event: MessageEvent) => {
const msg = event.channel ? event.channel : '';
const args = event.args;
const arg0 = args && args.length >= 1 ? args[0] : null;
@@ -35,8 +34,8 @@ export default function useMessageHandler(
s.splice(0, 1);
reg.logger().error(s.join(':'));
} else if (msg === 'noteRenderComplete') {
if (scrollWhenReady) {
const options = { ...scrollWhenReady };
if (scrollWhenReadyRef.current) {
const options = { ...scrollWhenReadyRef.current };
clearScrollWhenReady();
editorRef.current.scrollTo(options);
}
@@ -78,5 +77,5 @@ export default function useMessageHandler(
// bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
}
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
}, [dispatch, setLocalSearchResultCount, scrollWhenReady, formNote]);
}, [dispatch, setLocalSearchResultCount, scrollWhenReadyRef, formNote]);
}

View File

@@ -1,41 +1,44 @@
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { RefObject, useCallback, useRef } from 'react';
import { NoteBodyEditorRef, ScrollOptions, ScrollOptionTypes } from './types';
import usePrevious from '@joplin/lib/hooks/usePrevious';
import type { EditorScrollPercents } from '../../../app.reducer';
import type { NoteIdToScrollPercent } from '../../../app.reducer';
import useNowEffect from '@joplin/lib/hooks/useNowEffect';
interface Props {
noteId: string;
selectedNoteHash: string;
lastEditorScrollPercents: EditorScrollPercents;
lastEditorScrollPercents: NoteIdToScrollPercent;
editorRef: RefObject<NoteBodyEditorRef>;
}
const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollPercents, editorRef }: Props) => {
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions|null>(null);
const scrollWhenReadyRef = useRef<ScrollOptions|null>(null);
const previousNoteId = usePrevious(noteId);
const lastScrollPercentsRef = useRef<EditorScrollPercents>(null);
const lastScrollPercentsRef = useRef<NoteIdToScrollPercent>(null);
lastScrollPercentsRef.current = lastEditorScrollPercents;
useEffect(() => {
if (noteId === previousNoteId) return;
// This needs to be a nowEffect to prevent race conditions
useNowEffect(() => {
if (noteId === previousNoteId) return () => {};
if (editorRef.current) {
editorRef.current.resetScroll();
}
const lastScrollPercent = lastScrollPercentsRef.current[noteId] || 0;
setScrollWhenReady({
scrollWhenReadyRef.current = {
type: selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
});
};
return () => {};
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
const clearScrollWhenReady = useCallback(() => {
setScrollWhenReady(null);
scrollWhenReadyRef.current = null;
}, []);
return { scrollWhenReady, clearScrollWhenReady };
return { scrollWhenReadyRef, clearScrollWhenReady };
};
export default useScrollWhenReadyOptions;

View File

@@ -251,8 +251,6 @@ export default class PromptDialog extends React.Component<Props, any> {
} else {
onClose(true);
}
} else if (event.key === 'Escape') {
onClose(false);
}
};
@@ -309,7 +307,7 @@ export default class PromptDialog extends React.Component<Props, any> {
}
return (
<Dialog className='prompt-dialog' contentStyle={styles.dialog}>
<Dialog className='prompt-dialog' contentStyle={styles.dialog} onCancel={() => onClose(false, 'cancel')}>
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
<div style={{ display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor }}>
{inputComp}

View File

@@ -25,14 +25,13 @@ interface Props {
const SidebarComponent = (props: Props) => {
const renderSynchronizeButton = (type: string) => {
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
const iconAnimation = type !== 'sync' ? 'icon-infinite-rotation 1s linear infinite' : '';
return (
<StyledSynchronizeButton
level={ButtonLevel.SidebarSecondary}
className={`sidebar-sync-button ${type === 'sync' ? '' : '-syncing'}`}
iconName="icon-sync"
key="sync_button"
iconAnimation={iconAnimation}
title={label}
onClick={() => {
void CommandService.instance().execute('synchronize', type !== 'sync');

View File

@@ -356,6 +356,7 @@ const useOnRenderItem = (props: Props) => {
onClick={tagItem_click}
onTagDrop={onTagDrop_}
onContextMenu={onItemContextMenu}
label={item.label}
tag={tag}
itemCount={itemCount}
index={index}
@@ -384,7 +385,7 @@ const useOnRenderItem = (props: Props) => {
anchorRef={anchorRef}
selected={selected}
folderId={folder.id}
folderTitle={Folder.displayTitle(folder)}
folderTitle={item.label}
folderIcon={Folder.unserializeIcon(folder.icon)}
depth={item.depth}
isExpanded={isExpanded}

View File

@@ -49,6 +49,24 @@ const getParentOffset = (childIndex: number, listItems: ListItem[]): number|null
return null;
};
const findNextTypeAheadMatch = (selectedIndex: number, query: string, listItems: ListItem[]) => {
const matches = (item: ListItem) => {
return item.label.startsWith(query);
};
const indexBefore = listItems.slice(0, selectedIndex).findIndex(matches);
// Search in all results **after** the current. This prevents the current item from
// always being identified as the next match, if the user repeatedly presses the
// same key.
const startAfter = selectedIndex + 1;
let indexAfter = listItems.slice(startAfter).findIndex(matches);
if (indexAfter !== -1) {
indexAfter += startAfter;
}
// Prefer jumping to the next match, rather than the previous
const matchingIndex = indexAfter !== -1 ? indexAfter : indexBefore;
return matchingIndex;
};
const useOnSidebarKeyDownHandler = (props: Props) => {
const { updateSelectedIndex, listItems, selectedIndex, collapsedFolderIds, dispatch } = props;
@@ -82,9 +100,22 @@ const useOnSidebarKeyDownHandler = (props: Props) => {
indexChange = 1;
} else if ((event.ctrlKey || event.metaKey) && event.code === 'KeyA') { // ctrl+a or cmd+a
event.preventDefault();
} else if (event.code === 'Home') {
event.preventDefault();
updateSelectedIndex(0);
indexChange = 0;
} else if (event.code === 'End') {
event.preventDefault();
updateSelectedIndex(listItems.length - 1);
indexChange = 0;
} else if (event.code === 'Enter' && !event.shiftKey) {
event.preventDefault();
void CommandService.instance().execute('focusElement', 'noteList');
} else if (selectedIndex && selectedIndex >= 0 && event.key.length === 1) {
const nextMatch = findNextTypeAheadMatch(selectedIndex, event.key, listItems);
if (nextMatch !== -1) {
indexChange = nextMatch - selectedIndex;
}
}
if (indexChange !== 0) {

View File

@@ -4,6 +4,8 @@ import { FolderEntity, TagsWithNoteCountEntity } from '@joplin/lib/services/data
import { buildFolderTree, renderFolders, renderTags } from '@joplin/lib/components/shared/side-menu-shared';
import { _ } from '@joplin/lib/locale';
import toggleHeader from './utils/toggleHeader';
import Folder from '@joplin/lib/models/Folder';
import Tag from '@joplin/lib/models/Tag';
interface Props {
tags: TagsWithNoteCountEntity[];
@@ -18,6 +20,7 @@ const useSidebarListData = (props: Props): ListItem[] => {
return renderTags<ListItem>(props.tags, (tag): TagListItem => {
return {
kind: ListItemType.Tag,
label: Tag.displayTitle(tag),
tag,
key: tag.id,
depth: 1,
@@ -38,6 +41,7 @@ const useSidebarListData = (props: Props): ListItem[] => {
return renderFolders<ListItem>(renderProps, (folder, hasChildren, depth): FolderListItem => {
return {
kind: ListItemType.Folder,
label: Folder.displayTitle(folder),
folder,
hasChildren,
// The toplevel headers have depth 1, so the toplevel notebook needs
@@ -65,9 +69,9 @@ const useSidebarListData = (props: Props): ListItem[] => {
hasChildren: folderItems.items.length > 0,
};
const foldersSectionContent: ListItem[] = props.folderHeaderIsExpanded ? [
{ kind: ListItemType.AllNotes, key: 'all-notes', depth: 2, hasChildren: false },
{ kind: ListItemType.AllNotes, label: _('All notes'), key: 'all-notes', depth: 2, hasChildren: false },
...folderItems.items,
{ kind: ListItemType.Spacer, key: 'after-folders-spacer', depth: 1, hasChildren: false },
{ kind: ListItemType.Spacer, label: '', key: 'after-folders-spacer', depth: 1, hasChildren: false },
] : [];
const tagsHeader: HeaderListItem = {

View File

@@ -7,7 +7,6 @@ import Setting from '@joplin/lib/models/Setting';
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import CommandService from '@joplin/lib/services/CommandService';
import PerFolderSortOrderService from '../../../services/sortOrder/PerFolderSortOrderService';
import { _ } from '@joplin/lib/locale';
import { connect } from 'react-redux';
import EmptyExpandLink from './EmptyExpandLink';
import ListItemWrapper, { ListItemRef } from './ListItemWrapper';
@@ -70,7 +69,7 @@ const AllNotesItem: React.FC<Props> = props => {
onClick={onAllNotesClick_}
onContextMenu={toggleAllNotesContextMenu}
>
{_('All notes')}
{props.item.label}
</StyledListItemAnchor>
</ListItemWrapper>
);

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { useCallback } from 'react';
import { useCallback, useState } from 'react';
import { StyledHeader, StyledHeaderIcon, StyledHeaderLabel } from '../styles';
import { HeaderId, HeaderListItem } from '../types';
import bridge from '../../../services/bridge';
@@ -25,6 +25,8 @@ const HeaderItem: React.FC<Props> = props => {
const item = props.item;
const onItemClick = item.onClick;
const itemId = item.id;
const [isHovered, setIsHovered] = useState(false);
const expanded = item.expanded;
const onClick: React.MouseEventHandler<HTMLElement> = useCallback(event => {
if (onItemClick) {
@@ -44,6 +46,14 @@ const HeaderItem: React.FC<Props> = props => {
}
}, [itemId]);
const handleMouseEnter = useCallback(() => {
setIsHovered(true);
}, []);
const handleMouseLeave = useCallback(() => {
setIsHovered(false);
}, []);
return (
<ListItemWrapper
containerRef={props.anchorRef}
@@ -58,8 +68,12 @@ const HeaderItem: React.FC<Props> = props => {
{...item.extraProps}
onDrop={props.onDrop}
>
<StyledHeader onClick={onClick}>
<StyledHeaderIcon aria-hidden='true' role='img' className={item.iconName}/>
<StyledHeader
onClick={onClick}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<StyledHeaderIcon aria-hidden='true' role='img' className={isHovered ? `fas ${expanded ? 'fa-caret-down' : 'fa-caret-right'}` : item.iconName}/>
<StyledHeaderLabel>{item.label}</StyledHeaderLabel>
</StyledHeader>
</ListItemWrapper>

View File

@@ -5,7 +5,6 @@ import { StyledListItemAnchor, StyledSpanFix } from '../styles';
import { TagsWithNoteCountEntity } from '@joplin/lib/services/database/types';
import BaseModel from '@joplin/lib/BaseModel';
import NoteCount from './NoteCount';
import Tag from '@joplin/lib/models/Tag';
import EmptyExpandLink from './EmptyExpandLink';
import ListItemWrapper, { ListItemRef } from './ListItemWrapper';
@@ -15,6 +14,7 @@ interface Props {
anchorRef: ListItemRef;
selected: boolean;
tag: TagsWithNoteCountEntity;
label: string;
onTagDrop: React.DragEventHandler<HTMLElement>;
onContextMenu: React.MouseEventHandler<HTMLElement>;
onClick: (event: TagLinkClickEvent)=> void;
@@ -58,7 +58,7 @@ const TagItem = (props: Props) => {
onContextMenu={props.onContextMenu}
onClick={onClickHandler}
>
<StyledSpanFix className="tag-label">{Tag.displayTitle(tag)}</StyledSpanFix>
<StyledSpanFix className="tag-label">{props.label}</StyledSpanFix>
{noteCount}
</StyledListItemAnchor>
</ListItemWrapper>

View File

@@ -5,4 +5,5 @@
@use 'styles/sidebar-expand-link.scss';
@use 'styles/sidebar-header-container.scss';
@use 'styles/sidebar-spacer-item.scss';
@use 'styles/sidebar-header-button.scss';
@use 'styles/sidebar-header-button.scss';
@use 'styles/sidebar-sync-button.scss';

View File

@@ -28,6 +28,11 @@ export const StyledHeader = styled.div`
user-select: none;
text-transform: uppercase;
//cursor: pointer;
cursor: default;
transition: background 0.2s;
&:hover {
background: ${(props: StyleProps) => props.theme.backgroundColorHover2};
}
`;
export const StyledHeaderIcon = styled.i`

View File

@@ -0,0 +1,16 @@
@keyframes icon-infinite-rotation {
to {
transform: rotate(360deg);
}
}
.sidebar-sync-button {
&.-syncing > .icon {
animation: icon-infinite-rotation 1s linear infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
}

View File

@@ -16,6 +16,8 @@ export enum ListItemType {
interface BaseListItem {
key: string;
// Used for typeahead
label: string;
depth: number;
hasChildren: boolean;
}
@@ -26,7 +28,6 @@ interface ToplevelListItem extends BaseListItem {
export interface HeaderListItem extends ToplevelListItem {
kind: ListItemType.Header;
label: string;
expanded: boolean;
iconName: string;
id: HeaderId;

View File

@@ -38,7 +38,7 @@ describe('NoteListUtils', () => {
const mockStore = {
getState: () => {
return {
...createAppDefaultWindowState(),
...createAppDefaultWindowState(null),
settings: {},
};
},

View File

@@ -143,7 +143,7 @@ export default class NoteListUtils {
menu.append(new MenuItem({ type: 'separator' }));
if ([9, 10].includes(Setting.value('sync.target'))) {
if ([9, 10, 11].includes(Setting.value('sync.target'))) {
menu.append(
new MenuItem(
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied

View File

@@ -2,11 +2,19 @@
<html>
<head>
<meta charset="UTF-8">
<!--
No CPS because we need to allow everything due to some dependencies (eg. depd, which comes from maybe Node or Electron
uses 'eval'.
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval'">
-->
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self' joplin-content://* ;
connect-src 'self' * http://* https://* joplin-content://* blob: ;
style-src 'unsafe-inline' 'self' blob: joplin-content://* https://* http://* ;
child-src 'self' joplin-content://* ;
script-src 'self' 'unsafe-inline' joplin-content://* ;
media-src 'self' * blob: data: https://* http://* joplin-content://* ;
img-src 'self' blob: data: http://* https://* joplin-content://* ;
font-src 'self' http://* https://* blob: data: joplin-content://* ;
"
/>
<title>Joplin</title>
<!-- Note: Add new dynamic CSS imports to style.scss to allow them to be included in secondary windows. -->
<link rel="stylesheet" href="style.min.css">

View File

@@ -8,6 +8,7 @@ import getMainWindow from './util/getMainWindow';
import setFilePickerResponse from './util/setFilePickerResponse';
import setMessageBoxResponse from './util/setMessageBoxResponse';
import getImageSourceSize from './util/getImageSourceSize';
import setSettingValue from './util/setSettingValue';
test.describe('main', () => {
@@ -19,6 +20,13 @@ test.describe('main', () => {
await mainPage.waitFor();
});
test('app should support French localization', async ({ mainWindow, electronApp }) => {
await setSettingValue(electronApp, mainWindow, 'locale', 'fr_FR');
// The "Notebooks" header should be localized
const localizedText = mainWindow.getByText('Carnets').first();
await expect(localizedText).toBeAttached();
});
test('should be able to create and edit a new note', async ({ mainWindow }) => {
const mainScreen = await new MainScreen(mainWindow).setup();
const editor = await mainScreen.createNewNote('Test note');

View File

@@ -180,8 +180,8 @@ test.describe('markdownEditor', () => {
await expect(matches).toHaveCount(1);
// Should continue searching after switching to view-only mode
await noteEditor.toggleEditorLayoutButton.click();
await noteEditor.toggleEditorLayoutButton.click();
await noteEditor.toggleEditorLayout();
await noteEditor.toggleEditorLayout();
await expect(noteEditor.codeMirrorEditor).not.toBeVisible();
await expect(noteEditor.editorSearchInput).not.toBeVisible();
await expect(noteEditor.viewerSearchInput).toBeVisible();
@@ -194,7 +194,7 @@ test.describe('markdownEditor', () => {
await expect(matches).toHaveCount(0);
// After showing the viewer again, search should still be hidden
await noteEditor.toggleEditorLayoutButton.click();
await noteEditor.toggleEditorLayout();
await expect(noteEditor.codeMirrorEditor).toBeVisible();
await expect(noteEditor.editorSearchInput).not.toBeVisible();
});
@@ -274,5 +274,57 @@ test.describe('markdownEditor', () => {
expect(imageSize[0]).toBeGreaterThan(0);
expect(imageSize[1]).toBeGreaterThan(0);
});
test('ctrl-clicking on note links should open the linked note (when the viewer is hidden)', async ({ mainWindow }) => {
const mainScreen = await new MainScreen(mainWindow).setup();
await mainScreen.createNewNote('Original');
const noteEditor = mainScreen.noteEditor;
await noteEditor.hideViewer();
await noteEditor.focusCodeMirrorEditor();
await mainWindow.keyboard.type('# Test');
await mainWindow.keyboard.press('Enter');
await mainWindow.keyboard.type('## Test 2');
await mainWindow.keyboard.press('Enter');
await mainWindow.keyboard.type('### Test 3');
const editorContent = await noteEditor.contentLocator();
// Extract the note ID
const note1Locator = mainScreen.noteList.getNoteItemByTitle('Original');
await note1Locator.dragTo(editorContent);
const linkExpression = /\[[^\]]*\]\(:\/([a-z0-9]{32})\)/;
await noteEditor.expectToHaveText(linkExpression);
const targetNoteId = (await editorContent.textContent()).match(linkExpression)[1];
await mainScreen.createNewNote('Test note links');
// Create a new link to a header
await noteEditor.focusCodeMirrorEditor();
await mainWindow.keyboard.press('Enter');
await mainWindow.keyboard.press('Enter');
await mainWindow.keyboard.type('[link](:/');
await mainWindow.keyboard.type(targetNoteId);
await mainWindow.keyboard.type('#test-2');
await mainWindow.keyboard.type(')');
await mainWindow.keyboard.press('Enter');
// Clicking the link should navigate to note1
const link = editorContent.getByText(/\[?link\]?/);
await link.click({ modifiers: ['ControlOrMeta'] });
await expect(noteEditor.noteTitleInput).toHaveValue('Original');
await noteEditor.expectToHaveText(/^# Test/);
await expect.poll(() => editorContent.evaluate(async editor => {
const selection = getSelection();
return editor.contains(selection.anchorNode);
})).toBe(true);
// The cursor should be positioned on the linked-to header
await expect.poll(async () => {
await mainWindow.keyboard.type('[[cursor]]');
await noteEditor.expectToHaveText(/## Test 2\[\[cursor\]\]/);
return true;
}).toBe(true);
});
});

View File

@@ -72,4 +72,10 @@ export default class MainScreen {
await setFilePickerResponse(electronApp, [path]);
await activateMainMenuItem(electronApp, 'HTML - HTML document (Directory)', 'Import');
}
public async pluginPanelLocator(pluginId: string) {
return this.page.locator(
`iframe[id^=${JSON.stringify(`plugin-view-${pluginId}`)}]`,
);
}
}

View File

@@ -65,12 +65,20 @@ export default class NoteEditorPage {
}
}
public async expectToHaveText(content: string) {
public async expectToHaveText(expected: string|RegExp) {
// expect(...).toHaveText can fail in the Rich Text Editor (perhaps due to frame locators).
// Using expect.poll refreshes the locator on each attempt, which seems to prevent flakiness.
await expect.poll(
async () => (await this.contentLocator()).textContent(),
).toBe(content);
const expectResult = expect.poll(
// Use .innerText: textContent doesn't handle line breaks correctly in the CodeMirror
// editor.
async () => (await this.contentLocator()).innerText(),
);
// Allow `expected` to be either an exact match (a string) or a pattern
if (typeof expected === 'string') {
await expectResult.toBe(expected);
} else {
await expectResult.toMatch(expected);
}
}
public getNoteViewerFrameLocator() {
@@ -117,4 +125,14 @@ export default class NoteEditorPage {
await expect(backButton).not.toBeDisabled();
await backButton.click();
}
public async toggleEditorLayout() {
await this.toggleEditorLayoutButton.click();
}
public async hideViewer() {
await expect(this.noteViewerContainer).toBeVisible();
await this.toggleEditorLayout();
await expect(this.noteViewerContainer).not.toBeVisible();
}
}

View File

@@ -30,7 +30,7 @@ test.describe('pluginApi', () => {
await mainScreen.createNewNote('First note');
const editor = mainScreen.noteEditor;
await editor.expectToHaveText('');
await editor.expectToHaveText('\n');
await mainScreen.goToAnything.runCommand(app, 'showTestDialog');
// Wait for the iframe to load
@@ -45,6 +45,41 @@ test.describe('pluginApi', () => {
}));
});
test('should report the correct visibility state for dialogs', async ({ startAppWithPlugins }) => {
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/dialogs.js']);
const mainScreen = await new MainScreen(mainWindow).setup();
await mainScreen.createNewNote('Dialog test note');
const editor = mainScreen.noteEditor;
const expectVisible = async (visible: boolean) => {
// Check UI visibility
if (visible) {
await expect(mainScreen.dialog).toBeVisible();
} else {
await expect(mainScreen.dialog).not.toBeVisible();
}
// Check visibility reported through the plugin API
await expect.poll(async () => {
await mainScreen.goToAnything.runCommand(app, 'getTestDialogVisibility');
const editorContent = await editor.contentLocator();
return editorContent.textContent();
}).toBe(JSON.stringify({
visible: visible,
active: visible,
}));
};
await expectVisible(false);
await mainScreen.goToAnything.runCommand(app, 'showTestDialog');
await expectVisible(true);
// Submitting the dialog should include form data in the output
await mainScreen.dialog.getByRole('button', { name: 'Okay' }).click();
await expectVisible(false);
});
test('should be possible to create multiple toasts with the same text from a plugin', async ({ startAppWithPlugins }) => {
const { app, mainWindow } = await startAppWithPlugins(['resources/test-plugins/showToast.js']);
const mainScreen = await new MainScreen(mainWindow).setup();
@@ -122,5 +157,30 @@ test.describe('pluginApi', () => {
await msleep(Second);
await expect(noteEditor.codeMirrorEditor).toHaveText(expectedUpdatedText);
});
test('should support hiding and showing panels', async ({ startAppWithPlugins }) => {
const { mainWindow, app } = await startAppWithPlugins(['resources/test-plugins/panels.js']);
const mainScreen = await new MainScreen(mainWindow).setup();
await mainScreen.createNewNote('Test note (panels)');
const panelLocator = await mainScreen.pluginPanelLocator('org.joplinapp.plugins.example.panels');
const noteEditor = mainScreen.noteEditor;
await mainScreen.goToAnything.runCommand(app, 'testShowPanel');
await expect(noteEditor.codeMirrorEditor).toHaveText('visible');
// Panel should be visible
await expect(panelLocator).toBeVisible();
// The panel should have the expected content
const panelContent = panelLocator.contentFrame();
await expect(
panelContent.getByRole('heading', { name: 'Panel content' }),
).toBeAttached();
await mainScreen.goToAnything.runCommand(app, 'testHidePanel');
await expect(noteEditor.codeMirrorEditor).toHaveText('hidden');
await expect(panelLocator).not.toBeVisible();
});
});

View File

@@ -47,5 +47,22 @@ joplin.plugins.register({
}));
},
});
await joplin.commands.register({
name: 'getTestDialogVisibility',
label: 'Returns the dialog visibility state',
execute: async () => {
// panels.visible should also work for dialogs.
const visible = await joplin.views.panels.visible(dialogHandle);
// For dialogs, isActive should return the visibility.
// (Prefer panels.visible for dialogs).
const active = await joplin.views.panels.isActive(dialogHandle);
await joplin.commands.execute('editor.setText', JSON.stringify({
visible,
active,
}));
},
});
},
});

View File

@@ -0,0 +1,71 @@
// Allows referencing the Joplin global:
/* eslint-disable no-undef */
// Allows the `joplin-manifest` block comment:
/* eslint-disable multiline-comment-style */
/* joplin-manifest:
{
"id": "org.joplinapp.plugins.example.panels",
"manifest_version": 1,
"app_min_version": "3.1",
"name": "JS Bundle test",
"description": "JS Bundle Test plugin",
"version": "1.0.0",
"author": "",
"homepage_url": "https://joplinapp.org"
}
*/
const waitFor = async (condition) => {
const wait = () => {
return new Promise(resolve => {
setTimeout(() => resolve(), 100);
});
};
for (let i = 0; i < 100; i++) {
if (await condition()) {
return;
}
// Pause for a brief delay
await wait();
}
throw new Error('Condition was never true');
};
joplin.plugins.register({
onStart: async function() {
const panels = joplin.views.panels;
const view = await panels.create('panelTestView');
await panels.setHtml(view, '<h1>Panel content</h1><p>Test</p>');
await panels.hide(view);
await joplin.commands.register({
name: 'testShowPanel',
label: 'Test panel visibility',
execute: async () => {
await panels.show(view);
await waitFor(async () => {
return await panels.visible(view);
});
await joplin.commands.execute('editor.setText', 'visible');
},
});
await joplin.commands.register({
name: 'testHidePanel',
label: 'Test: Hide the panel',
execute: async () => {
await panels.hide(view);
await waitFor(async () => {
return !await panels.visible(view);
});
await joplin.commands.execute('editor.setText', 'hidden');
},
});
},
});

View File

@@ -44,6 +44,28 @@ test.describe('sidebar', () => {
await expect(mainWindow.locator(':focus')).toHaveText('All notes');
});
test('should allow changing the focused folder by pressing the first character of the title', async ({ electronApp, mainWindow }) => {
const mainScreen = await new MainScreen(mainWindow).setup();
const sidebar = mainScreen.sidebar;
const folderAHeader = await sidebar.createNewFolder('1-Test A');
await expect(folderAHeader).toBeVisible();
const folderBHeader = await sidebar.createNewFolder('Folder b');
await expect(folderBHeader).toBeVisible();
await folderBHeader.click();
await sidebar.forceUpdateSorting(electronApp);
await folderBHeader.click();
await mainWindow.keyboard.type('1');
await expect(mainWindow.locator(':focus')).toHaveText('1-Test A');
await mainWindow.keyboard.type('F');
await expect(mainWindow.locator(':focus')).toHaveText('Folder b');
await mainWindow.keyboard.type('A');
await expect(mainWindow.locator(':focus')).toHaveText('All notes');
});
test('left/right arrow keys should expand/collapse notebooks', async ({ electronApp, mainWindow }) => {
const mainScreen = await new MainScreen(mainWindow).setup();
const sidebar = mainScreen.sidebar;

View File

@@ -97,12 +97,6 @@ a {
border-radius: 5px;
}
@keyframes icon-infinite-rotation{
to {
transform: rotate(360deg);
}
}
.rdtPicker {
min-width: 250px;
width: auto !important;

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "3.4.11",
"version": "3.5.5",
"description": "Joplin for Desktop",
"main": "main.bundle.js",
"private": true,
@@ -16,6 +16,7 @@
"test": "jest",
"test-ui": "gulp before-start && playwright test",
"test-ci": "yarn test",
"resolve-sourcemap": "node tools/resolveSourceMap.js",
"modifyReleaseAssets": "node tools/modifyReleaseAssets.js"
},
"repository": {
@@ -46,6 +47,7 @@
"asar": true,
"asarUnpack": "./node_modules/node-notifier/vendor/**",
"win": {
"sign": "./sign.js",
"rfc3161TimeStampServer": "http://timestamp.digicert.com",
"icon": "../../Assets/ImageSources/Joplin.ico",
"target": [
@@ -131,28 +133,27 @@
"homepage": "https://github.com/laurent22/joplin#readme",
"devDependencies": {
"7zip-bin": "5.2.0",
"@axe-core/playwright": "4.10.1",
"@axe-core/playwright": "4.10.2",
"@electron/notarize": "2.5.0",
"@electron/rebuild": "3.7.2",
"@fortawesome/fontawesome-free": "5.15.4",
"@joeattardi/emoji-button": "4.6.4",
"@joplin/default-plugins": "~3.4",
"@joplin/editor": "~3.4",
"@joplin/lib": "~3.4",
"@joplin/renderer": "~3.4",
"@joplin/tools": "~3.4",
"@joplin/utils": "~3.4",
"@playwright/test": "1.52.0",
"@joplin/default-plugins": "~3.5",
"@joplin/editor": "~3.5",
"@joplin/lib": "~3.5",
"@joplin/renderer": "~3.5",
"@joplin/tools": "~3.5",
"@joplin/utils": "~3.5",
"@playwright/test": "1.53.2",
"@sentry/electron": "4.24.0",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.5.14",
"@types/mustache": "4.2.6",
"@types/node": "18.19.103",
"@types/node": "18.19.130",
"@types/react": "18.3.23",
"@types/react-dom": "18.3.7",
"@types/react-redux": "7.1.33",
"@types/styled-components": "5.1.32",
"@types/tesseract.js": "2.0.0",
"async-mutex": "0.5.0",
"axios": "^1.7.7",
"codemirror": "5.65.9",
@@ -160,13 +161,13 @@
"compare-versions": "6.1.1",
"countable": "3.0.1",
"debounce": "1.2.1",
"electron": "35.7.5",
"electron": "37.7.0",
"electron-builder": "24.13.3",
"electron-updater": "6.6.2",
"electron-window-state": "5.0.3",
"esbuild": "^0.25.3",
"formatcoords": "1.1.3",
"glob": "11.0.2",
"glob": "11.0.3",
"gulp": "4.0.2",
"highlight.js": "11.11.1",
"immer": "9.0.21",
@@ -187,7 +188,7 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-redux": "8.1.3",
"react-select": "5.10.1",
"react-select": "5.10.2",
"react-test-renderer": "18.3.1",
"react-toggle-button": "2.2.0",
"react-tooltip": "4.5.1",
@@ -199,15 +200,15 @@
"styled-components": "5.3.11",
"styled-system": "5.1.5",
"taboverride": "4.0.3",
"tesseract.js": "5.1.1",
"tesseract.js": "6.0.1",
"tinymce": "6.8.5",
"ts-jest": "29.3.1",
"ts-jest": "29.3.4",
"ts-node": "10.9.2",
"typescript": "5.8.2"
"typescript": "5.8.3"
},
"dependencies": {
"@electron/remote": "2.1.2",
"@joplin/onenote-converter": "~3.4",
"@joplin/onenote-converter": "~3.5",
"fs-extra": "11.2.0",
"keytar": "7.9.0",
"node-fetch": "2.6.7",

View File

@@ -0,0 +1,109 @@
/* eslint-disable no-console */
const { execSync } = require('child_process');
const { chdir, cwd } = require('process');
const { mkdirpSync, moveSync, pathExists } = require('fs-extra');
const { readdirSync, writeFileSync } = require('fs');
const { dirname } = require('path');
const signToolName = 'CodeSignTool.bat';
const getTempDir = () => {
if (process.env.RUNNER_TEMP) return process.env.RUNNER_TEMP;
if (process.env.GITHUB_WORKSPACE) return process.env.GITHUB_WORKSPACE;
const output = `${dirname(dirname(__dirname))}/temp`;
mkdirpSync(output);
return output;
};
const tempDir = getTempDir();
const downloadSignTool = async () => {
const signToolUrl = 'https://www.ssl.com/download/codesigntool-for-windows/';
const downloadDir = `${tempDir}/signToolDownloadTemp`;
const extractDir = `${tempDir}/signToolExtractTemp`;
if (await pathExists(`${extractDir}/${signToolName}`)) {
console.info('sign.js: Sign tool has already been downloaded - skipping');
return extractDir;
}
mkdirpSync(downloadDir);
mkdirpSync(extractDir);
const response = await fetch(signToolUrl);
if (!response.ok) throw new Error(`sign.js: HTTP error ${response.status}: ${response.statusText}`);
const zipPath = `${downloadDir}/codeSignTool.zip`;
const buffer = Buffer.from(await response.arrayBuffer());
writeFileSync(zipPath, buffer);
console.info('sign.js: Downloaded sign tool zip:', readdirSync(downloadDir));
mkdirpSync(extractDir);
execSync(
`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${extractDir}' -Force"`,
{ stdio: 'inherit' },
);
console.info('sign.js: Extracted sign tool zip:', readdirSync(extractDir));
return extractDir;
};
exports.default = async (configuration) => {
const inputFilePath = configuration.path;
const {
SSL_ESIGNER_USER_NAME,
SSL_ESIGNER_USER_PASSWORD,
SSL_ESIGNER_CREDENTIAL_ID,
SSL_ESIGNER_USER_TOTP,
SIGN_APPLICATION,
} = process.env;
console.info('sign.js: File to sign:', inputFilePath);
console.info('sign.js: Using temp dir:', tempDir);
if (SIGN_APPLICATION !== '1') {
console.info('sign.js: SIGN_APPLICATION != 1 - not signing application');
return;
}
console.info('sign.js: SIGN_APPLICATION = 1 - signing application');
const signToolDir = await downloadSignTool();
const signToolOutDir = `${tempDir}/signedToolOutDir`;
mkdirpSync(signToolOutDir);
const previousDir = cwd();
chdir(signToolDir);
try {
const cmd = [
`${signToolName} sign`,
`-input_file_path="${inputFilePath}"`,
`-output_dir_path="${signToolOutDir}"`,
`-credential_id="${SSL_ESIGNER_CREDENTIAL_ID}"`,
`-username="${SSL_ESIGNER_USER_NAME}"`,
`-password="${SSL_ESIGNER_USER_PASSWORD}"`,
`-totp_secret="${SSL_ESIGNER_USER_TOTP}"`,
];
execSync(cmd.join(' '));
const createdFiles = readdirSync(signToolOutDir);
console.info('sign.js: Created files:', createdFiles);
moveSync(`${signToolOutDir}/${createdFiles[0]}`, inputFilePath, { overwrite: true });
} catch (error) {
console.error('sign.js: Could not sign file:', error);
process.exit(1);
} finally {
chdir(previousDir);
}
};

View File

@@ -9,7 +9,7 @@ const baseNodeModules = join(baseDir, 'node_modules');
// Note: Roughly based on js-draw's use of esbuild:
// https://github.com/personalizedrefrigerator/js-draw/blob/6fe6d6821402a08a8d17f15a8f48d95e5d7b084f/packages/build-tool/src/BundledFile.ts#L64
const makeBuildContext = (entryPoint: string, renderer: boolean, computeFileSizeStats: boolean) => {
const makeBuildContext = (entryPoint: string, renderer: boolean, addDebugStats: boolean) => {
return esbuild.context({
entryPoints: [entryPoint],
outfile: `${filename(entryPoint)}.bundle.js`,
@@ -19,7 +19,7 @@ const makeBuildContext = (entryPoint: string, renderer: boolean, computeFileSize
format: 'iife', // Immediately invoked function expression
sourcemap: true,
sourcesContent: false, // Do not embed full source file content in the .map file
metafile: computeFileSizeStats,
metafile: addDebugStats,
platform: 'node',
target: ['node20.0'],
mainFields: renderer ? ['browser', 'main'] : ['main'],
@@ -92,26 +92,29 @@ const makeBuildContext = (entryPoint: string, renderer: boolean, computeFileSize
{
name: 'joplin--smaller-source-map-size',
setup: build => {
// Exclude dependencies from node_modules. This significantly reduces the size of the
// Unless bundling with additional debug information, exclude 3rd-party
// dependencies from source maps. This significantly reduces the size of the
// source map, improving startup performance.
//
// See https://github.com/evanw/esbuild/issues/1685#issuecomment-944916409
// and https://github.com/evanw/esbuild/issues/4130
const emptyMapData = Buffer.from(
JSON.stringify({ version: 3, sources: [null], mappings: 'AAAA' }),
'utf-8',
).toString('base64');
const emptyMapUrl = `data:application/json;base64,${emptyMapData}`;
if (!addDebugStats) {
const emptyMapData = Buffer.from(
JSON.stringify({ version: 3, sources: [null], mappings: 'AAAA' }),
'utf-8',
).toString('base64');
const emptyMapUrl = `data:application/json;base64,${emptyMapData}`;
build.onLoad({ filter: /node_modules.*js$/ }, args => {
return {
contents: [
readFileSync(args.path, 'utf8'),
`//# sourceMappingURL=${emptyMapUrl}`,
].join('\n'),
loader: 'default',
};
});
build.onLoad({ filter: /node_modules.*js$/ }, args => {
return {
contents: [
readFileSync(args.path, 'utf8'),
`//# sourceMappingURL=${emptyMapUrl}`,
].join('\n'),
loader: 'default',
};
});
}
},
},
],

View File

@@ -0,0 +1,55 @@
import { dirname, relative } from 'path';
import * as yargs from 'yargs';
const { wrapCallSite } = require('source-map-support');
/* eslint-disable no-console */
const resolveLine = (lineNumber: number, columnNumber: number, filePath: string) => {
// Note: This is an undocumented function provided by source-map-support. It
// may change in the future:
const frame = wrapCallSite({
getFileName: () => filePath,
isEval: ()=>false,
isNative: ()=>false,
getLineNumber: ()=>lineNumber,
getColumnNumber: ()=>columnNumber,
});
const baseDir = dirname(dirname(dirname(__dirname)));
const relativeFilePath = relative(baseDir, frame.getFileName());
return `${relativeFilePath}:${frame.getLineNumber()}`;
};
const resolvePosition = (position: string, sourceMap: string) => {
const match = /^(\d{1,10}):(\d{1,10})$/.exec(position.trim());
if (!match) {
throw new Error('Invalid format. Expected line:col');
}
const lineNumber = Number(match[1]);
const columnNumber = Number(match[2]);
return resolveLine(lineNumber, columnNumber, sourceMap);
};
void yargs
.usage('$0 [args]')
.command(
'$0 <position>',
'Resolves a position based on a source map. If resolving a position in a specific error message, be sure to use the source map generated by "yarn bundle" from that specific commit.',
(yargs) => {
return yargs.options({
'position': { type: 'string', help: 'A line:col position (e.g. 123:4567)' },
'sourcemap': {
type: 'string',
default: './main-html.bundle.js',
help: 'The path to the source map. This source map should be a source map compiled from the commit/release that created the error.',
},
});
},
async (args) => {
console.log(await resolvePosition(args.position, args.sourcemap));
process.exit(0);
},
)
.help()
.argv;

View File

@@ -90,7 +90,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097780
versionName "3.4.7"
versionName "3.5.0"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
@@ -100,6 +100,8 @@ android {
externalNativeBuild {
cmake {
cppFlags '-DCMAKE_BUILD_TYPE=Release'
// For 16 KB pages. This should be removable after upgrading to NDK r28
arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
}
}
}

View File

@@ -38,8 +38,9 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
set(WHISPER_LIB_DIR ${CMAKE_SOURCE_DIR}/../../../../vendor/whisper.cpp)
# Based on the Whisper.cpp Android example:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections")
set(SHARED_FLAGS "-O3 ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SHARED_FLAGS} ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SHARED_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections")
# Whisper: See https://stackoverflow.com/a/76290722
add_subdirectory(${WHISPER_LIB_DIR} ./whisper)

View File

@@ -24,29 +24,8 @@ buildscript {
allprojects {
repositories {
mavenCentral()
// Seems to be required for react-native-vosk, otherwise the lib looks for it at "https://maven.aliyun.com/repository/jcenter/com/alphacephei/vosk-android/0.3.46/vosk-android-0.3.46.aar" but it's not there. And we get this error:
//
// Execution failed for task ':app:checkDebugAarMetadata'.
// > Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
// > Failed to transform vosk-android-0.3.46.aar (com.alphacephei:vosk-android:0.3.46) to match attributes {artifactType=android-aar-metadata, org.gradle.status=release}.
// > Could not find vosk-android-0.3.46.aar (com.alphacephei:vosk-android:0.3.46).
// Searched in the following locations:
// https://maven.aliyun.com/repository/jcenter/com/alphacephei/vosk-android/0.3.46/vosk-android-0.3.46.aar
//
// But according to this page, the lib is on the Apache repository:
//
// https://search.maven.org/artifact/com.alphacephei/vosk-android/0.3.46/aar
maven { url "https://maven.apache.org" }
// Also required for react-native-vosk?
maven { url "https://maven.google.com" }
// Maybe still needed to fetch above package?
google()
maven { url 'https://www.jitpack.io' }
mavenCentral()
maven {
// expo-camera bundles a custom com.google.android:cameraview

View File

@@ -4,7 +4,7 @@ import { Platform, ScrollView, StyleSheet, View } from 'react-native';
import { BarcodeScanner } from './utils/useBarcodeScanner';
import { LinkButton, PrimaryButton } from '../buttons';
import { _ } from '@joplin/lib/locale';
import DismissibleDialog, { DialogSize } from '../DismissibleDialog';
import DismissibleDialog, { DialogVariant } from '../DismissibleDialog';
import { Chip, Text } from 'react-native-paper';
import { isCallbackUrl, parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import CommandService from '@joplin/lib/services/CommandService';
@@ -84,7 +84,7 @@ const ScannedBarcodes: React.FC<Props> = props => {
visible={dialogVisible}
onDismiss={onHideDialog}
themeId={props.themeId}
size={DialogSize.Small}
size={DialogVariant.Small}
>
<ScrollView>
<Text variant='titleMedium' role='heading'>{_('Scanned code')}</Text>

View File

@@ -66,12 +66,12 @@ describe('ComboBox', () => {
unmount();
});
test('changing the search query should limit which items are visible', () => {
test('changing the search query should limit which items are visible and be case insensitive', () => {
const testItems = [
{ title: 'a' },
{ title: 'b' },
{ title: 'c' },
{ title: 'aa' },
{ title: 'Aa' },
];
const { unmount } = render(
<WrappedComboBox items={testItems}/>,
@@ -82,7 +82,7 @@ describe('ComboBox', () => {
const updatedResults = getSearchResults();
expect(updatedResults[0]).toHaveTextContent('a');
expect(updatedResults[1]).toHaveTextContent('aa');
expect(updatedResults[1]).toHaveTextContent('Aa');
expect(updatedResults).toHaveLength(2);
unmount();

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