1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

218 Commits

Author SHA1 Message Date
Laurent Cozic
051d628250 tsconfig 2023-06-13 11:10:07 +01:00
Laurent Cozic
d8f365834b update 2023-06-13 11:01:45 +01:00
Laurent Cozic
6dc6acc644 update 2023-06-13 10:54:44 +01:00
Laurent Cozic
30e1164df9 Merge branch 'dev' into user_data 2023-06-12 17:31:20 +01:00
Laurent Cozic
3c0331eb6e Desktop: Fixes #8210: Display plugin console in dev mode 2023-06-12 17:30:47 +01:00
Laurent Cozic
a8821dfcb5 test 2023-06-12 17:29:57 +01:00
Laurent Cozic
54b15a1917 Plugins: Updated types 2023-06-12 17:09:09 +01:00
Laurent Cozic
e2d27275db Plugins: Updated types 2023-06-12 17:04:46 +01:00
Laurent Cozic
b90cb89e0f fix 2023-06-12 17:02:49 +01:00
Laurent Cozic
a6516c5f14 Plugins: Updated types 2023-06-12 16:52:09 +01:00
Laurent Cozic
249a744e9d init 2023-06-12 16:49:54 +01:00
Laurent Cozic
1eeb5ab471 Mobile: Improved Vosk error handling 2023-06-11 16:13:36 +01:00
Joplin Bot
c77a0b4709 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-11 12:17:44 +00:00
Laurent Cozic
1b64688042 Doc: Update French translation 2023-06-11 12:20:13 +01:00
Joplin Bot
c082505f61 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-11 00:46:39 +00:00
Laurent Cozic
f555e528ba All: Resolves #7686: Upgrade E2EE encryption method to AES-256 2023-06-10 19:20:08 +01:00
Joplin Bot
96ff76fd1d Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-10 18:12:20 +00:00
Laurent Cozic
9ceb7b9c88 Mobile: Upgrade react-native-webview to v12 2023-06-10 17:58:55 +01:00
Marph
6dc8ad2ba6 Desktop: Resolves #3535: Configure Rich Text editor to handle the first table row as header (#8163) 2023-06-10 17:08:15 +01:00
renovate[bot]
152e354d87 Update dependency highlight.js to v11.8.0 (#8296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-10 17:07:35 +01:00
Laurent Cozic
dbef391855 Disable RN auto-updates 2023-06-10 17:07:07 +01:00
Laurent Cozic
78f3f1c01a Mobile: Fixed text update issue when attaching a file to an empty note 2023-06-10 17:04:45 +01:00
Laurent Cozic
e7409145b6 Mobile: Upgrade to React Native 0.71 2023-06-10 17:03:34 +01:00
Calum Lind
bf8e34a0be Linux: Fixes #8297: Fix corrupted sidebar (#8298) 2023-06-10 14:03:26 +01:00
renovate[bot]
4aaea3fe71 Update dependency @react-native-community/datetimepicker to v7.0.1 (#8295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 18:15:23 +00:00
Laurent Cozic
e5b4332f17 Android 2.11.26 2023-06-08 17:18:25 +01:00
Laurent Cozic
72a0adb434 Android: Fixes #8229: Fix alarms for latest Android versions 2023-06-08 16:43:06 +01:00
Laurent Cozic
37b745c69e Mobile: Fixes #8285: Fix sharing data with the app 2023-06-08 16:23:48 +01:00
Laurent Cozic
3338164f43 lock file 2023-06-08 16:02:42 +01:00
Laurent Cozic
98440beffa All: Fixes #8286: Allow certain HTML anchor tags 2023-06-08 15:18:46 +01:00
Laurent Cozic
0c6f779aab Desktop: Fixes #8287: Duplicated published notes retain the published note's label's color 2023-06-08 15:09:10 +01:00
Laurent Cozic
fb27ae991c Linux: Resolves #8289: Ask user to restart app after switching profile 2023-06-08 14:46:37 +01:00
Laurent Cozic
a41c9b6819 Renovate exclude 2023-06-08 14:46:36 +01:00
renovate[bot]
625812f7e6 Update dependency @react-native-community/datetimepicker to v7 (#8281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 14:37:32 +01:00
renovate[bot]
e8cee2907c Update dependency domutils to v3.1.0 (#8293)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 13:50:57 +01:00
renovate[bot]
da25d10900 Update dependency style-loader to v3.3.3 (#8292)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 07:45:01 +00:00
renovate[bot]
9f01258341 Update dependency css-loader to v6.7.4 (#8291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-08 05:23:18 +00:00
Joplin Bot
8608032243 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-06 18:17:20 +00:00
Laurent Cozic
bdbb362644 Tools: Fixed git-changelog 2023-06-06 16:43:46 +01:00
Laurent Cozic
30671e7a20 Desktop release v2.11.9 2023-06-06 16:34:13 +01:00
Laurent Cozic
8b578c5dde Desktop: Trying to fix white screen issue 2023-06-06 16:31:31 +01:00
Laurent Cozic
3792a5fb94 lock file 2023-06-06 16:31:12 +01:00
Laurent Cozic
fb28268582 fix 2023-06-06 12:27:54 +01:00
Laurent Cozic
be3c15c37c Doc: Add link to profile 2023-06-06 12:04:34 +01:00
Laurent Cozic
b34342f01e Tools: Improve git-changelog tag detection 2023-06-06 12:04:06 +01:00
renovate[bot]
19992ffcf9 Update buildTools (major) (#8266)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2023-06-06 10:55:02 +01:00
renovate[bot]
6c15940005 Update dependency tar to v6.1.15 (#8280)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-06 09:39:30 +00:00
Joplin Bot
7f0f745af3 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-06 00:43:31 +00:00
renovate[bot]
1d62003486 Update dependency react-native-safe-area-context to v4.5.3 (#8279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-06 00:23:56 +00:00
renovate[bot]
fd373baeae Update dependency react-native-document-picker to v8.2.1 (#8278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-05 23:26:55 +00:00
Laurent Cozic
f880e12a26 Desktop release v2.11.8 2023-06-05 20:05:56 +01:00
Laurent Cozic
e81332e6b9 Desktop: Prevent double-initialisation of plugins 2023-06-05 20:05:00 +01:00
Laurent Cozic
bda8766564 Desktop release v2.11.7 2023-06-05 18:46:41 +01:00
Laurent Cozic
6d7b856d1d Desktop: Resolves #8083: Improved word count when em-dash is used 2023-06-05 18:35:34 +01:00
Laurent Cozic
90a8d704c1 Desktop: Resolves #8175: Add support for AVIF image format 2023-06-05 18:23:41 +01:00
Rio Sinnott
8dfcec249f Desktop: Fixes #8159: "New note" buttons so large they occlude Search (#8249) 2023-06-05 18:22:31 +01:00
Laurent Cozic
659f602a12 Revert "Desktop: Fixes #4124: Fix note list blank space display problems (#7888)"
This reverts commit 5c6e17bc89.

Appears to cause this regression (based on stacktrace):

https://github.com/laurent22/joplin/issues/8237
2023-06-05 17:56:12 +01:00
Laurent Cozic
45877f8b26 Desktop: Fixes #7484: Window is white on startup 2023-06-05 17:49:34 +01:00
Laurent Cozic
77505e3c75 Revert "Android: Fixes #7766: Support monochrome icons (#7772)"
This reverts commit fd578d1c36.

Ref: https://github.com/laurent22/joplin/issues/8273
2023-06-05 17:15:24 +01:00
renovate[bot]
dde7acc70f Update buildTools (#8264)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2023-06-04 18:34:32 +01:00
Laurent Cozic
531402cede Android 2.11.25 2023-06-03 17:47:00 +01:00
Laurent Cozic
56ca9d4125 Revert "Mobile: Fixes #8017: Fixed sync crash (#8056)"
This reverts commit b824ff5457.

Fixes error "Download interrupted" when downloading resources from
Joplin Cloud/Server.
2023-06-03 16:45:15 +01:00
Laurent Cozic
60b3921808 Android: Fix Vosk logic 2023-06-03 15:43:40 +01:00
Laurent Cozic
3a1759aabc Android 2.11.24 2023-06-02 16:37:34 +01:00
Laurent Cozic
ac732f4d1d Tools: Fixed Vosk release script 2023-06-02 15:47:42 +01:00
Laurent Cozic
7779879c6d Mobile: Write to note in realtime using voice typing 2023-06-02 15:46:04 +01:00
github-actions[bot]
c6a9837f1f @wimmers has signed the CLA in laurent22/joplin#8269 2023-06-02 14:27:03 +00:00
Laurent Cozic
6e49440b95 Android 2.11.23 2023-06-01 18:19:48 +01:00
Laurent Cozic
12746c5ff9 Revert "Mobile: Fixes #7918: Use react-native-drawer-layout instead of react-native-side-menu-updated (#7953)"
This reverts commit 40e0037d50.

It doesn't build on Android:

> Task :react-native-reanimated:buildCMakeRelWithDebInfo[arm64-v8a] FAILED
C/C++: ninja: error: '../../../../build/third-party-ndk/hermes/jni/arm64-v8a/libhermes.so',
needed by '../../../../build-main/intermediates/cxx/RelWithDebInfo/6w4f694p/obj/arm64-v8a/libreanimated.so',
missing and no known rule to make it
2023-06-01 17:35:27 +01:00
Laurent Cozic
7bd4a999a0 Update translations 2023-06-01 16:56:42 +01:00
Laurent Cozic
e48d55c3e5 Mobile: Auto-detect language on start 2023-06-01 16:48:02 +01:00
Laurent Cozic
54efc9f2f5 Chore: Revert AWS package upgrade due to failing mobile build 2023-06-01 16:13:08 +01:00
Laurent Cozic
963eeccf7b Fixed buid 2023-06-01 15:25:40 +01:00
Laurent Cozic
c89edd7b22 Tools: Implement "prefer-object-spread" eslint rule 2023-06-01 12:02:36 +01:00
Laurent Cozic
804d674d37 lock file 2023-06-01 11:26:02 +01:00
Laurent Cozic
514091eb41 Tools: Install rsync on CI 2023-06-01 09:44:27 +01:00
renovate[bot]
cd506bd3f1 Update aws (#8265)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-01 05:03:41 +00:00
Joplin Bot
e2d47d7d0d Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-01 00:54:11 +00:00
Laurent Cozic
7a9afd4aff Desktop release v2.11.6 2023-05-31 20:33:57 +01:00
Laurent Cozic
e647775608 All: When resetting the master password, also create a new master key with that password 2023-05-31 20:31:44 +01:00
Laurent Cozic
577aa519e0 All: Fixes #8254: Improve selection of active E2EE key 2023-05-31 20:17:22 +01:00
renovate[bot]
03cfef6a8d Update dependency react-native-reanimated to v3.1.0 (#8255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-31 19:39:01 +01:00
github-actions[bot]
1219a30dff @cwilby has signed the CLA in laurent22/joplin#8259 2023-05-31 02:07:47 +00:00
Linkosred
801719e3ba Doc: Update faq_joplin_cloud.md (#8250)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2023-05-29 14:04:05 +01:00
Laurent Cozic
02098dbd79 iOS 12.11.3 2023-05-29 13:28:04 +01:00
Laurent Cozic
e149e4b5fd Revert "Mobile: Fix "Download interrupted" error"
This reverts commit b023f58497.

Due to:

https://discourse.joplinapp.org/t/the-latest-ios-version-of-joplin-12-11-2-works-weird/31045
2023-05-29 13:27:10 +01:00
Laurent Cozic
b19f1a1023 Mobile: Fixed regression in biometric check
Due to f0ade02435
2023-05-29 13:27:09 +01:00
Laurent Cozic
192bfb5555 iOS: Fixed broken domain detection 2023-05-29 13:27:07 +01:00
Andrey Mukamolov
fd578d1c36 Android: Fixes #7766: Support monochrome icons (#7772) 2023-05-29 11:36:47 +01:00
jcgurango
230e7f6914 Mobile: Resolves #8193: Implement parenting of notebooks (#7980) 2023-05-29 11:31:21 +01:00
GitStart
12bba9da29 Desktop: Fixes #7933: Don't display "obsolete encryption method" message if the key is disabled (#8025)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2023-05-29 11:27:53 +01:00
Calum Lind
03424f76ea Desktop: Fixes #7506: Improve sidebar workaround for Linux w/Intel GPU (#8126) 2023-05-29 11:20:11 +01:00
Christopher O'Toole
745849023d Desktop: Fixes #6431: Preserve Table Alignment When Editing a Note With the Rich Text Editor (#8214) 2023-05-29 11:10:17 +01:00
github-actions[bot]
3e3b1764b7 @rio-codes has signed the CLA in laurent22/joplin#8249 2023-05-28 03:55:26 +00:00
Joplin Bot
902f0c3bf7 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-28 00:43:11 +00:00
Joplin Bot
0f51249a76 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-27 18:14:29 +00:00
Laurent Cozic
3621e252d1 Desktop release v2.11.5 2023-05-27 17:24:51 +01:00
Laurent Cozic
5669b1f04f iOS 12.11.2 2023-05-27 16:33:33 +01:00
Laurent Cozic
91c99960ba Chore: iOS: Fix build 2023-05-27 16:30:25 +01:00
Laurent Cozic
b309504ffb lock file 2023-05-27 14:46:21 +01:00
Laurent Cozic
22ab4b7473 iOS 12.11.1 2023-05-27 14:38:02 +01:00
Laurent Cozic
04ea9343b0 Doc: Mention Joplin Cloud in download page 2023-05-27 14:28:02 +01:00
Laurent Cozic
b93f91078b Doc: Added Liberapay method 2023-05-27 12:43:46 +01:00
renovate[bot]
b82e4be5c5 Update dependency @react-native-community/netinfo to v9.3.10 (#8239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-25 23:25:58 +00:00
Joplin Bot
77a6e6f617 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-25 18:16:45 +00:00
Laurent Cozic
959ae59af0 Doc: Add sponsor 2023-05-25 19:00:18 +01:00
Joplin Bot
98dd926cb9 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-25 12:20:03 +00:00
Joplin Bot
8197c7aa7a Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-25 06:18:05 +00:00
renovate[bot]
1687adf015 Update dependency react-select to v5.7.3 (#8230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-23 11:39:08 +00:00
renovate[bot]
7542ca907c Update dependency tar to v6.1.14 (#8228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-23 02:19:51 +00:00
renovate[bot]
1652b06e8c Update dependency jsdom to v21.1.2 (#8223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-21 18:13:20 +00:00
Milo Ivir
1a835ef094 All: Translation: Update hr_HR.po (#8219) 2023-05-20 18:27:29 -04:00
Joplin Bot
575f55b22c Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-20 18:15:16 +00:00
Joplin Bot
d105387ece Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-20 06:16:16 +00:00
renovate[bot]
587eceb7c0 Update dependency react-native-safe-area-context to v4.5.2 (#8218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-19 19:51:19 +00:00
Laurent Cozic
c90d7756c0 Renovate - exclude xml2js 2023-05-19 20:50:37 +01:00
Laurent Cozic
9e90d9016d All: Security: Prevent XSS by sanitizing certain HTML attributes 2023-05-19 11:00:31 +01:00
Laurent Cozic
ccec93eaa3 fix tests 2023-05-19 10:44:49 +01:00
github-actions[bot]
7bf823e0df @christopher-o-toole has signed the CLA in laurent22/joplin#8213 2023-05-18 23:44:24 +00:00
Daniel Junho
b533d8d164 Linux: Fix the ldconfig dependency (#8205)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
Co-authored-by: Helmut K. C. Tessarek <tessarek@evermeet.cx>
2023-05-18 12:46:58 -04:00
ScriptInfra
97477bb751 Doc: Update README.md (#8197)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
Co-authored-by: Helmut K. C. Tessarek <tessarek@evermeet.cx>
2023-05-18 00:49:06 -04:00
renovate[bot]
9cf863979b Update dependency yargs to v17.7.2 (#8207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-18 00:01:12 +00:00
Laurent Cozic
9ffe990c0b Fixed tests 2023-05-17 19:59:22 +01:00
Joplin Bot
2c57e949b9 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-17 18:16:02 +00:00
Laurent Cozic
a7e185eb11 Server: Fixed copyright year 2023-05-17 18:23:38 +01:00
Laurent Cozic
091eff9bc2 Server: Allow giving a different version number to forks 2023-05-17 18:16:29 +01:00
Laurent Cozic
a04be2b28a Tools: Ignore unsupport tags 2023-05-17 17:38:37 +01:00
Laurent Cozic
caf66068bf Desktop, Mobile: Security: Disable SVG tag support in editor to prevent XSS 2023-05-17 16:00:24 +01:00
Laurent Cozic
8deba24d7d Chore: Improve TS types 2023-05-17 15:46:35 +01:00
Laurent Cozic
84b130e0cb Server: Process orphaned items 2023-05-17 15:11:55 +01:00
github-actions[bot]
e6209f449e @sandrotanner has signed the CLA in laurent22/joplin#8204 2023-05-17 13:32:16 +00:00
Joplin Bot
faa4c5b9fc Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-17 12:20:02 +00:00
Laurent Cozic
df9bfc7635 Doc: Describe how to escape Markdown in coding style 2023-05-17 10:04:31 +01:00
renovate[bot]
a46648f1ee Update dependency sharp to v0.32.1 (#8128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-17 09:36:45 +01:00
github-actions[bot]
215434bb4f @ScriptInfra has signed the CLA in laurent22/joplin#8197 2023-05-16 21:29:02 +00:00
jcgurango
40e0037d50 Mobile: Fixes #7918: Use react-native-drawer-layout instead of react-native-side-menu-updated (#7953) 2023-05-16 20:51:51 +01:00
renovate[bot]
6afd839ae8 Update dependency lint-staged to v13.2.2 (#8196)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 18:02:46 +00:00
renovate[bot]
63595d2469 Update dependency @react-native-community/push-notification-ios to v1.11.0 (#8195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 15:18:08 +01:00
Joplin Bot
d715e1ba6b Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-16 12:18:50 +00:00
Daniel Junho
fb8a0c9ea9 Linux: Check libfuse2 dependency in install script (#8079) 2023-05-16 11:57:50 +01:00
github-actions[bot]
ad20eba65e @laurent22 has signed the CLA in laurent22/joplin#8189 2023-05-16 10:56:45 +00:00
renovate[bot]
ea73e20115 Update dependency react-native-paper to v5.8.0 (#8183)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 11:43:30 +01:00
renovate[bot]
1b8e9271a6 Update dependency markdown-it-multimd-table to v4.2.2 (#8180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 11:43:21 +01:00
renovate[bot]
3a8a7a592d Update dependency nodemailer to v6.9.2 (#8164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 11:43:06 +01:00
renovate[bot]
9d5c63de4f Update dependency sass to v1.62.1 (#8119)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 11:42:35 +01:00
Laurent Cozic
e37c95b9e5 Tools: Set pruneBranchAfterAutomerge for Renovate 2023-05-16 11:40:18 +01:00
Laurent Cozic
201c7d893e Desktop release v2.11.4 2023-05-16 10:26:01 +01:00
Laurent Cozic
c12108d1ac Desktop: Fixes #8087: Fix slow startup time 2023-05-16 10:25:45 +01:00
Laurent Cozic
6ffd1046bd Desktop release v2.11.3 2023-05-16 09:32:38 +01:00
Laurent Cozic
d4f49d6a02 CI: Fix Android build 2023-05-15 19:58:59 +01:00
Laurent Cozic
cff24af099 Server v2.11.1 2023-05-15 18:16:42 +01:00
Laurent Cozic
b99ba85acd Fixed tests 2023-05-15 18:14:26 +01:00
Laurent Cozic
a0b707cbda Desktop, Server: Improved handling of items with duplicate IDs 2023-05-15 17:49:26 +01:00
Joplin Bot
1d7ffe358e Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-14 18:15:33 +00:00
Laurent Cozic
9160f0e2a2 Android 2.11.22 2023-05-14 14:49:59 +01:00
Laurent Cozic
92272533e5 Merge branch 'dev' of github.com:laurent22/joplin into dev 2023-05-14 14:34:27 +01:00
Laurent Cozic
648e091523 Chore: Fix Android release script 2023-05-14 14:33:13 +01:00
Laurent Cozic
b023f58497 Mobile: Fix "Download interrupted" error 2023-05-14 14:31:55 +01:00
Laurent Cozic
c639791d4f Include yarn patches 2023-05-14 14:24:58 +01:00
Joplin Bot
6d52288e28 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-14 12:19:54 +00:00
Laurent Cozic
3e2f4b163b Android 2.11.21 2023-05-14 12:20:01 +01:00
renovate[bot]
e0dbd198d8 Update dependency react-native-paper to v5.6.0 (#8181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-13 18:40:18 +01:00
Laurent Cozic
a76c5c8746 Tools: Allow publishing varients 2023-05-13 18:25:29 +01:00
Laurent Cozic
20b43ce56e fix 2023-05-13 16:31:24 +01:00
Laurent Cozic
8215ce14c6 Tools: Allow different Android variants 2023-05-13 15:54:17 +01:00
renovate[bot]
3fead0a8a7 Update dependency styled-components to v5.3.10 (#8178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-13 05:50:55 +00:00
Jonatan
c8bf3e8583 All: Translation: Update sv.po (#8169) 2023-05-12 13:57:39 -04:00
Dmitriy Q
42dee6c275 All: Translation: Update ru_RU.po (#8161) 2023-05-12 13:56:10 -04:00
Laurent Cozic
6f3f866f78 Android 2.11.16 2023-05-12 13:44:15 +01:00
Laurent Cozic
adf2e7322d fix 2023-05-12 13:18:29 +01:00
Laurent Cozic
0df170926a Tools: Restore content after patching Android app 2023-05-12 13:18:28 +01:00
Laurent Cozic
20a26732a9 Tools: Restore content after patching Android app 2023-05-12 13:18:27 +01:00
Laurent Cozic
0da3e91a29 Tools: Exclude Vosk model files when building regular app 2023-05-12 13:17:01 +01:00
renovate[bot]
9ad56dc373 Update dependency gettext-extractor to v3.7.2 (#8167)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-12 05:32:43 +00:00
Arda Kılıçdağı
29dab26dce All: Translation: Update tr_TR.po (#8162)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2023-05-11 18:22:36 -04:00
github-actions[bot]
10e8fbcdab @Ardakilic has signed the CLA in laurent22/joplin#8162 2023-05-11 19:11:11 +00:00
Laurent Cozic
6c4f566765 Doc: Fix download links 2023-05-11 19:12:39 +01:00
Joplin Bot
78df302e86 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-11 17:55:45 +00:00
Laurent Cozic
f52dd4f098 Doc: Update download links 2023-05-11 18:44:50 +01:00
github-actions[bot]
dbab786c7b @marph91 has signed the CLA in laurent22/joplin#8163 2023-05-11 14:17:59 +00:00
Laurent Cozic
3eb44d27b2 Mobile: Sync as soon as the app starts, and immediately after changing a note 2023-05-11 14:58:19 +01:00
Laurent Cozic
52bea115ac Tools: Allow uploading Android version to different repo 2023-05-11 14:48:33 +01:00
Laurent Cozic
19bdda25c6 Desktop: Security: Prevent XSS and potential RCE when using a special HTML tag 2023-05-11 14:17:37 +01:00
Laurent Cozic
b26bc9ed5f Desktop: Security: Fixed possible XSS injection 2023-05-10 16:27:16 +01:00
Laurent Cozic
865cedc24f Android 2.11.14 2023-05-10 13:28:43 +01:00
Laurent Cozic
33f0811ad2 Server: Resolves #8153: Allow setting NTP server using NTP_SERVER env variable 2023-05-10 12:50:48 +01:00
Laurent Cozic
8cedf27fea Desktop, Mobile: Resolves #8154: Translate Welcome notes 2023-05-10 12:20:04 +01:00
Laurent Cozic
052a829167 Desktop: Auto-detect locale on startup 2023-05-10 12:20:03 +01:00
Laurent Cozic
5371c97ccd Chore: Refactor build-welcome script 2023-05-10 12:20:02 +01:00
Joplin Bot
c53b957293 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-09 18:17:54 +00:00
Laurent Cozic
e6f8dc96df Fixed CI 2023-05-09 17:21:40 +01:00
Laurent Cozic
21648b1b1b Desktop: Fixes #8149: Application cannot be installed on Windows 10 in some cases 2023-05-09 12:56:23 +01:00
Laurent Cozic
83db6f6596 Desktop release v2.11.2 2023-05-09 11:13:38 +01:00
Laurent Cozic
3adfa574c0 Desktop: Fixes #8149: Application cannot be installed on Windows 10 in some cases 2023-05-09 11:13:23 +01:00
Laurent Cozic
4d0ffc5beb clean up 2023-05-09 11:12:53 +01:00
Laurent Cozic
69f9b160dd Chore: Cleanup Android release script 2023-05-09 11:01:43 +01:00
Laurent Cozic
c17b02cfb5 Android 2.11.13 2023-05-09 08:49:00 +01:00
Laurent Cozic
6dd57b63a6 Chore: Enable Hermes again on Android to try to fix crash 2023-05-08 21:03:11 +01:00
Laurent Cozic
248c8014c8 Android 2.11.12 2023-05-08 21:02:28 +01:00
Joplin Bot
5fe2766a6b Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-08 18:16:06 +00:00
Laurent Cozic
000e0ad517 Chore: Android: Remove non longer needed joplin/react-native-vosk package 2023-05-08 18:54:23 +01:00
Laurent Cozic
c047375143 Chore: Android: Trying to fix random crash 2023-05-08 18:44:52 +01:00
Laurent Cozic
bd9e62cbd2 Android 2.11.11 2023-05-08 18:27:18 +01:00
Laurent Cozic
5ecae17538 Mobile: Tells whether Hermes engine is enabled or not 2023-05-08 17:50:19 +01:00
Laurent Cozic
35037e2dc9 fix tests 2023-05-08 17:30:30 +01:00
Laurent Cozic
059202be09 Desktop: Fixes #8072: Enter Key No Longer Saves and Closes The Tag Dialog 2023-05-08 17:07:55 +01:00
Laurent Cozic
6672f63981 Desktop: Fixes #8143: Fixes crash when using multiple profiles along with certain plugins 2023-05-08 16:45:18 +01:00
Laurent Cozic
f390eca4de Desktop: Fixes #8143: Fixes crash when using multiple profiles along with certain plugins 2023-05-08 15:34:38 +01:00
Joplin Bot
edc5e33559 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-08 13:12:39 +00:00
Laurent Cozic
a4b81edb5a Doc: Fix news page 2023-05-08 13:12:57 +01:00
Joplin Bot
fc5f338e16 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-08 11:38:41 +00:00
Laurent Cozic
767e380144 Doc: Release notes for 2.10 2023-05-08 12:24:55 +01:00
Laurent Cozic
60aff6ae77 Android 2.11.10 2023-05-08 12:09:28 +01:00
Laurent Cozic
345d9da7f2 Update translations 2023-05-08 10:56:16 +01:00
Laurent Cozic
98d45b1b81 Chore: Make text translatable 2023-05-08 10:38:52 +01:00
Joplin Bot
2cbf12bf0b Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-05-08 06:17:54 +00:00
Laurent Cozic
968529f7e8 Android 2.11.9 2023-05-07 20:04:55 +01:00
Laurent Cozic
b1b7c3903f Update renovate.json5 2023-05-07 18:58:56 +01:00
466 changed files with 43296 additions and 33340 deletions

View File

@@ -75,6 +75,7 @@ packages/pdf-viewer/dist
plugin_types/
readme/
packages/react-native-vosk/lib/
packages/lib/countable/Countable.js
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-cli/app/LinkSelector.js
@@ -363,6 +364,7 @@ packages/app-mobile/components/CameraView.js
packages/app-mobile/components/CustomButton.js
packages/app-mobile/components/Dropdown.js
packages/app-mobile/components/ExtendedWebView.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnMessage.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
@@ -425,8 +427,8 @@ packages/app-mobile/services/AlarmServiceDriver.android.js
packages/app-mobile/services/AlarmServiceDriver.ios.js
packages/app-mobile/services/e2ee/RSA.react-native.js
packages/app-mobile/services/profiles/index.js
packages/app-mobile/services/voiceTyping/vosk.dummy.js
packages/app-mobile/services/voiceTyping/vosk.js
packages/app-mobile/services/voiceTyping/vosk.android.js
packages/app-mobile/services/voiceTyping/vosk.ios.js
packages/app-mobile/setupQuickActions.js
packages/app-mobile/tools/buildInjectedJs.js
packages/app-mobile/utils/ShareExtension.js
@@ -483,6 +485,7 @@ packages/lib/SyncTargetOneDrive.js
packages/lib/SyncTargetRegistry.js
packages/lib/Synchronizer.js
packages/lib/TaskQueue.js
packages/lib/WelcomeUtils.js
packages/lib/array.js
packages/lib/callbackUrlUtils.js
packages/lib/callbackUrlUtils.test.js
@@ -518,6 +521,7 @@ packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.test.js
packages/lib/import-enex.js
packages/lib/locale.js
packages/lib/locale.test.js
packages/lib/markdownUtils.js
packages/lib/markdownUtils.test.js
packages/lib/markdownUtils2.test.js
@@ -553,6 +557,8 @@ packages/lib/models/utils/itemCanBeEncrypted.js
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/types.js
packages/lib/models/utils/userData.js
packages/lib/models/utils/userData.test.js
packages/lib/net-utils.js
packages/lib/ntp.js
packages/lib/onedrive-api.js
@@ -811,7 +817,6 @@ packages/plugins/ToggleSidebars/api/index.js
packages/plugins/ToggleSidebars/api/types.js
packages/plugins/ToggleSidebars/src/index.js
packages/react-native-saf-x/src/index.js
packages/react-native-vosk/src/index.js
packages/renderer/HtmlToHtml.js
packages/renderer/InMemoryCache.js
packages/renderer/MarkupToHtml.js
@@ -845,6 +850,7 @@ packages/renderer/noteStyle.js
packages/renderer/pathUtils.js
packages/renderer/utils.js
packages/tools/build-release-stats.js
packages/tools/build-welcome.js
packages/tools/buildServerDocker.js
packages/tools/buildServerDocker.test.js
packages/tools/bundleDefaultPlugins.js

View File

@@ -141,6 +141,7 @@ module.exports = {
'spaced-comment': ['error', 'always'],
'keyword-spacing': ['error', { 'before': true, 'after': true }],
'no-multi-spaces': ['error'],
'prefer-object-spread': ['error'],
// Regarding the keyword blacklist:
// - err: We generally avoid using too many abbreviations, so it should

View File

@@ -183,12 +183,32 @@ if [[ $GIT_TAG_NAME = v* ]]; then
# cd "$ROOT_DIR/packages/tools"
# node bundleDefaultPlugins.js
cd "$ROOT_DIR/packages/app-desktop"
USE_HARD_LINKS=false yarn run dist
if [ "$IS_MACOS" == "1" ]; then
# This is to fix this error:
#
# Exit code: ENOENT. spawn /usr/bin/python ENOENT
#
# Ref: https://github.com/electron-userland/electron-builder/issues/6767#issuecomment-1096589528
#
# It can be removed once we upgrade to electron-builder@23, however we
# cannot currently do this due to this error:
# https://github.com/laurent22/joplin/issues/8149
PYTHON_PATH=$(which python) USE_HARD_LINKS=false yarn run dist
else
USE_HARD_LINKS=false yarn run dist
fi
elif [[ $IS_LINUX = 1 ]] && [[ $GIT_TAG_NAME = $SERVER_TAG_PREFIX-* ]]; then
echo "Step: Building Docker Image..."
cd "$ROOT_DIR"
yarn run buildServerDocker --tag-name $GIT_TAG_NAME --push-images --repository $SERVER_REPOSITORY
else
echo "Step: Building but *not* publishing desktop application..."
USE_HARD_LINKS=false yarn run dist --publish=never
if [ "$IS_MACOS" == "1" ]; then
# See above why we need to specify Python
PYTHON_PATH=$(which python) USE_HARD_LINKS=false yarn run dist --publish=never
else
USE_HARD_LINKS=false yarn run dist --publish=never
fi
fi

View File

@@ -17,9 +17,8 @@ jobs:
concurrent_skipping: 'same_content_newer'
BuildAndroidDebug:
if: github.repository == 'laurent22/joplin'
needs: pre_job
if: needs.pre_job.outputs.should_skip != 'true'
if: github.repository == 'laurent22/joplin' && needs.pre_job.outputs.should_skip != 'true'
runs-on: ubuntu-latest
steps:
- name: Install Linux dependencies

View File

@@ -50,6 +50,7 @@ jobs:
sudo apt-get install -y gettext
sudo apt-get install -y libsecret-1-dev
sudo apt-get install -y translate-toolkit
sudo apt-get install -y rsync
- name: Install Docker Engine
# if: runner.os == 'Linux' && startsWith(github.ref, 'refs/tags/server-v')

11
.gitignore vendored
View File

@@ -349,6 +349,7 @@ packages/app-mobile/components/CameraView.js
packages/app-mobile/components/CustomButton.js
packages/app-mobile/components/Dropdown.js
packages/app-mobile/components/ExtendedWebView.js
packages/app-mobile/components/FolderPicker.js
packages/app-mobile/components/NoteBodyViewer/NoteBodyViewer.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnMessage.js
packages/app-mobile/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
@@ -411,8 +412,8 @@ packages/app-mobile/services/AlarmServiceDriver.android.js
packages/app-mobile/services/AlarmServiceDriver.ios.js
packages/app-mobile/services/e2ee/RSA.react-native.js
packages/app-mobile/services/profiles/index.js
packages/app-mobile/services/voiceTyping/vosk.dummy.js
packages/app-mobile/services/voiceTyping/vosk.js
packages/app-mobile/services/voiceTyping/vosk.android.js
packages/app-mobile/services/voiceTyping/vosk.ios.js
packages/app-mobile/setupQuickActions.js
packages/app-mobile/tools/buildInjectedJs.js
packages/app-mobile/utils/ShareExtension.js
@@ -469,6 +470,7 @@ packages/lib/SyncTargetOneDrive.js
packages/lib/SyncTargetRegistry.js
packages/lib/Synchronizer.js
packages/lib/TaskQueue.js
packages/lib/WelcomeUtils.js
packages/lib/array.js
packages/lib/callbackUrlUtils.js
packages/lib/callbackUrlUtils.test.js
@@ -504,6 +506,7 @@ packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.test.js
packages/lib/import-enex.js
packages/lib/locale.js
packages/lib/locale.test.js
packages/lib/markdownUtils.js
packages/lib/markdownUtils.test.js
packages/lib/markdownUtils2.test.js
@@ -539,6 +542,8 @@ packages/lib/models/utils/itemCanBeEncrypted.js
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/types.js
packages/lib/models/utils/userData.js
packages/lib/models/utils/userData.test.js
packages/lib/net-utils.js
packages/lib/ntp.js
packages/lib/onedrive-api.js
@@ -797,7 +802,6 @@ packages/plugins/ToggleSidebars/api/index.js
packages/plugins/ToggleSidebars/api/types.js
packages/plugins/ToggleSidebars/src/index.js
packages/react-native-saf-x/src/index.js
packages/react-native-vosk/src/index.js
packages/renderer/HtmlToHtml.js
packages/renderer/InMemoryCache.js
packages/renderer/MarkupToHtml.js
@@ -831,6 +835,7 @@ packages/renderer/noteStyle.js
packages/renderer/pathUtils.js
packages/renderer/utils.js
packages/tools/build-release-stats.js
packages/tools/build-welcome.js
packages/tools/buildServerDocker.js
packages/tools/buildServerDocker.test.js
packages/tools/bundleDefaultPlugins.js

View File

@@ -15,7 +15,6 @@
"@joplin/tools",
"@joplin/react-native-saf-x",
"@joplin/react-native-alarm-notification",
"@joplin/react-native-vosk",
"@joplin/utils"
]
}

View File

@@ -105,6 +105,70 @@ index 441e41cc402cca3a60b34978ef4fea976076259c..a173acebb4b314402550442ad471e0f7
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

View File

@@ -1,30 +0,0 @@
diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
index a8abd71833879201e3438b2fa51d712a311c4551..ffe9c2c6dfa5c703ba76b65d94d5dd6784102c19 100644
--- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
+++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
@@ -591,7 +591,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
// ignored.printStackTrace();
}
- RNFetchBlobFileResp rnFetchBlobFileResp = (RNFetchBlobFileResp) responseBody;
+ RNFetchBlobFileResp rnFetchBlobFileResp = new RNFetchBlobFileResp(responseBody);
if(rnFetchBlobFileResp != null && !rnFetchBlobFileResp.isDownloadComplete()){
callback.invoke("Download interrupted.", null);
diff --git a/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java b/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
index 2470eef612308c15a89dfea5a1f16937469be29f..965f8becc195965907699182c764ec9e51811450 100644
--- a/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
+++ b/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
@@ -35,6 +35,12 @@ public class RNFetchBlobFileResp extends ResponseBody {
FileOutputStream ofStream;
boolean isEndMarkerReceived;
+ // ref: https://github.com/joltup/rn-fetch-blob/issues/490#issuecomment-990899440
+ public RNFetchBlobFileResp(ResponseBody body) {
+ super();
+ this.originalBody = body;
+ }
+
public RNFetchBlobFileResp(ReactApplicationContext ctx, String taskId, ResponseBody body, String path, boolean overwrite) throws IOException {
super();
this.rctContext = ctx;

View File

@@ -42,7 +42,7 @@ export function addJoplinChecklistCommands(editor, ToggleList) {
});
editor.addCommand('InsertJoplinChecklist', function (ui, detail) {
detail = Object.assign({}, detail, { listType: 'joplinChecklist' });
detail = { ...detail, listType: 'joplinChecklist' };
ToggleList.toggleList(editor, 'UL', detail);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -9,7 +9,9 @@ function getOs() {
function getFilename(path) {
if (!path) return '';
const s = path.split('/');
return s.pop();
const urlWithParams = s.pop();
const s2 = urlWithParams.split('?');
return s2[0];
}
function getMobileOs() {

View File

@@ -1,4 +1,46 @@
<?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>Thu, 02 Mar 2023 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Thu, 02 Mar 2023 00:00:00 GMT</pubDate><item><title><![CDATA[Joplin will participate in JdLL 2023!]]></title><description><![CDATA[<p>On 1 and 2 April 2023, we will have a stand for Joplin at the <a href="https://www.jdll.org/">Journées du Logiciel Libre</a> in Lyon, France. The JdLL has been taking place in Lyon for 24 years and is a popular open source conference in France. We had a stand in 2020 and 2021 but that was cancelled due to Covid, so this year is a first for Joplin!</p>
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Mon, 08 May 2023 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Mon, 08 May 2023 00:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 2.10]]></title><description><![CDATA[<p>Great news! Joplin 2.10 is here and we've made some amazing improvements and bug fixes, with a focus on the mobile app.</p>
<h1>New design for &quot;New note&quot; and &quot;New to-do&quot; buttons<a name="new-design-for-new-note-and-new-to-do-buttons" href="#new-design-for-new-note-and-new-to-do-buttons" class="heading-anchor">🔗</a></h1>
<p>We're excited to announce that we've made it even easier to create new notes and to-do lists with new designs for the &quot;New note&quot; and &quot;New to-do&quot; buttons.</p>
<p>If there is enough space, the button labels will be shown in full:</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230508-new-note-1.png" alt=""></p>
<p>While for those who prefer a more narrow note list, only the button icons will be shown:</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230508-new-note-2.png" alt=""></p>
<p>It's a small improvement, but we're confident it will make the app even more intuitive for new users.</p>
<h1>Fixes and improvements<a name="fixes-and-improvements" href="#fixes-and-improvements" class="heading-anchor">🔗</a></h1>
<p>This version includes 30 bug fixes and 16 general improvements. Let's dive into some of the highlights:</p>
<ul>
<li>
<p>Self Not Found and Pedro have been working tirelessly on improving the pasting of plain text in the application, and we're happy to say that there is now a brand new &quot;Paste as text&quot; option in the Edit menu.</p>
</li>
<li>
<p>For our amazing plugin developers out there, we have added a few new APIs and fixed a bug that was preventing certain plugins from starting. You can now get even more creative with your plugins!</p>
</li>
<li>
<p>Tao Klerks, has been hard at work fixing and improving the custom sort order of the note list. No more notes in the wrong position when you drop them! Plus, custom sort order is now synchronised too.</p>
</li>
<li>
<p>We've also fixed a few bugs with our Web Clipper, including an issue where certain pages wouldn't import their images. A new Web Clipper has also been released, so you can clip to your heart's content!</p>
</li>
</ul>
<p>Last but not least, we have modernised both the desktop and mobile application modules, just <a href="https://joplinapp.org/news/20221115-renovate/">as we previously announced</a>. Although these changes may not be visible to you, they required a lot of work! But the result is that our applications are now more stable and secure, and it will be easier to maintain them in the long run. We're using a tool called <a href="https://www.mend.io/free-developer-tools/renovate/">Renovate</a>, which will automatically propose package updates that we will review. In total, we've updated a whopping 633 packages so far!</p>
<h1>Android app is available in the Play Store<a name="android-app-is-available-in-the-play-store" href="#android-app-is-available-in-the-play-store" class="heading-anchor">🔗</a></h1>
<p>Our latest version, 2.10, is now back in the Play Store and ready for download! Although we had to skip 2.9 due to some of Google's requirements, we worked hard to ensure that our app complies with their standards, and we are excited to announce that we are back and better than ever! Our iOS version is also available, so you can continue to enjoy the app regardless of the platform you use.</p>
<h2>Biometrics support<a name="biometrics-support" href="#biometrics-support" class="heading-anchor">🔗</a></h2>
<p>To make your experience even more secure, our Android and iOS apps now support biometric unlock! With just a quick scan of your fingerprint or Face ID, you can unlock your app in no time. To enable this beta feature, just head over to the settings and click on &quot;Use biometrics to secure access to the app&quot;.</p>
<p>We've tested this feature thoroughly during prerelease, and have already fixed all known issues. However we still consider it as a beta feature for now, so if you run into any issues please let us know.</p>
<h2>Support for multiple profile<a name="support-for-multiple-profile" href="#support-for-multiple-profile" class="heading-anchor">🔗</a></h2>
<p>We're thrilled to announce that multiple profiles are now supported in our mobile app! To create a new profile, simply go to the Configuration screen and click on &quot;Manage profiles&quot; under the Tools section.</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230508-biometrics-1.png" alt=""></p>
<p>From there, you can easily add or remove profiles as needed.</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230508-biometrics-2.png" alt=""></p>
<p>Once multiple profiles are setup, you will see a new option in the sidebar to quickly toggle from one profile to another:</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230508-biometrics-3.png" alt=""></p>
<p>Once you've set up multiple profiles, you can easily toggle between them using the new option in the sidebar. This feature is perfect for separating your personal and work notes into independent collections.</p>
<h2>Support for realtime search<a name="support-for-realtime-search" href="#support-for-realtime-search" class="heading-anchor">🔗</a></h2>
<p>Our mobile app now has an improved search function that performs text searches in real time! No more waiting for the search results to load, they'll appear instantly as you type.</p>
<h2>Improved filesystem sync performance<a name="improved-filesystem-sync-performance" href="#improved-filesystem-sync-performance" class="heading-anchor">🔗</a></h2>
<p>Thanks to the hard work of jd1378, the sync no longer freezes during filesystem synchronisation. We know how frustrating that can be, and we're thrilled to have solved this issue. Getting filesystem sync to work on Android is never easy due to the restrictions put in place by Google, especially since they frequently change, but we're committed to delivering the best possible experience for our Android users.</p>
]]></description><link>https://joplinapp.org/news/20230508-release-2-10/</link><guid isPermaLink="false">20230508-release-2-10</guid><pubDate>Mon, 08 May 2023 00:00:00 GMT</pubDate><twitter-text>What&apos;s new in Joplin 2.10</twitter-text></item><item><title><![CDATA[Joplin will participate in JdLL 2023!]]></title><description><![CDATA[<p>On 1 and 2 April 2023, we will have a stand for Joplin at the <a href="https://www.jdll.org/">Journées du Logiciel Libre</a> in Lyon, France. The JdLL has been taking place in Lyon for 24 years and is a popular open source conference in France. We had a stand in 2020 and 2021 but that was cancelled due to Covid, so this year is a first for Joplin!</p>
<p>Admission is free, so don't hesitate to come and meet us, exchange ideas and learn more about Joplin!</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230202-jdll.jpg" alt="Joplin at JdLL 2023"></p>
]]></description><link>https://joplinapp.org/news/20230302-jdll-2023/</link><guid isPermaLink="false">20230302-jdll-2023</guid><pubDate>Thu, 02 Mar 2023 00:00:00 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Introducing the "GitHub Actions Raw Log Viewer" extension for Chrome]]></title><description><![CDATA[<p>If you've ever used GitHub Actions, you will find that they provide by default a nice coloured output for the log. It looks good and it's even interactive! (You can click to collapse/expand blocks of text) But unfortunately it doesn't scale to large workflows, like we have for Joplin - the log can freeze and it will take forever to search for something. Indeed searching is done in &quot;real time&quot;... which mostly means it will freeze for a minute or two for each letter you type in the search box. Not great.</p>
@@ -271,30 +313,4 @@
<p>- <a href="https://community.letsencrypt.org/t/issues-with-electron-and-expired-root/160991">Issue with Electron and expired root</a> on Let's Encrypt</p>
<p>- <a href="https://github.com/electron/electron/issues/31212">Let's Encrypt root CA isn't working properly</a> on Electron GitHub repository</p>
<p><strong>Update:</strong> I have implemented a temporary fix on Joplin Cloud which should solve the issue for now. If you're still having some issues please let me know. An updated desktop app will be available later on with a more permanent fix.</p>
]]></description><link>https://joplinapp.org/news/20210930-163458/</link><guid isPermaLink="false">20210930-163458</guid><pubDate>Thu, 30 Sep 2021 16:34:58 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Joplin 2.4 is available!]]></title><description><![CDATA[<p>Joplin 2.4 is now available on desktop, mobile and CLI. Here's what's new in this release:</p>
<h3>Sync Wizard Dialog<a name="sync-wizard-dialog" href="#sync-wizard-dialog" class="heading-anchor">🔗</a></h3>
<p>A new Sync Wizard Dialog has been added to simplify setting up sync on new clients.</p>
<p>The dialog shows the main sync targets, their differences, and makes it easy to choose one and start synchronising. This is mostly aimed at new users or those perhaps less technical. Those who are self hosting or using complex setups will still easily find what they need from a link on that dialog (or in Config &gt; Synchronisation like before).</p>
<p>Sync setup on mobile has been slightly improved too - now on a new client, instead of asking you to sync with Dropbox directly (which may not be what you want), it jumps to the Config &gt; Synchronisation section where you can select the sync target</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20210929-144036_0.png" alt=""></p>
<h3>Disable synchronisation<a name="disable-synchronisation" href="#disable-synchronisation" class="heading-anchor">🔗</a></h3>
<p>It's a small change but something that's been asked many time - it's now possible to disable synchronisation entirely by selecting &quot;None&quot; as a sync target. Previously that could be done in a hacky way, by selecting a non-configured sync target. Now it's clearer and easier to do.</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20210929-144036_1.png" alt=""></p>
<h3>Add back support for deprecated plugins<a name="add-back-support-for-deprecated-plugins" href="#add-back-support-for-deprecated-plugins" class="heading-anchor">🔗</a></h3>
<p>Recently some plugins stopped working because deprecated plugin APIs had been removed. It had been planned for a long time but I suspect the warnings weren't visible enough so plugin developers didn't act on them, and as a result many plugins stopped working.</p>
<p>This is now fixed in the latest version. A selected number of plugins will have access to these old deprecated APIs, which means they will start working again. This was mainly affecting ambrt's plugins such as &quot;Convert Text To New Note&quot; or the popular &quot;Embed Search&quot; plugin.</p>
<h3>Add support for recommended plugins<a name="add-support-for-recommended-plugins" href="#add-support-for-recommended-plugins" class="heading-anchor">🔗</a></h3>
<p>As mentioned in an earlier post, we now support <a href="https://www.patreon.com/posts/introducing-in-55618802">recommended plugins</a>. These recommended plugins appear on top when searching and are identified by a small crown.</p>
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20210929-144036_2.png" alt=""></p>
<h3>End to End Encryption improvements<a name="end-to-end-encryption-improvements" href="#end-to-end-encryption-improvements" class="heading-anchor">🔗</a></h3>
<p>Like most recent releases, v2.4 includes a few improvement to the End to End Encryption (E2EE) system. The goal is to make it easier to use, to make it more reliable and to support the future use case of sharing encrypted notebooks or notes.</p>
<p>One important change is the support for a master password. This single password will be responsible to encrypt various keys, including some that will be automatically generated. Thanks to this, it won't be necessary to ask to enter a new password every time a key needs to be encrypted, since the master password can be used. It will also be easier to manage since you'll only have one password to remember instead of a different one for each notebook you might have shared.</p>
<p>Finally, it's now possible to disable a master key. What it means is that it will no longer show up in the list of master keys, and will also no longer generate a warning asking you to enter the password. In some case you might have forgotten it and no longer need it key, so you can now disable it.</p>
<h3>Custom CSS<a name="custom-css" href="#custom-css" class="heading-anchor">🔗</a></h3>
<p>This version also introduces a few internal change to better support custom CSS. In particular the colours now come from a CSS file, which could potentially be overridden, and new UI elements are styled using stylesheets, which likewise could be overridden.</p>
<p>Those are just first steps, but eventually these changes will make it easier to style the UI and create new themes.</p>
<h3>Bug fixes<a name="bug-fixes" href="#bug-fixes" class="heading-anchor">🔗</a></h3>
<p>This release also includes about 30 various bug fixes and improvements.</p>
<p>A notable one is a fix for GotoAnything, which recently wasn't working on first try.</p>
<p>The plugin screen has also been improved so that search works even when GitHub is down or blocked, as it is in China in particular.</p>
]]></description><link>https://joplinapp.org/news/20210929-144036/</link><guid isPermaLink="false">20210929-144036</guid><pubDate>Wed, 29 Sep 2021 14:40:36 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
]]></description><link>https://joplinapp.org/news/20210930-163458/</link><guid isPermaLink="false">20210930-163458</guid><pubDate>Thu, 30 Sep 2021 16:34:58 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>

View File

@@ -114,6 +114,20 @@ elif [[ $ARCHITECTURE =~ .*i386.*|.*i686.* ]] ; then
exit 1
fi
#-----------------------------------------------------
print "Checking dependencies..."
## Check if libfuse2 is present.
if [[ $(command -v ldconfig) ]]; then
LIBFUSE=$(ldconfig -p | grep "libfuse.so.2" || echo '')
else
LIBFUSE=$(find /lib /usr/lib /lib64 /usr/lib64 /usr/local/lib -name "libfuse.so.2" 2>/dev/null | grep "libfuse.so.2" || echo '')
fi
if [[ $LIBFUSE == "" ]] ; then
print "${COLOR_RED}Error: Can't get libfuse2 on system, please install libfuse2${COLOR_RESET}"
print "See https://joplinapp.org/faq/#desktop-application-will-not-launch-on-linux and https://github.com/AppImage/AppImageKit/wiki/FUSE for more information"
exit 1
fi
#-----------------------------------------------------
# Download Joplin
#-----------------------------------------------------
@@ -134,10 +148,16 @@ else
print "The latest version is ${RELEASE_VERSION}, but you have ${CURRENT_VERSION:-no version} installed."
fi
# Check if it's an update or a new install
DOWNLOAD_TYPE="New"
if [[ -f ~/.joplin/Joplin.AppImage ]]; then
DOWNLOAD_TYPE="Update"
fi
#-----------------------------------------------------
print 'Downloading Joplin...'
TEMP_DIR=$(mktemp -d)
wget -O "${TEMP_DIR}/Joplin.AppImage" "https://github.com/laurent22/joplin/releases/download/v${RELEASE_VERSION}/Joplin-${RELEASE_VERSION}.AppImage"
wget -O "${TEMP_DIR}/Joplin.AppImage" "https://objects.joplinusercontent.com/v${RELEASE_VERSION}/Joplin-${RELEASE_VERSION}.AppImage?source=LinuxInstallScript&type=$DOWNLOAD_TYPE"
wget -O "${TEMP_DIR}/joplin.png" https://joplinapp.org/images/Icon512.png
#-----------------------------------------------------

View File

@@ -22,21 +22,23 @@ Three types of applications are available: for **desktop** (Windows, macOS and L
Operating System | Download
---|---
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v2.9.17/Joplin-Setup-2.9.17.exe'><img alt='Get it on Windows' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v2.9.17/Joplin-2.9.17.dmg'><img alt='Get it on macOS' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v2.9.17/Joplin-2.9.17.AppImage'><img alt='Get it on Linux' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeLinux.png'/></a>
Windows (32 and 64-bit) | <a href='https://objects.joplinusercontent.com/v2.10.19/Joplin-Setup-2.10.19.exe?source=JoplinWebsite&type=New'><img alt='Get it on Windows' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeWindows.png'/></a>
macOS | <a href='https://objects.joplinusercontent.com/v2.10.19/Joplin-2.10.19.dmg?source=JoplinWebsite&type=New'><img alt='Get it on macOS' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeMacOS.png'/></a>
Linux | <a href='https://objects.joplinusercontent.com/v2.10.19/Joplin-2.10.19.AppImage?source=JoplinWebsite&type=New'><img alt='Get it on Linux' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeLinux.png'/></a>
**On Windows**, you may also use the <a href='https://github.com/laurent22/joplin/releases/download/v2.9.17/JoplinPortable.exe'>Portable version</a>. The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
**On Windows**, you may also use the <a href='https://objects.joplinusercontent.com/v2.10.19/JoplinPortable.exe?source=JoplinWebsite&type=New'>Portable version</a>. The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
**On Linux**, the recommended way is to use the following installation script as it will handle the desktop icon too:
<pre><code style="word-break: break-all">wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh | bash</code></pre>
The install and update script supports the [following flags](https://github.com/laurent22/joplin/blob/dev/Joplin_install_and_update.sh#L50) (around line 50 at the time of this writing).
## Mobile applications
Operating System | Download | Alt. Download
---|---|---
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.9.8/joplin-v2.9.8.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.9.8/joplin-v2.9.8-32bit.apk)
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://objects.joplinusercontent.com/android-v2.9.8/joplin-v2.9.8.apk?source=JoplinWebsite&type=New) [32-bit](https://objects.joplinusercontent.com/android-v2.9.8/joplin-v2.9.8-32bit.apk?source=JoplinWebsite&type=New)
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeIOS.png'/></a> | -
## Terminal application
@@ -64,7 +66,7 @@ A community maintained list of these distributions can be found here: [Unofficia
# Sponsors
<!-- SPONSORS-ORG -->
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-webseite&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://residence-greece.com/"><img title="Greece Golden Visa" width="256" src="https://joplinapp.org/images/sponsors/ResidenceGreece.jpg"/></a> <a href="https://grundstueckspreise.info/"><img title="SP Software GmbH" width="256" src="https://joplinapp.org/images/sponsors/Grundstueckspreise.png"/></a>
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&amp;mtm_kwd=joplinapp&amp;mtm_source=joplinapp-webseite&amp;mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://residence-greece.com/"><img title="Greece Golden Visa" width="256" src="https://joplinapp.org/images/sponsors/ResidenceGreece.jpg"/></a> <a href="https://grundstueckspreise.info/"><img title="SP Software GmbH" width="256" src="https://joplinapp.org/images/sponsors/Grundstueckspreise.png"/></a> <a href="https://tranio.com/spain/"><img title="Property for sale in Spain" width="256" src="https://joplinapp.org/images/sponsors/TranioOverseasProperty.jpg"/></a>
<!-- SPONSORS-ORG -->
* * *
@@ -530,47 +532,47 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 80%
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 23%
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 58%
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 79%
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 22%
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 57%
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 45%
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 77%
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [MrKanister](mailto:pueblos_spatulas@aleeas.com) | 99%
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | Fejby | 98%
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 98%
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [MrKanister](mailto:pueblos_spatulas@aleeas.com) | 98%
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 44%
<img src="https://joplinapp.org/images/flags/country-4x3/gb.png" width="16px"/> | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
<img src="https://joplinapp.org/images/flags/country-4x3/us.png" width="16px"/> | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Villaverde](mailto:teko.gr@gmail.com) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Villaverde](mailto:teko.gr@gmail.com) | 97%
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 25%
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato0 | 95%
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato0 | 98%
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 100%
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 29%
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Wisnu Adi Santoso](mailto:waditos@gmail.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Manuel Tassi](mailto:mannivuwiki@gmail.com) | 81%
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 78%
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 79%
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MHolkamp](mailto:mholkamp@users.noreply.github.com) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Wisnu Adi Santoso](mailto:waditos@gmail.com) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Manuel Tassi](mailto:mannivuwiki@gmail.com) | 80%
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 77%
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 78%
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MHolkamp](mailto:mholkamp@users.noreply.github.com) | 87%
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 87%
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 55%
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [X3NO](mailto:X3NO@disroot.org) | 91%
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Renato Nunes Bastos](mailto:rnbastos@gmail.com) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 73%
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 51%
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [X3NO](mailto:X3NO@disroot.org) | 90%
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Douglas Leão](mailto:djlsplays@gmail.com) | 87%
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 72%
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 50%
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 80%
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 36%
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 78%
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 77%
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 72%
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Dmitriy Q](mailto:krotesk@mail.ru) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 65%
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [wh201906](mailto:wh201906@yandex.com) | 97%
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Kevin Hsu](mailto:kevin.hsu.hws@gmail.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 89%
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 87%
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Dmitriy K](mailto:dmitry@atsip.ru) | 99%
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 64%
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [wh201906](mailto:wh201906@yandex.com) | 96%
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Kevin Hsu](mailto:kevin.hsu.hws@gmail.com) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 88%
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 88%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Contributors

View File

@@ -107,7 +107,10 @@
".eslintignore": true,
".gitignore": true,
".vscode/*": true,
".yarn": true,
".yarn/cache": true,
".yarn/install-state.gz": true,
".yarn/plugins": true,
".yarn/releases": true,
"*.sublime-workspace": true,
"**/_mydocs": true,
"**/_mydocs/EnexSamples/*.enex": true,

View File

@@ -66,37 +66,36 @@
"devDependencies": {
"@joplin/utils": "~2.11",
"@seiyab/eslint-plugin-react-hooks": "4.5.1-beta.0",
"@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2",
"@typescript-eslint/eslint-plugin": "5.59.0",
"@typescript-eslint/parser": "5.59.0",
"cspell": "5.21.2",
"eslint": "8.31.0",
"eslint-interactive": "10.3.0",
"eslint-plugin-import": "2.27.4",
"eslint": "8.39.0",
"eslint-interactive": "10.7.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jest": "27.2.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-react": "7.32.0",
"eslint-plugin-react": "7.32.2",
"execa": "5.1.1",
"fs-extra": "11.1.1",
"glob": "8.1.0",
"gulp": "4.0.2",
"husky": "3.1.0",
"lerna": "3.22.1",
"lint-staged": "13.2.1",
"lint-staged": "13.2.2",
"madge": "6.0.0",
"npm-package-json-lint": "6.4.0",
"typedoc": "0.17.8",
"typescript": "4.9.4"
"typescript": "5.0.2"
},
"dependencies": {
"@types/fs-extra": "9.0.13",
"@types/fs-extra": "11.0.1",
"http-server": "14.1.1",
"node-gyp": "9.3.1",
"nodemon": "2.0.22"
},
"packageManager": "yarn@3.3.1",
"packageManager": "yarn@3.5.0",
"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",
"rn-fetch-blob@0.12.0": "patch:rn-fetch-blob@npm%3A0.12.0#./.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch",
"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"
}
}

View File

@@ -302,7 +302,7 @@ class AppGui {
const output = [];
for (let i = 0; i < keymap.length; i++) {
const item = Object.assign({}, keymap[i]);
const item = { ...keymap[i] };
if (!item.command) throw new Error(`Missing command for keymap item: ${JSON.stringify(item)}`);
@@ -427,7 +427,7 @@ class AppGui {
async handleModelAction(action) {
this.logger().info('Action:', action);
const state = Object.assign({}, defaultState);
const state = { ...defaultState };
state.notes = this.widget('noteList').items;
const newState = reducer(state, action);

View File

@@ -192,7 +192,7 @@ class Application extends BaseApplication {
let output = await this.cache_.getItem('metadata');
if (output) {
this.commandMetadata_ = output;
return Object.assign({}, this.commandMetadata_);
return { ...this.commandMetadata_ };
}
const commands = this.commands();
@@ -207,7 +207,7 @@ class Application extends BaseApplication {
await this.cache_.setItem('metadata', output, 1000 * 60 * 60 * 24);
this.commandMetadata_ = output;
return Object.assign({}, this.commandMetadata_);
return { ...this.commandMetadata_ };
}
hasGui() {

View File

@@ -57,7 +57,7 @@
"proper-lockfile": "4.1.2",
"read-chunk": "2.1.0",
"server-destroy": "1.0.1",
"sharp": "0.32.0",
"sharp": "0.32.1",
"sprintf-js": "1.1.2",
"sqlite3": "5.1.6",
"string-padding": "1.0.2",
@@ -71,13 +71,13 @@
},
"devDependencies": {
"@joplin/tools": "~2.11",
"@types/fs-extra": "9.0.13",
"@types/jest": "29.2.6",
"@types/node": "18.11.18",
"@types/fs-extra": "11.0.1",
"@types/jest": "29.5.1",
"@types/node": "18.15.13",
"@types/proper-lockfile": "^4.1.2",
"gulp": "4.0.2",
"jest": "29.4.3",
"jest": "29.5.0",
"temp": "0.9.4",
"typescript": "4.9.4"
"typescript": "5.0.2"
}
}

View File

@@ -0,0 +1,19 @@
<table>
<tbody>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,5 @@
| | | |
| :--- | :---: | ---: |
| Left | Centered | Right |
| Left | Centered | Right |
| Left | Centered | Right |

View File

@@ -0,0 +1,26 @@
<table>
<thead>
<tr>
<th style="text-align:left">Left-aligned Column</th>
<th style="text-align:center">Center-aligned Column</th>
<th style="text-align:right">Right-aligned Column</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,5 @@
| Left-aligned Column | Center-aligned Column | Right-aligned Column |
| :--- | :---: | ---: |
| Left | Centered | Right |
| Left | Centered | Right |
| Left | Centered | Right |

View File

@@ -0,0 +1,14 @@
<table>
<thead>
<tr>
<th align="center">abc</th>
<th align="right">defghi</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">bar</td>
<td align="right">baz</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,3 @@
| abc | defghi |
| :---: | ---: |
| bar | baz |

View File

@@ -0,0 +1,29 @@
<table>
<thead>
<tr>
<th style="text-align:left">Left-aligned Column</th>
<th>This header cell's text is unaligned, but a majority of the text in this column is center-aligned so the
column will be center-aligned</th>
<th style="text-align:right">Right-aligned Column</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">Left</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
<tr>
<td style="text-align:right">This is the only right-aligned cell in this column. This is possible if a user
edits a cell's alignment using the cell properties dialog.</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
<tr>
<td style="text-align:center">This is the only center-aligned cell in this column. This is possible if a
user edits a cell's alignment using the cell properties dialog.</td>
<td style="text-align:center">Centered</td>
<td style="text-align:right">Right</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,5 @@
| Left-aligned Column | This header cell's text is unaligned, but a majority of the text in this column is center-aligned so the column will be center-aligned | Right-aligned Column |
| :--- | :---: | ---: |
| Left | Centered | Right |
| This is the only right-aligned cell in this column. This is possible if a user edits a cell's alignment using the cell properties dialog. | Centered | Right |
| This is the only center-aligned cell in this column. This is possible if a user edits a cell's alignment using the cell properties dialog. | Centered | Right |

View File

@@ -0,0 +1 @@
<div class="jop-noMdConv">

View File

@@ -0,0 +1 @@
<div><svg><style></svg><iframe srcdoc="<script>top.require('child_process').execSync('calc')</script>"></iframe></div>

View File

@@ -0,0 +1 @@
<a href="#" class="jop-noMdConv">XSS</a>

View File

@@ -0,0 +1 @@
<a data-from-md="" href="javascript:top.require('child_process').execSync('open -a Calculator')">XSS</a>

View File

@@ -0,0 +1 @@
<use href="#" class="jop-noMdConv">

View File

@@ -0,0 +1 @@
<svg><use href="data:image/svg+xml,&lt;svg id='x' xmlns='http://www.w3.org/2000/svg'&gt;&lt;image href='asdf' onerror='top.require(`child_process`).execSync(`calc.exe`)' /&gt;&lt;/svg&gt;#x" />

After

Width:  |  Height:  |  Size: 193 B

View File

@@ -0,0 +1 @@
<map name="test" class="jop-noMdConv"><area coords="0,0,1000,1000" href="#" class="jop-noMdConv"/></map><img usemap="#test" src="https://github.com/Ry0taK.png" class="jop-noMdConv"/>

View File

@@ -0,0 +1 @@
<map name="test"><area coords="0,0,1000,1000" href="javascript:top.require(`child_process`).execSync(`calc.exe`)"></map><img usemap="#test" src="https://github.com/Ry0taK.png">

View File

@@ -0,0 +1 @@
<a href="#top" class="jop-noMdConv">⬆️</a>

View File

@@ -0,0 +1 @@
<a href="#top">⬆️</a>

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -20,3 +20,4 @@ cd "$SCRIPT_DIR/withExternalModules/" && yo joplin --update --skip-install --sil
cd "$SCRIPT_DIR/post_messages/" && yo joplin --update --skip-install --silent
cd "$SCRIPT_DIR/nativeModule/" && yo joplin --update --skip-install --silent
cd "$SCRIPT_DIR/external_assets/" && yo joplin --update --skip-install --silent
cd "$SCRIPT_DIR/user_data/" && yo joplin --update --skip-install --silent

View File

@@ -0,0 +1,3 @@
dist/
node_modules/
publish/

View File

@@ -0,0 +1,8 @@
*.md
!README.md
/*.jpl
/api
/src
/dist
tsconfig.json
webpack.config.js

View File

@@ -0,0 +1,72 @@
# generator-joplin
Scaffolds out a new Joplin plugin
## Installation
First, install [Yeoman](http://yeoman.io) and generator-joplin using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
```bash
npm install -g yo
npm install -g generator-joplin
```
Then generate your new project:
```bash
yo joplin
```
## Development
To test the generator for development purposes, follow the instructions there: https://yeoman.io/authoring/#running-the-generator
This is a template to create a new Joplin plugin.
## Structure
The main two files you will want to look at are:
- `/src/index.ts`, which contains the entry point for the plugin source code.
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
The file `/plugin.config.json` could also be useful if you intend to use [external scripts](#external-script-files), such as content scripts or webview scripts.
## Building the plugin
The plugin is built using Webpack, which creates the compiled code in `/dist`. A JPL archive will also be created at the root, which can use to distribute the plugin.
To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
- In `package.json`, the name starts with "joplin-plugin-". For example, "joplin-plugin-toc".
- In `package.json`, the keywords include "joplin-plugin".
- In the `publish/` directory, there should be a .jpl and .json file (which are built by `npm run dist`)
In general all this is done automatically by the plugin generator, which will set the name and keywords of package.json, and will put the right files in the "publish" directory. But if something doesn't work and your plugin doesn't appear in the repository, double-check the above conditions.
## Updating the plugin framework
To update the plugin framework, run `npm run update`.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
## External script files
By default, the compiler (webpack) is going to compile `src/index.ts` only (as well as any file it imports), and any other file will simply be copied to the plugin package. In some cases this is sufficient, however if you have [content scripts](https://joplinapp.org/api/references/plugin_api/classes/joplincontentscripts.html) or [webview scripts](https://joplinapp.org/api/references/plugin_api/classes/joplinviewspanels.html#addscript) you might want to compile them too, in particular in these two cases:
- The script is a TypeScript file - in which case it has to be compiled to JavaScript.
- The script requires modules you've added to package.json. In that case, the script, whether JS or TS, must be compiled so that the dependencies are bundled with the JPL file.
To get such an external script file to compile, you need to add it to the `extraScripts` array in `plugin.config.json`. The path you add should be relative to /src. For example, if you have a file in "/src/webviews/index.ts", the path should be set to "webviews/index.ts". Once compiled, the file will always be named with a .js extension. So you will get "webviews/index.js" in the plugin package, and that's the path you should use to reference the file.
## License
MIT © Laurent Cozic

View File

@@ -0,0 +1,24 @@
# Joplin Plugin
This is a template to create a new Joplin plugin.
The main two files you will want to look at are:
- `/src/index.ts`, which contains the entry point for the plugin source code.
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
## Building the plugin
The plugin is built using Webpack, which creates the compiled code in `/dist`. A JPL archive will also be created at the root, which can use to distribute the plugin.
To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Updating the plugin framework
To update the plugin framework, run `npm run update`.
In general this command tries to do the right thing - in particular it's going to merge the changes in package.json and .gitignore instead of overwriting. It will also leave "/src" as well as README.md untouched.
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.

View File

@@ -0,0 +1,14 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
/**
* @ignore
*/
/**
* @ignore
*/
export default class Global {
private joplin_;
constructor(implementation: any, plugin: Plugin, store: any);
get joplin(): Joplin;
get process(): any;
}

View File

@@ -0,0 +1,68 @@
import Plugin from '../Plugin';
import JoplinData from './JoplinData';
import JoplinPlugins from './JoplinPlugins';
import JoplinWorkspace from './JoplinWorkspace';
import JoplinFilters from './JoplinFilters';
import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import JoplinContentScripts from './JoplinContentScripts';
import JoplinClipboard from './JoplinClipboard';
import JoplinWindow from './JoplinWindow';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*
* The API is now relatively stable and in general maintaining backward compatibility is a top priority, so you shouldn't except much breakages.
*
* If a breaking change ever becomes needed, best effort will be done to:
*
* - Deprecate features instead of removing them, so as to give you time to fix the issue;
* - Document breaking changes in the changelog;
*
* So if you are developing a plugin, please keep an eye on the changelog as everything will be in there with information about how to update your code.
*/
export default class Joplin {
private data_;
private plugins_;
private workspace_;
private filters_;
private commands_;
private views_;
private interop_;
private settings_;
private contentScripts_;
private clipboard_;
private window_;
constructor(implementation: any, plugin: Plugin, store: any);
get data(): JoplinData;
get clipboard(): JoplinClipboard;
get window(): JoplinWindow;
get plugins(): JoplinPlugins;
get workspace(): JoplinWorkspace;
get contentScripts(): JoplinContentScripts;
/**
* @ignore
*
* Not sure if it's the best way to hook into the app
* so for now disable filters.
*/
get filters(): JoplinFilters;
get commands(): JoplinCommands;
get views(): JoplinViews;
get interop(): JoplinInterop;
get settings(): JoplinSettings;
/**
* It is not possible to bundle native packages with a plugin, because they
* need to work cross-platforms. Instead access to certain useful native
* packages is provided using this function.
*
* Currently these packages are available:
*
* - [sqlite3](https://www.npmjs.com/package/sqlite3)
* - [fs-extra](https://www.npmjs.com/package/fs-extra)
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/nativeModule)
*/
require(_path: string): any;
}

View File

@@ -0,0 +1,23 @@
export default class JoplinClipboard {
private electronClipboard_;
private electronNativeImage_;
constructor(electronClipboard: any, electronNativeImage: any);
readText(): Promise<string>;
writeText(text: string): Promise<void>;
readHtml(): Promise<string>;
writeHtml(html: string): Promise<void>;
/**
* Returns the image in [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.
*/
readImage(): Promise<string>;
/**
* Takes an image in [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format.
*/
writeImage(dataUrl: string): Promise<void>;
/**
* Returns the list available formats (mime types).
*
* For example [ 'text/plain', 'text/html' ]
*/
availableFormats(): Promise<string[]>;
}

View File

@@ -0,0 +1,89 @@
import { Command } from './types';
/**
* This class allows executing or registering new Joplin commands. Commands
* can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or
* {@link JoplinViewsMenuItems | menu items}.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*
* ## Executing Joplin's internal commands
*
* It is also possible to execute internal Joplin's commands which, as of
* now, are not well documented. You can find the list directly on GitHub
* though at the following locations:
*
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts)
*
* To view what arguments are supported, you can open any of these files
* and look at the `execute()` command.
*
* ## Executing editor commands
*
* There might be a situation where you want to invoke editor commands
* without using a {@link JoplinContentScripts | contentScript}. For this
* reason Joplin provides the built in `editor.execCommand` command.
*
* `editor.execCommand` should work with any core command in both the
* [CodeMirror](https://codemirror.net/doc/manual.html#execCommand) and
* [TinyMCE](https://www.tiny.cloud/docs/api/tinymce/tinymce.editorcommands/#execcommand) editors,
* as well as most functions calls directly on a CodeMirror editor object (extensions).
*
* * [CodeMirror commands](https://codemirror.net/doc/manual.html#commands)
* * [TinyMCE core editor commands](https://www.tiny.cloud/docs/advanced/editor-command-identifiers/#coreeditorcommands)
*
* `editor.execCommand` supports adding arguments for the commands.
*
* ```typescript
* await joplin.commands.execute('editor.execCommand', {
* name: 'madeUpCommand', // CodeMirror and TinyMCE
* args: [], // CodeMirror and TinyMCE
* ui: false, // TinyMCE only
* value: '', // TinyMCE only
* });
* ```
*
* [View the example using the CodeMirror editor](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/tests/support/plugins/codemirror_content_script/src/index.ts)
*
*/
export default class JoplinCommands {
/**
* <span class="platform-desktop">desktop</span> Executes the given
* command.
*
* The command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
*
* ```typescript
* // Create a new note in the current notebook:
* await joplin.commands.execute('newNote');
*
* // Create a new sub-notebook under the provided notebook
* // Note: internally, notebooks are called "folders".
* await joplin.commands.execute('newFolder', "SOME_FOLDER_ID");
* ```
*/
execute(commandName: string, ...args: any[]): Promise<any | void>;
/**
* <span class="platform-desktop">desktop</span> Registers a new command.
*
* ```typescript
* // Register a new commmand called "testCommand1"
*
* await joplin.commands.register({
* name: 'testCommand1',
* label: 'My Test Command 1',
* iconName: 'fas fa-music',
* execute: () => {
* alert('Testing plugin command 1');
* },
* });
* ```
*/
register(command: Command): Promise<void>;
}

View File

@@ -0,0 +1,40 @@
import Plugin from '../Plugin';
import { ContentScriptType } from './types';
export default class JoplinContentScripts {
private plugin;
constructor(plugin: Plugin);
/**
* Registers a new content script. Unlike regular plugin code, which runs in
* a separate process, content scripts run within the main process code and
* thus allow improved performances and more customisations in specific
* cases. It can be used for example to load a Markdown or editor plugin.
*
* Note that registering a content script in itself will do nothing - it
* will only be loaded in specific cases by the relevant app modules (eg.
* the Markdown renderer or the code editor). So it is not a way to inject
* and run arbitrary code in the app, which for safety and performance
* reasons is not supported.
*
* The plugin generator provides a way to build any content script you might
* want to package as well as its dependencies. See the [Plugin Generator
* doc](https://github.com/laurent22/joplin/blob/dev/packages/generator-joplin/README.md)
* for more information.
*
* * [View the renderer demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* * [View the editor demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
*
* See also the [postMessage demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.
* @param scriptPath Must be a path relative to the plugin main script. For example, if your file content_script.js is next to your index.ts file, you would set `scriptPath` to `"./content_script.js`.
*/
register(type: ContentScriptType, id: string, scriptPath: string): Promise<void>;
/**
* Listens to a messages sent from the content script using postMessage().
* See {@link ContentScriptType} for more information as well as the
* [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages)
*/
onMessage(contentScriptId: string, callback: any): Promise<void>;
}

View File

@@ -0,0 +1,50 @@
import { ModelType } from '../../../BaseModel';
import { Path } from './types';
/**
* This module provides access to the Joplin data API: https://joplinapp.org/api/references/rest_api/
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
* or to update them or delete them.
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
*
* In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls.
* And each method takes these parameters:
*
* * `path`: This is an array that represents the path to the resource in the form `["resouceName", "resourceId", "resourceLink"]` (eg. ["tags", ":id", "notes"]). The "resources" segment is the name of the resources you want to access (eg. "notes", "folders", etc.). If not followed by anything, it will refer to all the resources in that collection. The optional "resourceId" points to a particular resources within the collection. Finally, an optional "link" can be present, which links the resource to a collection of resources. This can be used in the API for example to retrieve all the notes associated with a tag.
* * `query`: (Optional) The query parameters. In a URL, this is the part after the question mark "?". In this case, it should be an object with key/value pairs.
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
* * `files`: (Optional) Used to create new resources and associate them with files.
*
* Please refer to the [Joplin API documentation](https://joplinapp.org/api/references/rest_api/) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
*
* For example:
*
* ```typescript
* // Get a note ID, title and body
* const noteId = 'some_note_id';
* const note = await joplin.data.get(['notes', noteId], { fields: ['id', 'title', 'body'] });
*
* // Get all folders
* const folders = await joplin.data.get(['folders']);
*
* // Set the note body
* await joplin.data.put(['notes', noteId], null, { body: "New note body" });
*
* // Create a new note under one of the folders
* await joplin.data.post(['notes'], null, { body: "my new note", title: "some title", parent_id: folders[0].id });
* ```
*/
export default class JoplinData {
private api_;
private pathSegmentRegex_;
private serializeApiBody;
private pathToString;
get(path: Path, query?: any): Promise<any>;
post(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
put(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
delete(path: Path, query?: any): Promise<any>;
itemType(itemId: string): Promise<ModelType>;
resourcePath(resourceId: string): Promise<string>;
}

View File

@@ -0,0 +1,10 @@
/**
* @ignore
*
* Not sure if it's the best way to hook into the app
* so for now disable filters.
*/
export default class JoplinFilters {
on(name: string, callback: Function): Promise<void>;
off(name: string, callback: Function): Promise<void>;
}

View File

@@ -0,0 +1,17 @@
import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.
*
* See the documentation of the [[ExportModule]] and [[ImportModule]] for more information.
*
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/api/references/rest_api/
*/
export default class JoplinInterop {
registerExportModule(module: ExportModule): Promise<void>;
registerImportModule(module: ImportModule): Promise<void>;
}

View File

@@ -0,0 +1,43 @@
import Plugin from '../Plugin';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
*/
export default class JoplinPlugins {
private plugin;
constructor(plugin: Plugin);
/**
* Registers a new plugin. This is the entry point when creating a plugin. You should pass a simple object with an `onStart` method to it.
* That `onStart` method will be executed as soon as the plugin is loaded.
*
* ```typescript
* joplin.plugins.register({
* onStart: async function() {
* // Run your plugin code here
* }
* });
* ```
*/
register(script: Script): Promise<void>;
/**
* @deprecated Use joplin.contentScripts.register()
*/
registerContentScript(type: ContentScriptType, id: string, scriptPath: string): Promise<void>;
/**
* Gets the plugin own data directory path. Use this to store any
* plugin-related data. Unlike [[installationDir]], any data stored here
* will be persisted.
*/
dataDir(): Promise<string>;
/**
* Gets the plugin installation directory. This can be used to access any
* asset that was packaged with the plugin. This directory should be
* considered read-only because any data you store here might be deleted or
* re-created at any time. To store new persistent data, use [[dataDir]].
*/
installationDir(): Promise<string>;
/**
* @deprecated Use joplin.require()
*/
require(_path: string): any;
}

View File

@@ -0,0 +1,64 @@
import Plugin from '../Plugin';
import { SettingItem, SettingSection } from './types';
export interface ChangeEvent {
/**
* Setting keys that have been changed
*/
keys: string[];
}
export declare type ChangeHandler = (event: ChangeEvent) => void;
/**
* This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user.
*
* Settings are essentially key/value pairs.
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
constructor(plugin: Plugin);
private get keyPrefix();
private namespacedKey;
/**
* Registers new settings.
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.
* What it means is that you need to register the setting every time the plugin starts (for example in the onStart event).
* The setting value however will be preserved from one launch to the next so there is no risk that it will be lost even if for some
* reason the plugin fails to start at some point.
*/
registerSettings(settings: Record<string, SettingItem>): Promise<void>;
/**
* @deprecated Use joplin.settings.registerSettings()
*
* Registers a new setting.
*/
registerSetting(key: string, settingItem: SettingItem): Promise<void>;
/**
* Registers a new setting section. Like for registerSetting, it is dynamic and needs to be done every time the plugin starts.
*/
registerSection(name: string, section: SettingSection): Promise<void>;
/**
* Gets a setting value (only applies to setting you registered from your plugin)
*/
value(key: string): Promise<any>;
/**
* Sets a setting value (only applies to setting you registered from your plugin)
*/
setValue(key: string, value: any): Promise<void>;
/**
* Gets a global setting value, including app-specific settings and those set by other plugins.
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/dev/packages/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
/**
* Called when one or multiple settings of your plugin have been changed.
* - For performance reasons, this event is triggered with a delay.
* - You will only get events for your own plugin settings.
*/
onChange(handler: ChangeHandler): Promise<void>;
}

View File

@@ -0,0 +1,28 @@
import Plugin from '../Plugin';
import JoplinViewsDialogs from './JoplinViewsDialogs';
import JoplinViewsMenuItems from './JoplinViewsMenuItems';
import JoplinViewsMenus from './JoplinViewsMenus';
import JoplinViewsToolbarButtons from './JoplinViewsToolbarButtons';
import JoplinViewsPanels from './JoplinViewsPanels';
/**
* This namespace provides access to view-related services.
*
* All view services provide a `create()` method which you would use to create the view object, whether it's a dialog, a toolbar button or a menu item.
* In some cases, the `create()` method will return a [[ViewHandle]], which you would use to act on the view, for example to set certain properties or call some methods.
*/
export default class JoplinViews {
private store;
private plugin;
private dialogs_;
private panels_;
private menuItems_;
private menus_;
private toolbarButtons_;
private implementation_;
constructor(implementation: any, plugin: Plugin, store: any);
get dialogs(): JoplinViewsDialogs;
get panels(): JoplinViewsPanels;
get menuItems(): JoplinViewsMenuItems;
get menus(): JoplinViewsMenus;
get toolbarButtons(): JoplinViewsToolbarButtons;
}

View File

@@ -0,0 +1,68 @@
import Plugin from '../Plugin';
import { ButtonSpec, ViewHandle, DialogResult } from './types';
/**
* Allows creating and managing dialogs. A dialog is modal window that
* contains a webview and a row of buttons. You can update the
* webview using the `setHtml` method. Dialogs are hidden by default and
* you need to call `open()` to open them. Once the user clicks on a
* button, the `open` call will return an object indicating what button was
* clicked on.
*
* ## Retrieving form values
*
* If your HTML content included one or more forms, a `formData` object
* will also be included with the key/value for each form.
*
* ## Special button IDs
*
* The following buttons IDs have a special meaning:
*
* - `ok`, `yes`, `submit`, `confirm`: They are considered "submit" buttons
* - `cancel`, `no`, `reject`: They are considered "dismiss" buttons
*
* This information is used by the application to determine what action
* should be done when the user presses "Enter" or "Escape" within the
* dialog. If they press "Enter", the first "submit" button will be
* automatically clicked. If they press "Escape" the first "dismiss" button
* will be automatically clicked.
*
* [View the demo
* plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
private plugin;
private implementation_;
constructor(implementation: any, plugin: Plugin, store: any);
private controller;
/**
* Creates a new dialog
*/
create(id: string): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/
showMessageBox(message: string): Promise<number>;
/**
* Sets the dialog HTML content
*/
setHtml(handle: ViewHandle, html: string): Promise<string>;
/**
* Adds and loads a new JS or CSS files into the dialog.
*/
addScript(handle: ViewHandle, scriptPath: string): Promise<void>;
/**
* Sets the dialog buttons.
*/
setButtons(handle: ViewHandle, buttons: ButtonSpec[]): Promise<ButtonSpec[]>;
/**
* Opens the dialog
*/
open(handle: ViewHandle): Promise<DialogResult>;
/**
* Toggle on whether to fit the dialog size to the content or not.
* When set to false, the dialog is set to 90vw and 80vh
* @default true
*/
setFitToContent(handle: ViewHandle, status: boolean): Promise<boolean>;
}

View File

@@ -0,0 +1,16 @@
import { CreateMenuItemOptions, MenuItemLocation } from './types';
import Plugin from '../Plugin';
/**
* Allows creating and managing menu items.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*/
export default class JoplinViewsMenuItems {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
/**
* Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter.
*/
create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
}

View File

@@ -0,0 +1,18 @@
import { MenuItem, MenuItemLocation } from './types';
import Plugin from '../Plugin';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
private registerCommandAccelerators;
/**
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
}

View File

@@ -0,0 +1,78 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
private controller;
/**
* Creates a new panel
*/
create(id: string): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/
setHtml(handle: ViewHandle, html: string): Promise<string>;
/**
* Adds and loads a new JS or CSS files into the panel.
*/
addScript(handle: ViewHandle, scriptPath: string): Promise<void>;
/**
* Called when a message is sent from the webview (using postMessage).
*
* To post a message from the webview to the plugin use:
*
* ```javascript
* const response = await webviewApi.postMessage(message);
* ```
*
* - `message` can be any JavaScript object, string or number
* - `response` is whatever was returned by the `onMessage` handler
*
* Using this mechanism, you can have two-way communication between the
* plugin and webview.
*
* See the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages) for more details.
*
*/
onMessage(handle: ViewHandle, callback: Function): Promise<void>;
/**
* Sends a message to the webview.
*
* The webview must have registered a message handler prior, otherwise the message is ignored. Use;
*
* ```javascript
* webviewApi.onMessage((message) => { ... });
* ```
*
* - `message` can be any JavaScript object, string or number
*
* The view API may have only one onMessage handler defined.
* This method is fire and forget so no response is returned.
*
* It is particularly useful when the webview needs to react to events emitted by the plugin or the joplin api.
*/
postMessage(handle: ViewHandle, message: any): void;
/**
* Shows the panel
*/
show(handle: ViewHandle, show?: boolean): Promise<void>;
/**
* Hides the panel
*/
hide(handle: ViewHandle): Promise<void>;
/**
* Tells whether the panel is visible or not
*/
visible(handle: ViewHandle): Promise<boolean>;
}

View File

@@ -0,0 +1,16 @@
import { ToolbarButtonLocation } from './types';
import Plugin from '../Plugin';
/**
* Allows creating and managing toolbar buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*/
export default class JoplinViewsToolbarButtons {
private store;
private plugin;
constructor(plugin: Plugin, store: any);
/**
* Creates a new toolbar button and associate it with the given command.
*/
create(id: string, commandName: string, location: ToolbarButtonLocation): Promise<void>;
}

View File

@@ -0,0 +1,24 @@
import Plugin from '../Plugin';
export interface Implementation {
injectCustomStyles(elementId: string, cssFilePath: string): Promise<void>;
}
export default class JoplinWindow {
private plugin_;
private store_;
private implementation_;
constructor(implementation: Implementation, plugin: Plugin, store: any);
/**
* Loads a chrome CSS file. It will apply to the window UI elements, except
* for the note viewer. It is the same as the "Custom stylesheet for
* Joplin-wide app styles" setting. See the [Load CSS Demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/load_css)
* for an example.
*/
loadChromeCssFile(filePath: string): Promise<void>;
/**
* Loads a note CSS file. It will apply to the note viewer, as well as any
* exported or printed note. It is the same as the "Custom stylesheet for
* rendered Markdown" setting. See the [Load CSS Demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/load_css)
* for an example.
*/
loadNoteCssFile(filePath: string): Promise<void>;
}

View File

@@ -0,0 +1,87 @@
import { FolderEntity } from '../../database/types';
import { Disposable, MenuItem } from './types';
export interface EditContextMenuFilterObject {
items: MenuItem[];
}
declare type FilterHandler<T> = (object: T) => Promise<void>;
declare enum ItemChangeEventType {
Create = 1,
Update = 2,
Delete = 3
}
interface ItemChangeEvent {
id: string;
event: ItemChangeEventType;
}
interface SyncStartEvent {
withErrors: boolean;
}
interface ResourceChangeEvent {
id: string;
}
declare type ItemChangeHandler = (event: ItemChangeEvent) => void;
declare type SyncStartHandler = (event: SyncStartEvent) => void;
declare type ResourceChangeHandler = (event: ResourceChangeEvent) => void;
/**
* The workspace service provides access to all the parts of Joplin that
* are being worked on - i.e. the currently selected notes or notebooks as
* well as various related events, such as when a new note is selected, or
* when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;
constructor(store: any);
/**
* Called when a new note or notes are selected.
*/
onNoteSelectionChange(callback: Function): Promise<Disposable>;
/**
* Called when the content of a note changes.
* @deprecated Use `onNoteChange()` instead, which is reliably triggered whenever the note content, or any note property changes.
*/
onNoteContentChange(callback: Function): Promise<void>;
/**
* Called when the content of the current note changes.
*/
onNoteChange(handler: ItemChangeHandler): Promise<Disposable>;
/**
* Called when a resource is changed. Currently this handled will not be
* called when a resource is added or deleted.
*/
onResourceChange(handler: ResourceChangeHandler): Promise<void>;
/**
* Called when an alarm associated with a to-do is triggered.
*/
onNoteAlarmTrigger(handler: Function): Promise<Disposable>;
/**
* Called when the synchronisation process is starting.
*/
onSyncStart(handler: SyncStartHandler): Promise<Disposable>;
/**
* Called when the synchronisation process has finished.
*/
onSyncComplete(callback: Function): Promise<Disposable>;
/**
* Called just before the editor context menu is about to open. Allows
* adding items to it.
*/
filterEditorContextMenu(handler: FilterHandler<EditContextMenuFilterObject>): void;
/**
* Gets the currently selected note
*/
selectedNote(): Promise<any>;
/**
* Gets the currently selected folder. In some cases, for example during
* search or when viewing a tag, no folder is actually selected in the user
* interface. In that case, that function would return the last selected
* folder.
*/
selectedFolder(): Promise<FolderEntity>;
/**
* Gets the IDs of the selected notes (can be zero, one, or many). Use the data API to retrieve information about these notes.
*/
selectedNoteIds(): Promise<string[]>;
}
export {};

View File

@@ -0,0 +1,5 @@
import type Joplin from './Joplin';
declare const joplin: Joplin;
export default joplin;

View File

@@ -0,0 +1,674 @@
// =================================================================
// Command API types
// =================================================================
export interface Command {
/**
* Name of command - must be globally unique
*/
name: string;
/**
* Label to be displayed on menu items or keyboard shortcut editor for example.
* If it is missing, it's assumed it's a private command, to be called programmatically only.
* In that case the command will not appear in the shortcut editor or command panel, and logically
* should not be used as a menu item.
*/
label?: string;
/**
* Icon to be used on toolbar buttons for example
*/
iconName?: string;
/**
* Code to be ran when the command is executed. It may return a result.
*/
execute(...args: any[]): Promise<any | void>;
/**
* Defines whether the command should be enabled or disabled, which in turns
* affects the enabled state of any associated button or menu item.
*
* The condition should be expressed as a "when-clause" (as in Visual Studio
* Code). It's a simple boolean expression that evaluates to `true` or
* `false`. It supports the following operators:
*
* Operator | Symbol | Example
* -- | -- | --
* Equality | == | "editorType == markdown"
* Inequality | != | "currentScreen != config"
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
* And | && | "oneNoteSelected && !inConflictFolder"
*
* Joplin, unlike VSCode, also supports parenthesis, which allows creating
* more complex expressions such as `cond1 || (cond2 && cond3)`. Only one
* level of parenthesis is possible (nested ones aren't supported).
*
* Currently the supported context variables aren't documented, but you can
* find the list below:
*
* - [Global When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts)
* - [Desktop app When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/app-desktop/services/commands/stateToWhenClauseContext.ts)
*
* Note: Commands are enabled by default unless you use this property.
*/
enabledCondition?: string;
}
// =================================================================
// Interop API types
// =================================================================
export enum FileSystemItem {
File = 'file',
Directory = 'directory',
}
export enum ImportModuleOutputFormat {
Markdown = 'md',
Html = 'html',
}
/**
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
*
* In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
*
* To get a better sense of what it will contain it can be useful to print it using `console.info(context)`.
*/
export interface ExportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
/**
* Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
*/
target: FileSystemItem;
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
/**
* The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
*/
fileExtensions?: string[];
/**
* Called when the export process starts.
*/
onInit(context: ExportContext): Promise<void>;
/**
* Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
*/
onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;
/**
* Called when a resource file needs to be exported.
*/
onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;
/**
* Called when the export process is done.
*/
onClose(context: ExportContext): Promise<void>;
}
export interface ImportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
/**
* The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
*/
sources: FileSystemItem[];
/**
* Tells the file extensions of the exported files.
*/
fileExtensions?: string[];
/**
* Tells the type of notes that will be generated, either HTML or Markdown (default).
*/
outputFormat?: ImportModuleOutputFormat;
/**
* Called when the import process starts. There is only one event handler within which you should import the complete data.
*/
onExec(context: ImportContext): Promise<void>;
}
export interface ExportOptions {
format?: string;
path?: string;
sourceFolderIds?: string[];
sourceNoteIds?: string[];
// modulePath?: string;
target?: FileSystemItem;
}
export interface ExportContext {
destPath: string;
options: ExportOptions;
/**
* You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next.
*/
userData?: any;
}
export interface ImportContext {
sourcePath: string;
options: any;
warnings: string[];
}
// =================================================================
// Misc types
// =================================================================
export interface Script {
onStart?(event: any): Promise<void>;
}
export interface Disposable {
// dispose():void;
}
export enum ModelType {
Note = 1,
Folder = 2,
Setting = 3,
Resource = 4,
Tag = 5,
NoteTag = 6,
Search = 7,
Alarm = 8,
MasterKey = 9,
ItemChange = 10,
NoteResource = 11,
ResourceLocalState = 12,
Revision = 13,
Migration = 14,
SmartFilter = 15,
Command = 16,
}
// =================================================================
// Menu types
// =================================================================
export interface CreateMenuItemOptions {
accelerator: string;
}
export enum MenuItemLocation {
File = 'file',
Edit = 'edit',
View = 'view',
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
Context = 'context',
// If adding an item here, don't forget to update isContextMenuItemLocation()
/**
* When a command is called from the note list context menu, the
* command will receive the following arguments:
*
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
* When a command is called from a folder context menu, the
* command will receive the following arguments:
*
* - `folderId:string`: ID of the folder that was right-clicked on
*/
FolderContextMenu = 'folderContextMenu',
/**
* When a command is called from a tag context menu, the
* command will receive the following arguments:
*
* - `tagId:string`: ID of the tag that was right-clicked on
*/
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,
MenuItemLocation.EditorContextMenu,
MenuItemLocation.FolderContextMenu,
MenuItemLocation.TagContextMenu,
].includes(location);
}
export interface MenuItem {
/**
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string;
/**
* Arguments that should be passed to the command. They will be as rest
* parameters.
*/
commandArgs?: any[];
/**
* Set to "separator" to create a divider line
*/
type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio');
/**
* Accelerator associated with the menu item
*/
accelerator?: string;
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[];
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string;
}
// =================================================================
// View API types
// =================================================================
export interface ButtonSpec {
id: ButtonId;
title?: string;
onClick?(): void;
}
export type ButtonId = string;
export enum ToolbarButtonLocation {
/**
* This toolbar in the top right corner of the application. It applies to the note as a whole, including its metadata.
*/
NoteToolbar = 'noteToolbar',
/**
* This toolbar is right above the text editor. It applies to the note body only.
*/
EditorToolbar = 'editorToolbar',
}
export type ViewHandle = string;
export interface EditorCommand {
name: string;
value?: any;
}
export interface DialogResult {
id: ButtonId;
formData?: any;
}
// =================================================================
// Settings types
// =================================================================
export enum SettingItemType {
Int = 1,
String = 2,
Bool = 3,
Array = 4,
Object = 5,
Button = 6,
}
export enum SettingItemSubType {
FilePathAndArgs = 'file_path_and_args',
FilePath = 'file_path', // Not supported on mobile!
DirectoryPath = 'directory_path', // Not supported on mobile!
}
export enum AppType {
Desktop = 'desktop',
Mobile = 'mobile',
Cli = 'cli',
}
export enum SettingStorage {
Database = 1,
File = 2,
}
// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
value: any;
type: SettingItemType;
/**
* Currently only used to display a file or directory selector. Always set
* `type` to `SettingItemType.String` when using this property.
*/
subType?: SettingItemSubType;
label: string;
description?: string;
/**
* A public setting will appear in the Configuration screen and will be
* modifiable by the user. A private setting however will not appear there,
* and can only be changed programmatically. You may use this to store some
* values that you do not want to directly expose.
*/
public: boolean;
/**
* You would usually set this to a section you would have created
* specifically for the plugin.
*/
section?: string;
/**
* To create a setting with multiple options, set this property to `true`.
* That setting will render as a dropdown list in the configuration screen.
*/
isEnum?: boolean;
/**
* This property is required when `isEnum` is `true`. In which case, it
* should contain a map of value => label.
*/
options?: Record<any, any>;
/**
* Reserved property. Not used at the moment.
*/
appTypes?: AppType[];
/**
* Set this to `true` to store secure data, such as passwords. Any such
* setting will be stored in the system keychain if one is available.
*/
secure?: boolean;
/**
* An advanced setting will be moved under the "Advanced" button in the
* config screen.
*/
advanced?: boolean;
/**
* Set the min, max and step values if you want to restrict an int setting
* to a particular range.
*/
minimum?: number;
maximum?: number;
step?: number;
/**
* Either store the setting in the database or in settings.json. Defaults to database.
*/
storage?: SettingStorage;
}
export interface SettingSection {
label: string;
iconName?: string;
description?: string;
name?: string;
}
// =================================================================
// Data API types
// =================================================================
/**
* An array of at least one element and at most three elements.
*
* - **[0]**: Resource name (eg. "notes", "folders", "tags", etc.)
* - **[1]**: (Optional) Resource ID.
* - **[2]**: (Optional) Resource link.
*/
export type Path = string[];
// =================================================================
// Content Script types
// =================================================================
export type PostMessageHandler = (message: any)=> Promise<any>;
/**
* When a content script is initialised, it receives a `context` object.
*/
export interface ContentScriptContext {
/**
* The plugin ID that registered this content script
*/
pluginId: string;
/**
* The content script ID, which may be necessary to post messages
*/
contentScriptId: string;
/**
* Can be used by CodeMirror content scripts to post a message to the plugin
*/
postMessage: PostMessageHandler;
}
export enum ContentScriptType {
/**
* Registers a new Markdown-It plugin, which should follow the template
* below.
*
* ```javascript
* module.exports = {
* default: function(context) {
* return {
* plugin: function(markdownIt, options) {
* // ...
* },
* assets: {
* // ...
* },
* }
* }
* }
* ```
* See [the
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* for a simple Markdown-it plugin example.
*
* ## Exported members
*
* - The `context` argument is currently unused but could be used later on
* to provide access to your own plugin so that the content script and
* plugin can communicate.
*
* - The **required** `plugin` key is the actual Markdown-It plugin - check
* the [official doc](https://github.com/markdown-it/markdown-it) for more
* information. The `options` parameter is of type
* [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml.ts),
* which contains a number of options, mostly useful for Joplin's internal
* code.
*
* - Using the **optional** `assets` key you may specify assets such as JS
* or CSS that should be loaded in the rendered HTML document. Check for
* example the Joplin [Mermaid
* plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
* to see how the data should be structured.
*
* ## Posting messages from the content script to your plugin
*
* The application provides the following function to allow executing
* commands from the rendered HTML code:
*
* ```javascript
* const response = await webviewApi.postMessage(contentScriptId, message);
* ```
*
* - `contentScriptId` is the ID you've defined when you registered the
* content script. You can retrieve it from the
* {@link ContentScriptContext | context}.
* - `message` can be any basic JavaScript type (number, string, plain
* object), but it cannot be a function or class instance.
*
* When you post a message, the plugin can send back a `response` thus
* allowing two-way communication:
*
* ```javascript
* await joplin.contentScripts.onMessage(contentScriptId, (message) => {
* // Process message
* return response; // Can be any object, string or number
* });
* ```
*
* See {@link JoplinContentScripts.onMessage} for more details, as well as
* the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
*
* ## Registering an existing Markdown-it plugin
*
* To include a regular Markdown-It plugin, that doesn't make use of any
* Joplin-specific features, you would simply create a file such as this:
*
* ```javascript
* module.exports = {
* default: function(context) {
* return {
* plugin: require('markdown-it-toc-done-right');
* }
* }
* }
* ```
*/
MarkdownItPlugin = 'markdownItPlugin',
/**
* Registers a new CodeMirror plugin, which should follow the template
* below.
*
* ```javascript
* module.exports = {
* default: function(context) {
* return {
* plugin: function(CodeMirror) {
* // ...
* },
* codeMirrorResources: [],
* codeMirrorOptions: {
* // ...
* },
* assets: {
* // ...
* },
* }
* }
* }
* ```
*
* - The `context` argument is currently unused but could be used later on
* to provide access to your own plugin so that the content script and
* plugin can communicate.
*
* - The `plugin` key is your CodeMirror plugin. This is where you can
* register new commands with CodeMirror or interact with the CodeMirror
* instance as needed.
*
* - The `codeMirrorResources` key is an array of CodeMirror resources that
* will be loaded and attached to the CodeMirror module. These are made up
* of addons, keymaps, and modes. For example, for a plugin that want's to
* enable clojure highlighting in code blocks. `codeMirrorResources` would
* be set to `['mode/clojure/clojure']`.
*
* - The `codeMirrorOptions` key contains all the
* [CodeMirror](https://codemirror.net/doc/manual.html#config) options
* that will be set or changed by this plugin. New options can alse be
* declared via
* [`CodeMirror.defineOption`](https://codemirror.net/doc/manual.html#defineOption),
* and then have their value set here. For example, a plugin that enables
* line numbers would set `codeMirrorOptions` to `{'lineNumbers': true}`.
*
* - Using the **optional** `assets` key you may specify **only** CSS assets
* that should be loaded in the rendered HTML document. Check for example
* the Joplin [Mermaid
* plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
* to see how the data should be structured.
*
* One of the `plugin`, `codeMirrorResources`, or `codeMirrorOptions` keys
* must be provided for the plugin to be valid. Having multiple or all
* provided is also okay.
*
* See also the [demo
* plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
* for an example of all these keys being used in one plugin.
*
* ## Posting messages from the content script to your plugin
*
* In order to post messages to the plugin, you can use the postMessage
* function passed to the {@link ContentScriptContext | context}.
*
* ```javascript
* const response = await context.postMessage('messageFromCodeMirrorContentScript');
* ```
*
* When you post a message, the plugin can send back a `response` thus
* allowing two-way communication:
*
* ```javascript
* await joplin.contentScripts.onMessage(contentScriptId, (message) => {
* // Process message
* return response; // Can be any object, string or number
* });
* ```
*
* See {@link JoplinContentScripts.onMessage} for more details, as well as
* the [postMessage
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
*
*/
CodeMirrorPlugin = 'codeMirrorPlugin',
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
{
"name": "joplin-plugin-userdata-demo",
"version": "1.0.0",
"scripts": {
"dist": "webpack --joplin-plugin-config buildMain && webpack --joplin-plugin-config buildExtraScripts && webpack --joplin-plugin-config createArchive",
"prepare": "npm run dist",
"update": "npm install -g generator-joplin && yo joplin --update"
},
"license": "MIT",
"keywords": [
"joplin-plugin"
],
"devDependencies": {
"@types/node": "^14.0.14",
"chalk": "^4.1.0",
"copy-webpack-plugin": "^6.1.0",
"fs-extra": "^9.0.1",
"glob": "^7.1.6",
"on-build-webpack": "^0.1.0",
"tar": "^6.0.5",
"ts-loader": "^7.0.5",
"typescript": "^3.9.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"yargs": "^16.2.0"
},
"files": [
"publish"
]
}

View File

@@ -0,0 +1,3 @@
{
"extraScripts": []
}

View File

@@ -0,0 +1,17 @@
import joplin from 'api';
import { ModelType } from 'api/types';
joplin.plugins.register({
onStart: async function() {
const folder = await joplin.data.post(['folders'], null, { title: "test" });
const note = await joplin.data.post(['notes'], null, { title: "test", parent_id: folder.id });
await joplin.data.userDataSet(ModelType.Note, note.id, 'mykey', 'abcd');
console.info('Got back user data:', await joplin.data.userDataGet(ModelType.Note, note.id, 'mykey'));
await joplin.data.userDataDelete(ModelType.Note, note.id, 'mykey');
console.info('Got back user data:', await joplin.data.userDataGet(ModelType.Note, note.id, 'mykey'));
},
});

View File

@@ -0,0 +1,12 @@
{
"manifest_version": 1,
"id": "org.joplinapp.plugins.UserDataDemo",
"app_min_version": "2.0",
"version": "1.0.0",
"name": "User Data Demo",
"description": "",
"author": "",
"homepage_url": "",
"repository_url": "",
"keywords": []
}

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"outDir": "./dist/",
"module": "commonjs",
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -0,0 +1,291 @@
// -----------------------------------------------------------------------------
// This file is used to build the plugin file (.jpl) and plugin info (.json). It
// is recommended not to edit this file as it would be overwritten when updating
// the plugin framework. If you do make some changes, consider using an external
// JS file and requiring it here to minimize the changes. That way when you
// update, you can easily restore the functionality you've added.
// -----------------------------------------------------------------------------
const path = require('path');
const crypto = require('crypto');
const fs = require('fs-extra');
const chalk = require('chalk');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackOnBuildPlugin = require('on-build-webpack');
const tar = require('tar');
const glob = require('glob');
const execSync = require('child_process').execSync;
const rootDir = path.resolve(__dirname);
const userConfigFilename = './plugin.config.json';
const userConfigPath = path.resolve(rootDir, userConfigFilename);
const distDir = path.resolve(rootDir, 'dist');
const srcDir = path.resolve(rootDir, 'src');
const publishDir = path.resolve(rootDir, 'publish');
const userConfig = Object.assign({}, {
extraScripts: [],
}, fs.pathExistsSync(userConfigPath) ? require(userConfigFilename) : {});
const manifestPath = `${srcDir}/manifest.json`;
const packageJsonPath = `${rootDir}/package.json`;
const allPossibleCategories = ['appearance', 'developer tools', 'productivity', 'themes', 'integrations', 'viewer', 'search', 'tags', 'editor', 'files', 'personal knowledge management'];
const manifest = readManifest(manifestPath);
const pluginArchiveFilePath = path.resolve(publishDir, `${manifest.id}.jpl`);
const pluginInfoFilePath = path.resolve(publishDir, `${manifest.id}.json`);
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
if (!content.keywords || content.keywords.indexOf('joplin-plugin') < 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package keywords should include "joplin-plugin" (found "${JSON.stringify(content.keywords)}") in ${packageJsonPath}`));
}
if (content.scripts && content.scripts.postinstall) {
console.warn(chalk.yellow(`WARNING: package.json contains a "postinstall" script. It is recommended to use a "prepare" script instead so that it is executed before publish. In ${packageJsonPath}`));
}
}
function fileSha256(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
}
function currentGitInfo() {
try {
let branch = execSync('git rev-parse --abbrev-ref HEAD', { stdio: 'pipe' }).toString().trim();
const commit = execSync('git rev-parse HEAD', { stdio: 'pipe' }).toString().trim();
if (branch === 'HEAD') branch = 'master';
return `${branch}:${commit}`;
} catch (error) {
const messages = error.message ? error.message.split('\n') : [''];
console.info(chalk.cyan('Could not get git commit (not a git repo?):', messages[0].trim()));
console.info(chalk.cyan('Git information will not be stored in plugin info file'));
return '';
}
}
function validateCategories(categories) {
if (!categories) return null;
if ((categories.length !== new Set(categories).size)) throw new Error('Repeated categories are not allowed');
categories.forEach(category => {
if (!allPossibleCategories.includes(category)) throw new Error(`${category} is not a valid category. Please make sure that the category name is lowercase. Valid Categories are: \n${allPossibleCategories}\n`);
});
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
const output = JSON.parse(content);
if (!output.id) throw new Error(`Manifest plugin ID is not set in ${manifestPath}`);
validateCategories(output.categories);
return output;
}
function createPluginArchive(sourceDir, destPath) {
const distFiles = glob.sync(`${sourceDir}/**/*`, { nodir: true })
.map(f => f.substr(sourceDir.length + 1));
if (!distFiles.length) throw new Error('Plugin archive was not created because the "dist" directory is empty');
fs.removeSync(destPath);
tar.create(
{
strict: true,
portable: true,
file: destPath,
cwd: sourceDir,
sync: true,
},
distFiles
);
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
}
function onBuildCompleted() {
try {
fs.removeSync(path.resolve(publishDir, 'index.js'));
createPluginArchive(distDir, pluginArchiveFilePath);
createPluginInfo(manifestPath, pluginInfoFilePath, pluginArchiveFilePath);
validatePackageJson();
} catch (error) {
console.error(chalk.red(error.message));
}
}
const baseConfig = {
mode: 'production',
target: 'node',
stats: 'errors-only',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
};
const pluginConfig = Object.assign({}, baseConfig, {
entry: './src/index.ts',
resolve: {
alias: {
api: path.resolve(__dirname, 'api'),
},
// JSON files can also be required from scripts so we include this.
// https://github.com/joplin/plugin-bibtex/pull/2
extensions: ['.js', '.tsx', '.ts', '.json'],
},
output: {
filename: 'index.js',
path: distDir,
},
plugins: [
new CopyPlugin({
patterns: [
{
from: '**/*',
context: path.resolve(__dirname, 'src'),
to: path.resolve(__dirname, 'dist'),
globOptions: {
ignore: [
// All TypeScript files are compiled to JS and
// already copied into /dist so we don't copy them.
'**/*.ts',
'**/*.tsx',
],
},
},
],
}),
],
});
const extraScriptConfig = Object.assign({}, baseConfig, {
resolve: {
alias: {
api: path.resolve(__dirname, 'api'),
},
extensions: ['.js', '.tsx', '.ts', '.json'],
},
});
const createArchiveConfig = {
stats: 'errors-only',
entry: './dist/index.js',
output: {
filename: 'index.js',
path: publishDir,
},
plugins: [new WebpackOnBuildPlugin(onBuildCompleted)],
};
function resolveExtraScriptPath(name) {
const relativePath = `./src/${name}`;
const fullPath = path.resolve(`${rootDir}/${relativePath}`);
if (!fs.pathExistsSync(fullPath)) throw new Error(`Could not find extra script: "${name}" at "${fullPath}"`);
const s = name.split('.');
s.pop();
const nameNoExt = s.join('.');
return {
entry: relativePath,
output: {
filename: `${nameNoExt}.js`,
path: distDir,
library: 'default',
libraryTarget: 'commonjs',
libraryExport: 'default',
},
};
}
function buildExtraScriptConfigs(userConfig) {
if (!userConfig.extraScripts.length) return [];
const output = [];
for (const scriptName of userConfig.extraScripts) {
const scriptPaths = resolveExtraScriptPath(scriptName);
output.push(Object.assign({}, extraScriptConfig, {
entry: scriptPaths.entry,
output: scriptPaths.output,
}));
}
return output;
}
function main(processArgv) {
const yargs = require('yargs/yargs');
const argv = yargs(processArgv).argv;
const configName = argv['joplin-plugin-config'];
if (!configName) throw new Error('A config file must be specified via the --joplin-plugin-config flag');
// Webpack configurations run in parallel, while we need them to run in
// sequence, and to do that it seems the only way is to run webpack multiple
// times, with different config each time.
const configs = {
// Builds the main src/index.ts and copy the extra content from /src to
// /dist including scripts, CSS and any other asset.
buildMain: [pluginConfig],
// Builds the extra scripts as defined in plugin.config.json. When doing
// so, some JavaScript files that were copied in the previous might be
// overwritten here by the compiled version. This is by design. The
// result is that JS files that don't need compilation, are simply
// copied to /dist, while those that do need it are correctly compiled.
buildExtraScripts: buildExtraScriptConfigs(userConfig),
// Ths config is for creating the .jpl, which is done via the plugin, so
// it doesn't actually need an entry and output, however webpack won't
// run without this. So we give it an entry that we know is going to
// exist and output in the publish dir. Then the plugin will delete this
// temporary file before packaging the plugin.
createArchive: [createArchiveConfig],
};
// If we are running the first config step, we clean up and create the build
// directories.
if (configName === 'buildMain') {
fs.removeSync(distDir);
fs.removeSync(publishDir);
fs.mkdirpSync(publishDir);
}
return configs[configName];
}
let exportedConfigs = [];
try {
exportedConfigs = main(process.argv);
} catch (error) {
console.error(chalk.red(error.message));
process.exit(1);
}
if (!exportedConfigs.length) {
// Nothing to do - for example where there are no external scripts to
// compile.
process.exit(0);
}
module.exports = exportedConfigs;

View File

@@ -5,6 +5,9 @@
"target": "es2015",
"jsx": "react",
"allowJs": true,
"baseUrl": "."
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types"
]
}
}

View File

@@ -94,12 +94,12 @@ browser_.runtime.onMessage.addListener(async (command) => {
const imageSize = await getImageSize(imageDataUrl);
const imagePixelRatio = imageSize.width / command.content.windowInnerWidth;
const content = Object.assign({}, command.content);
const content = { ...command.content };
content.image_data_url = imageDataUrl;
if ('url' in content) content.source_url = content.url;
const ratio = browserZoom * imagePixelRatio;
const newArea = Object.assign({}, command.content.crop_rect);
const newArea = { ...command.content.crop_rect };
newArea.x *= ratio;
newArea.y *= ratio;
newArea.width *= ratio;

View File

@@ -378,7 +378,7 @@
tags: command.tags || '',
image_sizes: imageSizes,
anchor_names: anchorNames,
source_command: Object.assign({}, command),
source_command: { ...command },
convert_to: convertToMarkup,
stylesheets: stylesheets,
};
@@ -392,7 +392,7 @@
} catch (error) {
console.warn(error);
console.warn('Sending full page HTML instead');
const newCommand = Object.assign({}, command, { name: 'completePageHtml' });
const newCommand = { ...command, name: 'completePageHtml' };
const response = await prepareCommandResponse(newCommand);
response.warning = 'Could not retrieve simplified version of page - full page has been saved instead.';
return response;

View File

@@ -67,7 +67,7 @@ class AppComponent extends Component {
});
this.confirm_click = async () => {
const content = Object.assign({}, this.props.clippedContent);
const content = { ...this.props.clippedContent };
content.tags = this.state.selectedTags.join(',');
content.parent_id = this.props.selectedFolderId;
const response = await bridge().sendContentToJoplin(content);

View File

@@ -410,7 +410,7 @@ class Bridge {
if (body) fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
query = Object.assign(query || {}, { token: this.token_ });
query = { ...query, token: this.token_ };
let queryString = '';
if (query) {

View File

@@ -40,34 +40,34 @@ function reducer(state = defaultState, action) {
if (action.type === 'WARNING_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.warning = action.text;
} else if (action.type === 'IS_PROBABLY_READERABLE') {
newState = Object.assign({}, state);
newState = { ...state };
newState.isProbablyReaderable = action.value;
} else if (action.type === 'CLIPPED_CONTENT_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.clippedContent = action.content;
} else if (action.type === 'CLIPPED_CONTENT_TITLE_SET') {
newState = Object.assign({}, state);
const newContent = newState.clippedContent ? Object.assign({}, newState.clippedContent) : {};
newState = { ...state };
const newContent = newState.clippedContent ? { ...newState.clippedContent } : {};
newContent.title = action.text;
newState.clippedContent = newContent;
} else if (action.type === 'CONTENT_UPLOAD') {
newState = Object.assign({}, state);
newState = { ...state };
newState.contentUploadOperation = action.operation;
} else if (action.type === 'FOLDERS_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.folders = action.folders;
if (!newState.selectedFolderId && action.folders.length) {
@@ -76,30 +76,30 @@ function reducer(state = defaultState, action) {
} else if (action.type === 'TAGS_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.tags = action.tags;
} else if (action.type === 'SELECTED_FOLDER_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.selectedFolderId = action.id;
} else if (action.type === 'CLIPPER_SERVER_SET') {
newState = Object.assign({}, state);
const clipperServer = Object.assign({}, newState.clipperServer);
newState = { ...state };
const clipperServer = { ...newState.clipperServer };
if ('foundState' in action) clipperServer.foundState = action.foundState;
if ('port' in action) clipperServer.port = action.port;
newState.clipperServer = clipperServer;
} else if (action.type === 'ENV_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.env = action.env;
} else if (action.type === 'AUTH_STATE_SET') {
newState = Object.assign({}, state);
newState = { ...state };
newState.authStatus = action.value;
}

View File

@@ -29,13 +29,11 @@ export default class InteropServiceHelper {
private static async exportNoteToHtmlFile(noteId: string, exportOptions: ExportNoteOptions) {
const tempFile = `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}.html`;
const fullExportOptions: ExportOptions = Object.assign({}, {
path: tempFile,
const fullExportOptions: ExportOptions = { path: tempFile,
format: 'html',
target: FileSystemItem.File,
sourceNoteIds: [noteId],
customCss: '',
}, exportOptions);
customCss: '', ...exportOptions };
const service = InteropService.instance();

View File

@@ -4,6 +4,9 @@ import { defaultState, State } from '@joplin/lib/reducer';
import iterateItems from './gui/ResizableLayout/utils/iterateItems';
import { LayoutItem } from './gui/ResizableLayout/utils/types';
import validateLayout from './gui/ResizableLayout/utils/validateLayout';
import Logger from '@joplin/lib/Logger';
const logger = Logger.create('app.reducer');
export interface AppStateRoute {
type: string;
@@ -82,7 +85,7 @@ export default function(state: AppState, action: any) {
const currentRoute = state.route;
newState = Object.assign({}, state);
newState = { ...state };
const newNavHistory = state.navHistory.slice();
if (goingBack) {
@@ -119,7 +122,7 @@ export default function(state: AppState, action: any) {
case 'WINDOW_CONTENT_SIZE_SET':
newState = Object.assign({}, state);
newState = { ...state };
newState.windowContentSize = action.size;
break;
@@ -147,7 +150,7 @@ export default function(state: AppState, action: any) {
return nextLayout === 'both' ? ['editor', 'viewer'] : [nextLayout];
};
newState = Object.assign({}, state);
newState = { ...state };
const panes = state.noteVisiblePanes.slice();
newState.noteVisiblePanes = getNextLayout(panes);
@@ -156,7 +159,7 @@ export default function(state: AppState, action: any) {
case 'NOTE_VISIBLE_PANES_SET':
newState = Object.assign({}, state);
newState = { ...state };
newState.noteVisiblePanes = action.panes;
break;
@@ -171,22 +174,31 @@ export default function(state: AppState, action: any) {
case 'MAIN_LAYOUT_SET_ITEM_PROP':
{
let newLayout = produce(state.mainLayout, (draftLayout: LayoutItem) => {
iterateItems(draftLayout, (_itemIndex: number, item: LayoutItem, _parent: LayoutItem) => {
if (item.key === action.itemKey) {
(item as any)[action.propName] = action.propValue;
return false;
}
return true;
if (!state.mainLayout) {
logger.warn('MAIN_LAYOUT_SET_ITEM_PROP: Trying to set an item prop on the layout, but layout is empty: ', JSON.stringify(action));
} else {
let newLayout = produce(state.mainLayout, (draftLayout: LayoutItem) => {
iterateItems(draftLayout, (_itemIndex: number, item: LayoutItem, _parent: LayoutItem) => {
if (!item) {
logger.warn('MAIN_LAYOUT_SET_ITEM_PROP: Found an empty item in layout: ', JSON.stringify(state.mainLayout));
} else {
if (item.key === action.itemKey) {
(item as any)[action.propName] = action.propValue;
return false;
}
}
return true;
});
});
});
if (newLayout !== state.mainLayout) newLayout = validateLayout(newLayout);
if (newLayout !== state.mainLayout) newLayout = validateLayout(newLayout);
newState = {
...state,
mainLayout: newLayout,
};
newState = {
...state,
mainLayout: newLayout,
};
}
}
break;
@@ -194,7 +206,7 @@ export default function(state: AppState, action: any) {
case 'NOTE_FILE_WATCHER_ADD':
if (newState.watchedNoteFiles.indexOf(action.id) < 0) {
newState = Object.assign({}, state);
newState = { ...state };
const watchedNoteFiles = newState.watchedNoteFiles.slice();
watchedNoteFiles.push(action.id);
newState.watchedNoteFiles = watchedNoteFiles;
@@ -204,7 +216,7 @@ export default function(state: AppState, action: any) {
case 'NOTE_FILE_WATCHER_REMOVE':
{
newState = Object.assign({}, state);
newState = { ...state };
const idx = newState.watchedNoteFiles.indexOf(action.id);
if (idx >= 0) {
const watchedNoteFiles = newState.watchedNoteFiles.slice();
@@ -217,7 +229,7 @@ export default function(state: AppState, action: any) {
case 'NOTE_FILE_WATCHER_CLEAR':
if (state.watchedNoteFiles.length) {
newState = Object.assign({}, state);
newState = { ...state };
newState.watchedNoteFiles = [];
}
break;
@@ -225,38 +237,38 @@ export default function(state: AppState, action: any) {
case 'EDITOR_SCROLL_PERCENT_SET':
{
newState = Object.assign({}, state);
const newPercents = Object.assign({}, newState.lastEditorScrollPercents);
newState = { ...state };
const newPercents = { ...newState.lastEditorScrollPercents };
newPercents[action.noteId] = action.percent;
newState.lastEditorScrollPercents = newPercents;
}
break;
case 'NOTE_DEVTOOLS_TOGGLE':
newState = Object.assign({}, state);
newState = { ...state };
newState.devToolsVisible = !newState.devToolsVisible;
break;
case 'NOTE_DEVTOOLS_SET':
newState = Object.assign({}, state);
newState = { ...state };
newState.devToolsVisible = action.value;
break;
case 'VISIBLE_DIALOGS_ADD':
newState = Object.assign({}, state);
newState.visibleDialogs = Object.assign({}, newState.visibleDialogs);
newState = { ...state };
newState.visibleDialogs = { ...newState.visibleDialogs };
newState.visibleDialogs[action.name] = true;
break;
case 'VISIBLE_DIALOGS_REMOVE':
newState = Object.assign({}, state);
newState.visibleDialogs = Object.assign({}, newState.visibleDialogs);
newState = { ...state };
newState.visibleDialogs = { ...newState.visibleDialogs };
delete newState.visibleDialogs[action.name];
break;
case 'FOCUS_SET':
newState = Object.assign({}, state);
newState = { ...state };
newState.focusedField = action.field;
break;
@@ -264,7 +276,7 @@ export default function(state: AppState, action: any) {
// A field can only clear its own state
if (action.field === state.focusedField) {
newState = Object.assign({}, state);
newState = { ...state };
newState.focusedField = null;
}
break;
@@ -281,7 +293,7 @@ export default function(state: AppState, action: any) {
isOpen = action.isOpen !== false;
}
newState = Object.assign({}, state);
newState = { ...state };
if (isOpen) {
const newDialogs = newState.dialogs.slice();

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