You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-11 23:17:19 +02:00
Compare commits
34 Commits
test_impro
...
media_play
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02a0c9b1e6 | ||
|
|
1a09f5e1dc | ||
|
|
91cd60a002 | ||
|
|
f436967779 | ||
|
|
f37224fc42 | ||
|
|
a737a21185 | ||
|
|
234c05a966 | ||
|
|
92b4795410 | ||
|
|
3e21e9a334 | ||
|
|
dc3e9178ef | ||
|
|
46ed1304f2 | ||
|
|
01cf0de974 | ||
|
|
abf5d16da9 | ||
|
|
f21199a7f3 | ||
|
|
cdf2873883 | ||
|
|
0390928fea | ||
|
|
efb3546675 | ||
|
|
169841dcc4 | ||
|
|
b780261153 | ||
|
|
c906354510 | ||
|
|
6487fde09d | ||
|
|
50f046ffc4 | ||
|
|
ded14ff8db | ||
|
|
64bbd93f54 | ||
|
|
9a30b69610 | ||
|
|
046433a947 | ||
|
|
42232fac84 | ||
|
|
245976f659 | ||
|
|
8d90cc234f | ||
|
|
5183767e43 | ||
|
|
ceb625dbeb | ||
|
|
7677d9cd7d | ||
|
|
92fe5e2362 | ||
|
|
717b8da1f8 |
@@ -10,6 +10,8 @@ highlight.pack.js
|
||||
Modules/TinyMCE/IconPack/postinstall.js
|
||||
Modules/TinyMCE/JoplinLists/
|
||||
Modules/TinyMCE/langs/
|
||||
packages/turndown/
|
||||
packages/turndown-plugin-gfm/
|
||||
node_modules/
|
||||
packages/lib/lib/lib.js
|
||||
packages/lib/locales/index.js
|
||||
@@ -68,6 +70,9 @@ packages/app-cli/app/services/plugins/PluginRunner.js.map
|
||||
packages/app-cli/tests/EnexToMd.d.ts
|
||||
packages/app-cli/tests/EnexToMd.js
|
||||
packages/app-cli/tests/EnexToMd.js.map
|
||||
packages/app-cli/tests/HtmlToMd.d.ts
|
||||
packages/app-cli/tests/HtmlToMd.js
|
||||
packages/app-cli/tests/HtmlToMd.js.map
|
||||
packages/app-cli/tests/InMemoryCache.d.ts
|
||||
packages/app-cli/tests/InMemoryCache.js
|
||||
packages/app-cli/tests/InMemoryCache.js.map
|
||||
@@ -104,6 +109,9 @@ packages/app-cli/tests/models_Note.js.map
|
||||
packages/app-cli/tests/models_Setting.d.ts
|
||||
packages/app-cli/tests/models_Setting.js
|
||||
packages/app-cli/tests/models_Setting.js.map
|
||||
packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.d.ts
|
||||
packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js
|
||||
packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js.map
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.d.ts
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
|
||||
@@ -263,6 +271,9 @@ packages/app-cli/tests/synchronizer_MigrationHandler.js.map
|
||||
packages/app-cli/tests/test-utils-synchronizer.d.ts
|
||||
packages/app-cli/tests/test-utils-synchronizer.js
|
||||
packages/app-cli/tests/test-utils-synchronizer.js.map
|
||||
packages/app-cli/tests/test-utils.d.ts
|
||||
packages/app-cli/tests/test-utils.js
|
||||
packages/app-cli/tests/test-utils.js.map
|
||||
packages/app-desktop/ElectronAppWrapper.d.ts
|
||||
packages/app-desktop/ElectronAppWrapper.js
|
||||
packages/app-desktop/ElectronAppWrapper.js.map
|
||||
@@ -902,6 +913,9 @@ packages/lib/errorUtils.js.map
|
||||
packages/lib/eventManager.d.ts
|
||||
packages/lib/eventManager.js
|
||||
packages/lib/eventManager.js.map
|
||||
packages/lib/fs-driver-base.d.ts
|
||||
packages/lib/fs-driver-base.js
|
||||
packages/lib/fs-driver-base.js.map
|
||||
packages/lib/fs-driver-node.d.ts
|
||||
packages/lib/fs-driver-node.js
|
||||
packages/lib/fs-driver-node.js.map
|
||||
@@ -959,6 +973,12 @@ packages/lib/services/ExternalEditWatcher.js.map
|
||||
packages/lib/services/KeymapService.d.ts
|
||||
packages/lib/services/KeymapService.js
|
||||
packages/lib/services/KeymapService.js.map
|
||||
packages/lib/services/KeymapService_keycodeToElectronMap.d.ts
|
||||
packages/lib/services/KeymapService_keycodeToElectronMap.js
|
||||
packages/lib/services/KeymapService_keycodeToElectronMap.js.map
|
||||
packages/lib/services/KeymapService_keysRegExp.d.ts
|
||||
packages/lib/services/KeymapService_keysRegExp.js
|
||||
packages/lib/services/KeymapService_keysRegExp.js.map
|
||||
packages/lib/services/KvStore.d.ts
|
||||
packages/lib/services/KvStore.js
|
||||
packages/lib/services/KvStore.js.map
|
||||
@@ -1310,6 +1330,9 @@ packages/renderer/MdToHtml/linkReplacement.js.map
|
||||
packages/renderer/MdToHtml/linkReplacement.test.d.ts
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js.map
|
||||
packages/renderer/MdToHtml/renderMedia.d.ts
|
||||
packages/renderer/MdToHtml/renderMedia.js
|
||||
packages/renderer/MdToHtml/renderMedia.js.map
|
||||
packages/renderer/MdToHtml/rules/checkbox.d.ts
|
||||
packages/renderer/MdToHtml/rules/checkbox.js
|
||||
packages/renderer/MdToHtml/rules/checkbox.js.map
|
||||
@@ -1334,6 +1357,9 @@ packages/renderer/MdToHtml/rules/image.js.map
|
||||
packages/renderer/MdToHtml/rules/katex.d.ts
|
||||
packages/renderer/MdToHtml/rules/katex.js
|
||||
packages/renderer/MdToHtml/rules/katex.js.map
|
||||
packages/renderer/MdToHtml/rules/link_close.d.ts
|
||||
packages/renderer/MdToHtml/rules/link_close.js
|
||||
packages/renderer/MdToHtml/rules/link_close.js.map
|
||||
packages/renderer/MdToHtml/rules/link_open.d.ts
|
||||
packages/renderer/MdToHtml/rules/link_open.js
|
||||
packages/renderer/MdToHtml/rules/link_open.js.map
|
||||
|
||||
@@ -24,6 +24,7 @@ module.exports = {
|
||||
'afterAll': 'readonly',
|
||||
'beforeEach': 'readonly',
|
||||
'afterEach': 'readonly',
|
||||
'jest': 'readonly',
|
||||
|
||||
// React Native variables
|
||||
'__DEV__': 'readonly',
|
||||
|
||||
26
.gitignore
vendored
26
.gitignore
vendored
@@ -59,6 +59,9 @@ packages/app-cli/app/services/plugins/PluginRunner.js.map
|
||||
packages/app-cli/tests/EnexToMd.d.ts
|
||||
packages/app-cli/tests/EnexToMd.js
|
||||
packages/app-cli/tests/EnexToMd.js.map
|
||||
packages/app-cli/tests/HtmlToMd.d.ts
|
||||
packages/app-cli/tests/HtmlToMd.js
|
||||
packages/app-cli/tests/HtmlToMd.js.map
|
||||
packages/app-cli/tests/InMemoryCache.d.ts
|
||||
packages/app-cli/tests/InMemoryCache.js
|
||||
packages/app-cli/tests/InMemoryCache.js.map
|
||||
@@ -95,6 +98,9 @@ packages/app-cli/tests/models_Note.js.map
|
||||
packages/app-cli/tests/models_Setting.d.ts
|
||||
packages/app-cli/tests/models_Setting.js
|
||||
packages/app-cli/tests/models_Setting.js.map
|
||||
packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.d.ts
|
||||
packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js
|
||||
packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js.map
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.d.ts
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js
|
||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
|
||||
@@ -254,6 +260,9 @@ packages/app-cli/tests/synchronizer_MigrationHandler.js.map
|
||||
packages/app-cli/tests/test-utils-synchronizer.d.ts
|
||||
packages/app-cli/tests/test-utils-synchronizer.js
|
||||
packages/app-cli/tests/test-utils-synchronizer.js.map
|
||||
packages/app-cli/tests/test-utils.d.ts
|
||||
packages/app-cli/tests/test-utils.js
|
||||
packages/app-cli/tests/test-utils.js.map
|
||||
packages/app-desktop/ElectronAppWrapper.d.ts
|
||||
packages/app-desktop/ElectronAppWrapper.js
|
||||
packages/app-desktop/ElectronAppWrapper.js.map
|
||||
@@ -893,6 +902,9 @@ packages/lib/errorUtils.js.map
|
||||
packages/lib/eventManager.d.ts
|
||||
packages/lib/eventManager.js
|
||||
packages/lib/eventManager.js.map
|
||||
packages/lib/fs-driver-base.d.ts
|
||||
packages/lib/fs-driver-base.js
|
||||
packages/lib/fs-driver-base.js.map
|
||||
packages/lib/fs-driver-node.d.ts
|
||||
packages/lib/fs-driver-node.js
|
||||
packages/lib/fs-driver-node.js.map
|
||||
@@ -950,6 +962,12 @@ packages/lib/services/ExternalEditWatcher.js.map
|
||||
packages/lib/services/KeymapService.d.ts
|
||||
packages/lib/services/KeymapService.js
|
||||
packages/lib/services/KeymapService.js.map
|
||||
packages/lib/services/KeymapService_keycodeToElectronMap.d.ts
|
||||
packages/lib/services/KeymapService_keycodeToElectronMap.js
|
||||
packages/lib/services/KeymapService_keycodeToElectronMap.js.map
|
||||
packages/lib/services/KeymapService_keysRegExp.d.ts
|
||||
packages/lib/services/KeymapService_keysRegExp.js
|
||||
packages/lib/services/KeymapService_keysRegExp.js.map
|
||||
packages/lib/services/KvStore.d.ts
|
||||
packages/lib/services/KvStore.js
|
||||
packages/lib/services/KvStore.js.map
|
||||
@@ -1301,6 +1319,9 @@ packages/renderer/MdToHtml/linkReplacement.js.map
|
||||
packages/renderer/MdToHtml/linkReplacement.test.d.ts
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js
|
||||
packages/renderer/MdToHtml/linkReplacement.test.js.map
|
||||
packages/renderer/MdToHtml/renderMedia.d.ts
|
||||
packages/renderer/MdToHtml/renderMedia.js
|
||||
packages/renderer/MdToHtml/renderMedia.js.map
|
||||
packages/renderer/MdToHtml/rules/checkbox.d.ts
|
||||
packages/renderer/MdToHtml/rules/checkbox.js
|
||||
packages/renderer/MdToHtml/rules/checkbox.js.map
|
||||
@@ -1325,6 +1346,9 @@ packages/renderer/MdToHtml/rules/image.js.map
|
||||
packages/renderer/MdToHtml/rules/katex.d.ts
|
||||
packages/renderer/MdToHtml/rules/katex.js
|
||||
packages/renderer/MdToHtml/rules/katex.js.map
|
||||
packages/renderer/MdToHtml/rules/link_close.d.ts
|
||||
packages/renderer/MdToHtml/rules/link_close.js
|
||||
packages/renderer/MdToHtml/rules/link_close.js.map
|
||||
packages/renderer/MdToHtml/rules/link_open.d.ts
|
||||
packages/renderer/MdToHtml/rules/link_open.js
|
||||
packages/renderer/MdToHtml/rules/link_open.js.map
|
||||
@@ -1349,4 +1373,4 @@ packages/renderer/pathUtils.js.map
|
||||
packages/renderer/utils.d.ts
|
||||
packages/renderer/utils.js
|
||||
packages/renderer/utils.js.map
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
|
||||
78
README.md
78
README.md
@@ -84,6 +84,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
|
||||
- [How to enable end-to-end encryption](https://github.com/laurent22/joplin/blob/dev/readme/e2ee.md)
|
||||
- [What is a conflict?](https://github.com/laurent22/joplin/blob/dev/readme/conflict.md)
|
||||
- [How to enable debug mode](https://github.com/laurent22/joplin/blob/dev/readme/debugging.md)
|
||||
- [About the Rich Text editor limitations](https://github.com/laurent22/joplin/blob/dev/readme/rich_text_editor.md)
|
||||
- [FAQ](https://github.com/laurent22/joplin/blob/dev/readme/faq.md)
|
||||
|
||||
- Joplin API - Get Started
|
||||
@@ -435,44 +436,45 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
 | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/ar.po) | [أحمد باشا إبراهيم](mailto:fi_ahmed_bacha@esi.dz) | 78%
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/eu.po) | juan.abasolo@ehu.eus | 33%
|
||||
 | Bosnian | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 82%
|
||||
 | Bulgarian | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/bg_BG.po) | | 65%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/ca.po) | jmontane, 2019 | 95%
|
||||
 | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/hr_HR.po) | [Hrvoje Mandić](mailto:trbuhom@net.hr) | 27%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/cs_CZ.po) | [Lukas Helebrandt](mailto:lukas@aiya.cz) | 98%
|
||||
 | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/da_DK.po) | [Morten Juhl-Johansen Zölde-Fejér](mailto:mjjzf@syntaktisk.) | 72%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/de_DE.po) | [Ettore Atalan](mailto:atalanttore@users.noreply.github.com) | 97%
|
||||
 | Eesti Keel | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/et_EE.po) | | 65%
|
||||
 | English (UK) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/en_GB.po) | | 100%
|
||||
 | English (US) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/en_US.po) | | 100%
|
||||
 | Español | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/es_ES.po) | [Mario Campo](mailto:mario.campo@gmail.com) | 98%
|
||||
 | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/eo.po) | Marton Paulo | 37%
|
||||
 | Français | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/fr_FR.po) | Laurent Cozic | 98%
|
||||
 | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 42%
|
||||
 | Indonesian | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/id_ID.po) | [Fathy AR](mailto:16875937+fathyar@users.noreply.github.com) | 92%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/it_IT.po) | [Alessandro Bernardello](mailto:mailfilledwithspam@gmail.com) | 98%
|
||||
 | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 94%
|
||||
 | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/nl_BE.po) | | 33%
|
||||
 | Norwegian | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 87%
|
||||
 | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 82%
|
||||
 | Polski | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/pl_PL.po) | | 97%
|
||||
 | Português | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/pt_PT.po) | [João Duarte](mailto:jduar@protonmail.com) | 98%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/pt_BR.po) | [Renato Nunes Bastos](mailto:rnbastos@gmail.com) | 96%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 77%
|
||||
 | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/sl_SI.po) | | 41%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 70%
|
||||
 | Thai | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/th_TH.po) | | 52%
|
||||
 | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/vi.po) | | 84%
|
||||
 | Türkçe | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 97%
|
||||
 | Ελληνικά | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 95%
|
||||
 | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 94%
|
||||
 | српски језик | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/sr_RS.po) | | 70%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/zh_CN.po) | [WhiredPlanck](mailto:fungdaat31@outlook.com) | 95%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/zh_TW.po) | [Yaoze Ye](mailto:yaozeye@yahoo.co.jp) | 94%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 99%
|
||||
 | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 99%
|
||||
 | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [أحمد باشا إبراهيم](mailto:fi_ahmed_bacha@esi.dz) | 77%
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 33%
|
||||
 | 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) | 79%
|
||||
 | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 64%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | jmontane, 2019 | 92%
|
||||
 | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Hrvoje Mandić](mailto:trbuhom@net.hr) | 26%
|
||||
 | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Lukas Helebrandt](mailto:lukas@aiya.cz) | 96%
|
||||
 | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | Mustafa Al-Dailemi (dailemi@hotmail.com)Language-Team: | 79%
|
||||
 | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Ettore Atalan](mailto:atalanttore@users.noreply.github.com) | 96%
|
||||
 | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 63%
|
||||
 | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
|
||||
 | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
|
||||
 | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Mario Campo](mailto:mario.campo@gmail.com) | 96%
|
||||
 | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 36%
|
||||
 | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | | 97%
|
||||
 | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 96%
|
||||
 | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 42%
|
||||
 | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Fathy AR](mailto:16875937+fathyar@users.noreply.github.com) | 88%
|
||||
 | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Alessandro Bernardello](mailto:mailfilledwithspam@gmail.com) | 97%
|
||||
 | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 33%
|
||||
 | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 96%
|
||||
 | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 85%
|
||||
 | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 79%
|
||||
 | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | | 95%
|
||||
 | 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) | 93%
|
||||
 | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [João Duarte](mailto:jduar@protonmail.com) | 95%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 74%
|
||||
 | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | | 41%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 68%
|
||||
 | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 50%
|
||||
 | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 82%
|
||||
 | 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) | 94%
|
||||
 | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 92%
|
||||
 | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 92%
|
||||
 | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 69%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [WhiredPlanck](mailto:fungdaat31@outlook.com) | 97%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Yaoze Ye](mailto:yaozeye@yahoo.co.jp) | 91%
|
||||
 | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 97%
|
||||
 | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 97%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Contributors
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"buildDoc": "./packages/tools/build-all.sh",
|
||||
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/api/references/plugin_api packages/lib/services/plugins/api/",
|
||||
"buildTranslations": "npm run tsc && node packages/tools/build-translation.js",
|
||||
"buildTranslationsNoTsc": "node packages/tools/build-translation.js",
|
||||
"buildWebsite": "npm run buildApiDoc && node ./packages/tools/build-website.js && npm run buildPluginDoc",
|
||||
"clean": "lerna clean -y && lerna run clean",
|
||||
"generateDatabaseTypes": "node packages/tools/generate-database-types",
|
||||
|
||||
@@ -54,4 +54,5 @@ module.exports = {
|
||||
|
||||
testEnvironment: 'node',
|
||||
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
|
||||
slowTestThreshold: 20,
|
||||
};
|
||||
|
||||
2
packages/app-cli/tests/.gitignore
vendored
2
packages/app-cli/tests/.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
data/
|
||||
test data/
|
||||
export/
|
||||
@@ -1,24 +1,15 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
|
||||
import shim from '@joplin/lib/shim';
|
||||
const os = require('os');
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const { filename } = require('@joplin/lib/path-utils');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
|
||||
const Folder = require('@joplin/lib/models/Folder.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const HtmlToMd = require('@joplin/lib/HtmlToMd');
|
||||
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
|
||||
|
||||
describe('HtmlToMd', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
// beforeEach(async (done) => {
|
||||
// await setupDatabaseAndSynchronizer(1);
|
||||
// await switchClient(1);
|
||||
// done();
|
||||
// });
|
||||
|
||||
it('should convert from Html to Markdown', (async () => {
|
||||
const basePath = `${__dirname}/html_to_md`;
|
||||
@@ -32,11 +23,11 @@ describe('HtmlToMd', function() {
|
||||
const htmlPath = `${basePath}/${htmlFilename}`;
|
||||
const mdPath = `${basePath}/${filename(htmlFilename)}.md`;
|
||||
|
||||
// if (htmlFilename !== 'joplin_source_2.html') continue;
|
||||
// if (htmlFilename !== 'anchor_same_title_and_url.html') continue;
|
||||
|
||||
// if (htmlFilename.indexOf('image_preserve_size') !== 0) continue;
|
||||
|
||||
const htmlToMdOptions = {};
|
||||
const htmlToMdOptions: any = {};
|
||||
|
||||
if (htmlFilename === 'anchor_local.html') {
|
||||
// Normally the list of anchor names in the document are retrieved from the HTML code
|
||||
@@ -61,16 +52,29 @@ describe('HtmlToMd', function() {
|
||||
}
|
||||
|
||||
if (actualMd !== expectedMd) {
|
||||
console.info('');
|
||||
console.info(`Error converting file: ${htmlFilename}`);
|
||||
console.info('--------------------------------- Got:');
|
||||
console.info(actualMd);
|
||||
console.info('--------------------------------- Raw:');
|
||||
console.info(actualMd.split('\n'));
|
||||
console.info('--------------------------------- Expected:');
|
||||
console.info(expectedMd.split('\n'));
|
||||
console.info('--------------------------------------------');
|
||||
console.info('');
|
||||
const result = [];
|
||||
|
||||
result.push('');
|
||||
result.push(`Error converting file: ${htmlFilename}`);
|
||||
result.push('--------------------------------- Got:');
|
||||
result.push(actualMd.split('\n').map((l: string) => `"${l}"`).join('\n'));
|
||||
result.push('--------------------------------- Expected:');
|
||||
result.push(expectedMd.split('\n').map((l: string) => `"${l}"`).join('\n'));
|
||||
result.push('--------------------------------------------');
|
||||
result.push('');
|
||||
|
||||
console.info(result.join('\n'));
|
||||
|
||||
// console.info('');
|
||||
// console.info(`Error converting file: ${htmlFilename}`);
|
||||
// console.info('--------------------------------- Got:');
|
||||
// console.info(actualMd);
|
||||
// console.info('--------------------------------- Raw:');
|
||||
// console.info(actualMd.split('\n'));
|
||||
// console.info('--------------------------------- Expected:');
|
||||
// console.info(expectedMd.split('\n'));
|
||||
// console.info('--------------------------------------------');
|
||||
// console.info('');
|
||||
|
||||
expect(false).toBe(true);
|
||||
// return;
|
||||
@@ -395,4 +395,4 @@ describe('Synchronizer.basics', function() {
|
||||
expect((await Note.all()).length).toBe(11);
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -291,4 +291,4 @@ describe('Synchronizer.conflicts', function() {
|
||||
await ignorableNoteConflictTest(true);
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -400,4 +400,4 @@ describe('Synchronizer.e2ee', function() {
|
||||
expect(note1_2.title).toBe('');
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -354,4 +354,4 @@ describe('Synchronizer.resources', function() {
|
||||
expect(syncItems[1].sync_disabled).toBe(1);
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -182,4 +182,4 @@ describe('Synchronizer.revisions', function() {
|
||||
expect((await Revision.all()).length).toBe(0);
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<ul>
|
||||
<li><a href="https://example.com"/>https://example.com</a></li>
|
||||
<li><a href="http://example.com"/>http://example.com</a></li>
|
||||
<li><a href="file:///mnt/c/test.txt"/>file:///mnt/c/test.txt</a></li>
|
||||
<li><a href="https://example.com" title="with title"/>https://example.com</a></li>
|
||||
<li><a href="example.com"/>example.com</a></li>
|
||||
<li><a href="test@example.com"/>test@example.com</a></li>
|
||||
</ul>
|
||||
@@ -0,0 +1,6 @@
|
||||
- https://example.com
|
||||
- http://example.com
|
||||
- file:///mnt/c/test.txt
|
||||
- [https://example.com](https://example.com "with title")
|
||||
- [example.com](example.com)
|
||||
- [test@example.com](test@example.com)
|
||||
1
packages/app-cli/tests/html_to_md/code_3.html
Normal file
1
packages/app-cli/tests/html_to_md/code_3.html
Normal file
@@ -0,0 +1 @@
|
||||
<pre style="font-family: monospace;"><span>├── myproj_app<br>│ ├── api.py<br>│ └── Dockerfile</span></pre>
|
||||
5
packages/app-cli/tests/html_to_md/code_3.md
Normal file
5
packages/app-cli/tests/html_to_md/code_3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
```
|
||||
├── myproj_app
|
||||
│ ├── api.py
|
||||
│ └── Dockerfile
|
||||
```
|
||||
@@ -1,6 +1,7 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import BaseModel from '@joplin/lib/BaseModel';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import markdownUtils from '@joplin/lib/markdownUtils';
|
||||
const { sortedIds, createNTestNotes, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
|
||||
const Folder = require('@joplin/lib/models/Folder.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
@@ -217,6 +218,8 @@ describe('models_Note', function() {
|
||||
const t1 = r1.updated_time;
|
||||
const t2 = r2.updated_time;
|
||||
|
||||
const resourceDirE = markdownUtils.escapeLinkUrl(resourceDir);
|
||||
|
||||
const testCases = [
|
||||
[
|
||||
false,
|
||||
@@ -241,17 +244,17 @@ describe('models_Note', function() {
|
||||
[
|
||||
true,
|
||||
``,
|
||||
``,
|
||||
``,
|
||||
],
|
||||
[
|
||||
true,
|
||||
`  `,
|
||||
`  `,
|
||||
`  `,
|
||||
],
|
||||
[
|
||||
true,
|
||||
``,
|
||||
``,
|
||||
``,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
|
||||
|
||||
describe('JoplinViewMenuItem', () => {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await afterEachCleanUp();
|
||||
});
|
||||
|
||||
test('should register commands with the keymap service', async () => {
|
||||
const service = new newPluginService() as PluginService;
|
||||
|
||||
KeymapService.instance().initialize();
|
||||
|
||||
const pluginScript = newPluginScript(`
|
||||
joplin.plugins.register({
|
||||
onStart: async function() {
|
||||
await joplin.commands.register({
|
||||
name: 'testCommand1',
|
||||
label: 'My Test Command 1',
|
||||
iconName: 'fas fa-music',
|
||||
execute: async () => {},
|
||||
});
|
||||
|
||||
await joplin.views.menuItems.create('myMenuItem1', 'testCommand1', 'tools', { accelerator: 'CmdOrCtrl+Alt+Shift+B' });
|
||||
|
||||
await joplin.commands.register({
|
||||
name: 'testCommand2',
|
||||
label: 'My Test Command 2',
|
||||
iconName: 'fas fa-music',
|
||||
execute: async () => {},
|
||||
});
|
||||
|
||||
await joplin.views.menuItems.create('myMenuItem2', 'testCommand2', 'tools');
|
||||
},
|
||||
});
|
||||
`);
|
||||
|
||||
const plugin = await service.loadPluginFromJsBundle('', pluginScript);
|
||||
await service.runPlugin(plugin);
|
||||
|
||||
const commandNames = KeymapService.instance().getCommandNames();
|
||||
|
||||
expect(commandNames.includes('testCommand1')).toBe(true);
|
||||
expect(commandNames.includes('testCommand2')).toBe(true);
|
||||
|
||||
await service.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -257,8 +257,8 @@ describe('services_EncryptionService', function() {
|
||||
await service.loadMasterKey_(masterKey, '123456', true);
|
||||
|
||||
const sourcePath = `${__dirname}/../tests/support/photo.jpg`;
|
||||
const encryptedPath = `${__dirname}/data/photo.crypted`;
|
||||
const decryptedPath = `${__dirname}/data/photo.jpg`;
|
||||
const encryptedPath = `${Setting.value('tempDir')}/photo.crypted`;
|
||||
const decryptedPath = `${Setting.value('tempDir')}/photo.jpg`;
|
||||
|
||||
await service.encryptFile(sourcePath, encryptedPath);
|
||||
await service.decryptFile(encryptedPath, decryptedPath);
|
||||
|
||||
@@ -2,8 +2,7 @@ import InteropService from '@joplin/lib/services/interop/InteropService';
|
||||
import { CustomExportContext, CustomImportContext, Module, ModuleType } from '@joplin/lib/services/interop/types';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
|
||||
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
|
||||
const { fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, exportDir } = require('./test-utils.js');
|
||||
const Folder = require('@joplin/lib/models/Folder.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
const Tag = require('@joplin/lib/models/Tag.js');
|
||||
@@ -11,10 +10,6 @@ const Resource = require('@joplin/lib/models/Resource.js');
|
||||
const fs = require('fs-extra');
|
||||
const ArrayUtils = require('@joplin/lib/ArrayUtils');
|
||||
|
||||
function exportDir() {
|
||||
return `${__dirname}/export`;
|
||||
}
|
||||
|
||||
async function recreateExportDir() {
|
||||
const dir = exportDir();
|
||||
await fs.remove(dir);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient, exportDir } = require('./test-utils.js');
|
||||
const InteropService_Exporter_Md = require('@joplin/lib/services/interop/InteropService_Exporter_Md').default;
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const Folder = require('@joplin/lib/models/Folder.js');
|
||||
@@ -10,29 +10,27 @@ const Resource = require('@joplin/lib/models/Resource.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
|
||||
const exportDir = `${__dirname}/export`;
|
||||
|
||||
describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
|
||||
await fs.remove(exportDir);
|
||||
await fs.mkdirp(exportDir);
|
||||
await fs.remove(exportDir());
|
||||
await fs.mkdirp(exportDir());
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create resources directory', (async () => {
|
||||
const service = new InteropService_Exporter_Md();
|
||||
await service.init(exportDir);
|
||||
await service.init(exportDir());
|
||||
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/_resources/`)).toBe(true);
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/_resources/`)).toBe(true);
|
||||
}));
|
||||
|
||||
it('should create note paths and add them to context', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -74,7 +72,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
it('should handle duplicate note names', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -101,7 +99,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
it('should not override existing files', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -118,7 +116,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
// Create a file with the path of note1 before processing note1
|
||||
await shim.fsDriver().writeFile(`${exportDir}/folder1/note1.md`, 'Note content', 'utf-8');
|
||||
await shim.fsDriver().writeFile(`${exportDir()}/folder1/note1.md`, 'Note content', 'utf-8');
|
||||
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
|
||||
@@ -128,7 +126,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
it('should save resource files in _resource directory', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -159,13 +157,13 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
await exporter.processResource(resource1, Resource.fullPath(resource1));
|
||||
await exporter.processResource(resource2, Resource.fullPath(resource2));
|
||||
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/_resources/${Resource.filename(resource1)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/_resources/${Resource.filename(resource2)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/_resources/${Resource.filename(resource1)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/_resources/${Resource.filename(resource2)}`)).toBe(true, 'Resource file should be copied to _resources directory.');
|
||||
}));
|
||||
|
||||
it('should create folders in fs', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -189,14 +187,14 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/folder1`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/folder1/folder2`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/folder1/folder3`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/folder1`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/folder1/folder2`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/folder1/folder3`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
}));
|
||||
|
||||
it('should save notes in fs', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -226,14 +224,14 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
await exporter.processItem(Note.modelType(), note3);
|
||||
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note1.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note2.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note3.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/${exporter.context().notePaths[note1.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/${exporter.context().notePaths[note2.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir()}/${exporter.context().notePaths[note3.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
}));
|
||||
|
||||
it('should replace resource ids with relative paths', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -269,8 +267,8 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
await exporter.processItem(Note.modelType(), note1);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
|
||||
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
|
||||
const note1_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note1.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note2.id]}`);
|
||||
|
||||
expect(note1_body).toContain('](../_resources/resource1.jpg)', 'Resource id should be replaced with a relative path.');
|
||||
expect(note2_body).toContain('](../../_resources/resource2.jpg)', 'Resource id should be replaced with a relative path.');
|
||||
@@ -278,7 +276,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
it('should replace note ids with relative paths', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -318,9 +316,9 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
await exporter.processItem(Note.modelType(), note3);
|
||||
|
||||
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
|
||||
const note3_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note3.id]}`);
|
||||
const note1_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note1.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note2.id]}`);
|
||||
const note3_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note3.id]}`);
|
||||
|
||||
expect(note1_body).toContain('](../folder3/note3.md)', 'Note id should be replaced with a relative path.');
|
||||
expect(note2_body).toContain('](../../folder3/note3.md)', 'Resource id should be replaced with a relative path.');
|
||||
@@ -330,7 +328,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
|
||||
it('should url encode relative note links', (async () => {
|
||||
const exporter = new InteropService_Exporter_Md();
|
||||
await exporter.init(exportDir);
|
||||
await exporter.init(exportDir());
|
||||
|
||||
const itemsToExport = [];
|
||||
const queueExportItem = (itemType, itemOrId) => {
|
||||
@@ -351,7 +349,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
||||
await exporter.processItem(Note.modelType(), note1);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir()}/${exporter.context().notePaths[note2.id]}`);
|
||||
expect(note2_body).toContain('[link](../folder%20with%20space1/note1%20name%20with%20space.md)', 'Whitespace in URL should be encoded');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -511,4 +511,17 @@ describe('services_SearchEngine', function() {
|
||||
expect((await engine.search('"- [ ]"', { searchType: SearchEngine.SEARCH_TYPE_BASIC })).length).toBe(1);
|
||||
expect((await engine.search('"[ ]"', { searchType: SearchEngine.SEARCH_TYPE_BASIC })).length).toBe(2);
|
||||
}));
|
||||
|
||||
it('should not mistake cyrillic "l" for latin "n"', (async () => {
|
||||
const n1 = await Note.save({ title: 'latin n', body: 'n' });
|
||||
const n2 = await Note.save({ title: 'cyrillic l', body: 'л' });
|
||||
|
||||
await engine.syncTables();
|
||||
|
||||
expect((await engine.search('n')).length).toBe(1);
|
||||
expect((await engine.search('n'))[0].id).toBe(n1.id);
|
||||
|
||||
expect((await engine.search('л')).length).toBe(1);
|
||||
expect((await engine.search('л'))[0].id).toBe(n2.id);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -7,8 +7,13 @@ joplin.plugins.register({
|
||||
console.info('Alarm was triggered for note: ', note);
|
||||
});
|
||||
|
||||
joplin.workspace.onSyncComplete(async () => {
|
||||
joplin.workspace.onSyncStart(async (event:any) => {
|
||||
console.info('Sync has started...');
|
||||
});
|
||||
|
||||
joplin.workspace.onSyncComplete(async (event:any) => {
|
||||
console.info('Sync has completed');
|
||||
console.info('With errors:', event.withErrors);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('synchronizer_MigrationHandler', function() {
|
||||
// global Jest timeout. So we need to set it globally.
|
||||
//
|
||||
// https://github.com/facebook/jest/issues/5055#issuecomment-513585906
|
||||
jest.setTimeout(specTimeout)
|
||||
jest.setTimeout(specTimeout);
|
||||
|
||||
// To test the migrations, we have to use the filesystem sync target
|
||||
// because the sync target snapshots are plain files. Eventually
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.localNotesFoldersSameAsRemote = exports.remoteResources = exports.remoteNotesFoldersResources = exports.remoteNotesAndFolders = exports.allNotesFolders = void 0;
|
||||
const BaseModel_1 = require("@joplin/lib/BaseModel");
|
||||
const { fileApi } = require('./test-utils.js');
|
||||
const Folder = require('@joplin/lib/models/Folder.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
const BaseItem = require('@joplin/lib/models/BaseItem.js');
|
||||
function allNotesFolders() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const folders = yield Folder.all();
|
||||
const notes = yield Note.all();
|
||||
return folders.concat(notes);
|
||||
});
|
||||
}
|
||||
exports.allNotesFolders = allNotesFolders;
|
||||
function remoteItemsByTypes(types) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const list = yield fileApi().list('', { includeDirs: false, syncItemsOnly: true });
|
||||
if (list.has_more)
|
||||
throw new Error('Not implemented!!!');
|
||||
const files = list.items;
|
||||
const output = [];
|
||||
for (const file of files) {
|
||||
const remoteContent = yield fileApi().get(file.path);
|
||||
const content = yield BaseItem.unserialize(remoteContent);
|
||||
if (types.indexOf(content.type_) < 0)
|
||||
continue;
|
||||
output.push(content);
|
||||
}
|
||||
return output;
|
||||
});
|
||||
}
|
||||
function remoteNotesAndFolders() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return remoteItemsByTypes([BaseModel_1.default.TYPE_NOTE, BaseModel_1.default.TYPE_FOLDER]);
|
||||
});
|
||||
}
|
||||
exports.remoteNotesAndFolders = remoteNotesAndFolders;
|
||||
function remoteNotesFoldersResources() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return remoteItemsByTypes([BaseModel_1.default.TYPE_NOTE, BaseModel_1.default.TYPE_FOLDER, BaseModel_1.default.TYPE_RESOURCE]);
|
||||
});
|
||||
}
|
||||
exports.remoteNotesFoldersResources = remoteNotesFoldersResources;
|
||||
function remoteResources() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return remoteItemsByTypes([BaseModel_1.default.TYPE_RESOURCE]);
|
||||
});
|
||||
}
|
||||
exports.remoteResources = remoteResources;
|
||||
function localNotesFoldersSameAsRemote(locals, expect) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let error = null;
|
||||
try {
|
||||
const nf = yield remoteNotesAndFolders();
|
||||
expect(locals.length).toBe(nf.length);
|
||||
for (let i = 0; i < locals.length; i++) {
|
||||
const dbItem = locals[i];
|
||||
const path = BaseItem.systemPath(dbItem);
|
||||
const remote = yield fileApi().stat(path);
|
||||
expect(!!remote).toBe(true);
|
||||
if (!remote)
|
||||
continue;
|
||||
let remoteContent = yield fileApi().get(path);
|
||||
remoteContent = dbItem.type_ == BaseModel_1.default.TYPE_NOTE ? yield Note.unserialize(remoteContent) : yield Folder.unserialize(remoteContent);
|
||||
expect(remoteContent.title).toBe(dbItem.title);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBe(null);
|
||||
});
|
||||
}
|
||||
exports.localNotesFoldersSameAsRemote = localNotesFoldersSameAsRemote;
|
||||
//# sourceMappingURL=test-utils-synchronizer.js.map
|
||||
@@ -62,4 +62,4 @@ export async function localNotesFoldersSameAsRemote(locals: any[], expect: Funct
|
||||
}
|
||||
|
||||
expect(error).toBe(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
/* eslint-disable require-atomic-updates */
|
||||
import BaseApplication from '@joplin/lib/BaseApplication';
|
||||
import BaseModel from '@joplin/lib/BaseModel';
|
||||
import Logger, { TargetType, LoggerWrapper } from '@joplin/lib/Logger';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import BaseService from '@joplin/lib/services/BaseService';
|
||||
import FsDriverNode from '@joplin/lib/fs-driver-node';
|
||||
import time from '@joplin/lib/time';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import uuid from '@joplin/lib/uuid';
|
||||
import ResourceService from '@joplin/lib/services/ResourceService';
|
||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||
import KvStore from '@joplin/lib/services/KvStore';
|
||||
import KeychainServiceDriver from '@joplin/lib/services/keychain/KeychainServiceDriver.node';
|
||||
import KeychainServiceDriverDummy from '@joplin/lib/services/keychain/KeychainServiceDriver.dummy';
|
||||
import PluginRunner from '../app/services/plugins/PluginRunner';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { JoplinDatabase } = require('@joplin/lib/joplin-database.js');
|
||||
const { DatabaseDriverNode } = require('@joplin/lib/database-driver-node.js');
|
||||
const BaseApplication = require('@joplin/lib/BaseApplication').default;
|
||||
const BaseModel = require('@joplin/lib/BaseModel').default;
|
||||
const Folder = require('@joplin/lib/models/Folder.js');
|
||||
const Note = require('@joplin/lib/models/Note.js');
|
||||
const ItemChange = require('@joplin/lib/models/ItemChange.js');
|
||||
@@ -12,8 +26,6 @@ const Resource = require('@joplin/lib/models/Resource.js');
|
||||
const Tag = require('@joplin/lib/models/Tag.js');
|
||||
const NoteTag = require('@joplin/lib/models/NoteTag.js');
|
||||
const Revision = require('@joplin/lib/models/Revision.js');
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const MasterKey = require('@joplin/lib/models/MasterKey');
|
||||
const BaseItem = require('@joplin/lib/models/BaseItem.js');
|
||||
const { FileApi } = require('@joplin/lib/file-api.js');
|
||||
@@ -23,12 +35,7 @@ const { FileApiDriverWebDav } = require('@joplin/lib/file-api-driver-webdav.js')
|
||||
const { FileApiDriverDropbox } = require('@joplin/lib/file-api-driver-dropbox.js');
|
||||
const { FileApiDriverOneDrive } = require('@joplin/lib/file-api-driver-onedrive.js');
|
||||
const { FileApiDriverAmazonS3 } = require('@joplin/lib/file-api-driver-amazon-s3.js');
|
||||
const BaseService = require('@joplin/lib/services/BaseService').default;
|
||||
const FsDriverNode = require('@joplin/lib/fs-driver-node').default;
|
||||
const time = require('@joplin/lib/time').default;
|
||||
const { shimInit } = require('@joplin/lib/shim-init-node.js');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
const uuid = require('@joplin/lib/uuid').default;
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
|
||||
const SyncTargetMemory = require('@joplin/lib/SyncTargetMemory.js');
|
||||
const SyncTargetFilesystem = require('@joplin/lib/SyncTargetFilesystem.js');
|
||||
@@ -38,20 +45,14 @@ const SyncTargetDropbox = require('@joplin/lib/SyncTargetDropbox.js');
|
||||
const SyncTargetAmazonS3 = require('@joplin/lib/SyncTargetAmazonS3.js');
|
||||
const EncryptionService = require('@joplin/lib/services/EncryptionService.js');
|
||||
const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker.js');
|
||||
const ResourceService = require('@joplin/lib/services/ResourceService').default;
|
||||
const RevisionService = require('@joplin/lib/services/RevisionService.js');
|
||||
const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js');
|
||||
const KvStore = require('@joplin/lib/services/KvStore').default;
|
||||
const WebDavApi = require('@joplin/lib/WebDavApi');
|
||||
const DropboxApi = require('@joplin/lib/DropboxApi');
|
||||
const { OneDriveApi } = require('@joplin/lib/onedrive-api');
|
||||
const { loadKeychainServiceAndSettings } = require('@joplin/lib/services/SettingUtils');
|
||||
const KeychainServiceDriver = require('@joplin/lib/services/keychain/KeychainServiceDriver.node').default;
|
||||
const KeychainServiceDriverDummy = require('@joplin/lib/services/keychain/KeychainServiceDriver.dummy').default;
|
||||
const md5 = require('md5');
|
||||
const S3 = require('aws-sdk/clients/s3');
|
||||
const PluginRunner = require('../app/services/plugins/PluginRunner').default;
|
||||
const PluginService = require('@joplin/lib/services/plugins/PluginService').default;
|
||||
const { Dirnames } = require('@joplin/lib/services/synchronizer/utils/types');
|
||||
const sharp = require('sharp');
|
||||
|
||||
@@ -62,16 +63,16 @@ const sharp = require('sharp');
|
||||
// Jest, to make debugging easier, but it's not clear how to get this info).
|
||||
const suiteName_ = uuid.createNano();
|
||||
|
||||
const databases_ = [];
|
||||
let synchronizers_ = [];
|
||||
const synchronizerContexts_ = {};
|
||||
const fileApis_ = {};
|
||||
const encryptionServices_ = [];
|
||||
const revisionServices_ = [];
|
||||
const decryptionWorkers_ = [];
|
||||
const resourceServices_ = [];
|
||||
const resourceFetchers_ = [];
|
||||
const kvStores_ = [];
|
||||
const databases_: any[] = [];
|
||||
let synchronizers_: any[] = [];
|
||||
const synchronizerContexts_: any = {};
|
||||
const fileApis_: any = {};
|
||||
const encryptionServices_: any[] = [];
|
||||
const revisionServices_: any[] = [];
|
||||
const decryptionWorkers_: any[] = [];
|
||||
const resourceServices_: any[] = [];
|
||||
const resourceFetchers_: any[] = [];
|
||||
const kvStores_: KvStore[] = [];
|
||||
let currentClient_ = 1;
|
||||
|
||||
// The line `process.on('unhandledRejection'...` in all the test files is going to
|
||||
@@ -99,7 +100,11 @@ FileApiDriverLocal.fsDriver_ = fsDriver;
|
||||
|
||||
const logDir = `${__dirname}/../tests/logs`;
|
||||
const baseTempDir = `${__dirname}/../tests/tmp/${suiteName_}`;
|
||||
const dataDir = `${__dirname}/data/${suiteName_}`;
|
||||
|
||||
// We add a space in the data directory path as that will help uncover
|
||||
// various space-in-path issues.
|
||||
const dataDir = `${__dirname}/test data/${suiteName_}`;
|
||||
|
||||
fs.mkdirpSync(logDir, 0o755);
|
||||
fs.mkdirpSync(baseTempDir, 0o755);
|
||||
fs.mkdirpSync(dataDir);
|
||||
@@ -112,7 +117,7 @@ SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
|
||||
|
||||
let syncTargetName_ = '';
|
||||
let syncTargetId_ = null;
|
||||
let syncTargetId_: number = null;
|
||||
let sleepTime = 0;
|
||||
let isNetworkSyncTarget_ = false;
|
||||
|
||||
@@ -120,7 +125,7 @@ function syncTargetName() {
|
||||
return syncTargetName_;
|
||||
}
|
||||
|
||||
function setSyncTargetName(name) {
|
||||
function setSyncTargetName(name: string) {
|
||||
if (name === syncTargetName_) return syncTargetName_;
|
||||
const previousName = syncTargetName_;
|
||||
syncTargetName_ = name;
|
||||
@@ -141,20 +146,19 @@ setSyncTargetName('memory');
|
||||
|
||||
const syncDir = `${__dirname}/../tests/sync/${suiteName_}`;
|
||||
|
||||
// TODO: Should probably update this for Jest?
|
||||
|
||||
// let defaultJasmineTimeout = 90 * 1000;
|
||||
// if (isNetworkSyncTarget_) defaultJasmineTimeout = 60 * 1000 * 10;
|
||||
// if (typeof jasmine !== 'undefined') jasmine.DEFAULT_TIMEOUT_INTERVAL = defaultJasmineTimeout;
|
||||
// 90 seconds now that the tests are running in parallel and have been
|
||||
// split into smaller suites might not be necessary but for now leave it
|
||||
// anyway.
|
||||
let defaultJestTimeout = 90 * 1000;
|
||||
if (isNetworkSyncTarget_) defaultJestTimeout = 60 * 1000 * 10;
|
||||
jest.setTimeout(defaultJestTimeout);
|
||||
|
||||
const dbLogger = new Logger();
|
||||
dbLogger.addTarget('console');
|
||||
// dbLogger.addTarget('file', { path: `${logDir}/log.txt` });
|
||||
dbLogger.addTarget(TargetType.Console);
|
||||
dbLogger.setLevel(Logger.LEVEL_WARN);
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget('console');
|
||||
// logger.addTarget('file', { path: `${logDir}/log.txt` });
|
||||
logger.addTarget(TargetType.Console);
|
||||
logger.setLevel(Logger.LEVEL_WARN); // Set to DEBUG to display sync process in console
|
||||
|
||||
Logger.initializeGlobalLogger(logger);
|
||||
@@ -184,16 +188,16 @@ function isNetworkSyncTarget() {
|
||||
return isNetworkSyncTarget_;
|
||||
}
|
||||
|
||||
function sleep(n) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function sleep(n: number) {
|
||||
return new Promise((resolve) => {
|
||||
shim.setTimeout(() => {
|
||||
resolve();
|
||||
}, Math.round(n * 1000));
|
||||
});
|
||||
}
|
||||
|
||||
function msleep(ms) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function msleep(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
shim.setTimeout(() => {
|
||||
resolve();
|
||||
}, ms);
|
||||
@@ -206,9 +210,10 @@ function currentClientId() {
|
||||
|
||||
async function afterEachCleanUp() {
|
||||
await ItemChange.waitForAllSaved();
|
||||
KeymapService.destroyInstance();
|
||||
}
|
||||
|
||||
async function switchClient(id, options = null) {
|
||||
async function switchClient(id: number, options: any = null) {
|
||||
options = Object.assign({}, { keychainEnabled: false }, options);
|
||||
|
||||
if (!databases_[id]) throw new Error(`Call setupDatabaseAndSynchronizer(${id}) first!!`);
|
||||
@@ -233,7 +238,7 @@ async function switchClient(id, options = null) {
|
||||
Setting.setValue('sync.wipeOutFailSafe', false); // To keep things simple, always disable fail-safe unless explicitely set in the test itself
|
||||
}
|
||||
|
||||
async function clearDatabase(id = null) {
|
||||
async function clearDatabase(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
if (!databases_[id]) return;
|
||||
|
||||
@@ -264,13 +269,19 @@ async function clearDatabase(id = null) {
|
||||
await databases_[id].transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
async function setupDatabase(id = null, options = null) {
|
||||
async function setupDatabase(id: number = null, options: any = null) {
|
||||
options = Object.assign({}, { keychainEnabled: false }, options);
|
||||
|
||||
if (id === null) id = currentClient_;
|
||||
|
||||
Setting.cancelScheduleSave();
|
||||
Setting.cache_ = null;
|
||||
|
||||
// Note that this was changed from `Setting.cache_ = []` to `await
|
||||
// Setting.reset()` during the TypeScript conversion. Normally this is
|
||||
// more correct but something to keep in mind anyway in case there are
|
||||
// some strange async issue related to settings when the tests are
|
||||
// running.
|
||||
await Setting.reset();
|
||||
|
||||
if (databases_[id]) {
|
||||
BaseModel.setDb(databases_[id]);
|
||||
@@ -295,22 +306,27 @@ async function setupDatabase(id = null, options = null) {
|
||||
await loadKeychainServiceAndSettings(options.keychainEnabled ? KeychainServiceDriver : KeychainServiceDriverDummy);
|
||||
}
|
||||
|
||||
function resourceDirName(id = null) {
|
||||
function exportDir(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return `${dataDir}/export`;
|
||||
}
|
||||
|
||||
function resourceDirName(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return `resources-${id}`;
|
||||
}
|
||||
|
||||
function resourceDir(id = null) {
|
||||
function resourceDir(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return `${dataDir}/${resourceDirName(id)}`;
|
||||
}
|
||||
|
||||
function pluginDir(id = null) {
|
||||
function pluginDir(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return `${dataDir}/plugins-${id}`;
|
||||
}
|
||||
|
||||
async function setupDatabaseAndSynchronizer(id, options = null) {
|
||||
async function setupDatabaseAndSynchronizer(id: number, options: any = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
|
||||
BaseService.logger_ = logger;
|
||||
@@ -347,12 +363,12 @@ async function setupDatabaseAndSynchronizer(id, options = null) {
|
||||
await fileApi().clearRoot();
|
||||
}
|
||||
|
||||
function db(id = null) {
|
||||
function db(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return databases_[id];
|
||||
}
|
||||
|
||||
function synchronizer(id = null) {
|
||||
function synchronizer(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return synchronizers_[id];
|
||||
}
|
||||
@@ -360,7 +376,7 @@ function synchronizer(id = null) {
|
||||
// This is like calling synchronizer.start() but it handles the
|
||||
// complexity of passing around the sync context depending on
|
||||
// the client.
|
||||
async function synchronizerStart(id = null, extraOptions = null) {
|
||||
async function synchronizerStart(id: number = null, extraOptions: any = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
const context = synchronizerContexts_[id];
|
||||
const options = Object.assign({}, extraOptions);
|
||||
@@ -370,41 +386,41 @@ async function synchronizerStart(id = null, extraOptions = null) {
|
||||
return newContext;
|
||||
}
|
||||
|
||||
function encryptionService(id = null) {
|
||||
function encryptionService(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return encryptionServices_[id];
|
||||
}
|
||||
|
||||
function kvStore(id = null) {
|
||||
function kvStore(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
const o = kvStores_[id];
|
||||
o.setDb(db(id));
|
||||
return o;
|
||||
}
|
||||
|
||||
function revisionService(id = null) {
|
||||
function revisionService(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return revisionServices_[id];
|
||||
}
|
||||
|
||||
function decryptionWorker(id = null) {
|
||||
function decryptionWorker(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
const o = decryptionWorkers_[id];
|
||||
o.setKvStore(kvStore(id));
|
||||
return o;
|
||||
}
|
||||
|
||||
function resourceService(id = null) {
|
||||
function resourceService(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return resourceServices_[id];
|
||||
}
|
||||
|
||||
function resourceFetcher(id = null) {
|
||||
function resourceFetcher(id: number = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
return resourceFetchers_[id];
|
||||
}
|
||||
|
||||
async function loadEncryptionMasterKey(id = null, useExisting = false) {
|
||||
async function loadEncryptionMasterKey(id: number = null, useExisting = false) {
|
||||
const service = encryptionService(id);
|
||||
|
||||
let masterKey = null;
|
||||
@@ -487,7 +503,7 @@ function fileApi() {
|
||||
return fileApis_[syncTargetId_];
|
||||
}
|
||||
|
||||
function objectsEqual(o1, o2) {
|
||||
function objectsEqual(o1: any, o2: any) {
|
||||
if (Object.getOwnPropertyNames(o1).length !== Object.getOwnPropertyNames(o2).length) return false;
|
||||
for (const n in o1) {
|
||||
if (!o1.hasOwnProperty(n)) continue;
|
||||
@@ -496,7 +512,7 @@ function objectsEqual(o1, o2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function checkThrowAsync(asyncFn) {
|
||||
async function checkThrowAsync(asyncFn: Function) {
|
||||
let hasThrown = false;
|
||||
try {
|
||||
await asyncFn();
|
||||
@@ -506,7 +522,7 @@ async function checkThrowAsync(asyncFn) {
|
||||
return hasThrown;
|
||||
}
|
||||
|
||||
async function expectThrow(asyncFn, errorCode = undefined) {
|
||||
async function expectThrow(asyncFn: Function, errorCode: any = undefined) {
|
||||
let hasThrown = false;
|
||||
let thrownError = null;
|
||||
try {
|
||||
@@ -517,7 +533,7 @@ async function expectThrow(asyncFn, errorCode = undefined) {
|
||||
}
|
||||
|
||||
if (!hasThrown) {
|
||||
expect('not throw').toBe('throw', 'Expected function to throw an error but did not');
|
||||
expect('not throw').toBe('throw');
|
||||
} else if (thrownError.code !== errorCode) {
|
||||
console.error(thrownError);
|
||||
expect(`error code: ${thrownError.code}`).toBe(`error code: ${errorCode}`);
|
||||
@@ -526,7 +542,7 @@ async function expectThrow(asyncFn, errorCode = undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
async function expectNotThrow(asyncFn) {
|
||||
async function expectNotThrow(asyncFn: Function) {
|
||||
let thrownError = null;
|
||||
try {
|
||||
await asyncFn();
|
||||
@@ -536,13 +552,13 @@ async function expectNotThrow(asyncFn) {
|
||||
|
||||
if (thrownError) {
|
||||
console.error(thrownError);
|
||||
expect(thrownError.message).toBe('', 'Expected function not to throw an error but it did');
|
||||
expect(thrownError.message).toBe('');
|
||||
} else {
|
||||
expect(true).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
function checkThrow(fn) {
|
||||
function checkThrow(fn: Function) {
|
||||
let hasThrown = false;
|
||||
try {
|
||||
fn();
|
||||
@@ -552,7 +568,7 @@ function checkThrow(fn) {
|
||||
return hasThrown;
|
||||
}
|
||||
|
||||
function fileContentEqual(path1, path2) {
|
||||
function fileContentEqual(path1: string, path2: string) {
|
||||
const fs = require('fs-extra');
|
||||
const content1 = fs.readFileSync(path1, 'base64');
|
||||
const content2 = fs.readFileSync(path2, 'base64');
|
||||
@@ -591,19 +607,19 @@ async function allSyncTargetItemsEncrypted() {
|
||||
return totalCount === encryptedCount;
|
||||
}
|
||||
|
||||
function id(a) {
|
||||
function id(a: any) {
|
||||
return a.id;
|
||||
}
|
||||
|
||||
function ids(a) {
|
||||
function ids(a: any[]) {
|
||||
return a.map(n => n.id);
|
||||
}
|
||||
|
||||
function sortedIds(a) {
|
||||
function sortedIds(a: any[]) {
|
||||
return ids(a).sort();
|
||||
}
|
||||
|
||||
function at(a, indexes) {
|
||||
function at(a: any[], indexes: any[]) {
|
||||
const out = [];
|
||||
for (let i = 0; i < indexes.length; i++) {
|
||||
out.push(a[indexes[i]]);
|
||||
@@ -611,7 +627,7 @@ function at(a, indexes) {
|
||||
return out;
|
||||
}
|
||||
|
||||
async function createNTestFolders(n) {
|
||||
async function createNTestFolders(n: number) {
|
||||
const folders = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
const folder = await Folder.save({ title: 'folder' });
|
||||
@@ -621,7 +637,7 @@ async function createNTestFolders(n) {
|
||||
return folders;
|
||||
}
|
||||
|
||||
async function createNTestNotes(n, folder, tagIds = null, title = 'note') {
|
||||
async function createNTestNotes(n: number, folder: any, tagIds: string[] = null, title: string = 'note') {
|
||||
const notes = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
const title_ = n > 1 ? `${title}${i}` : title;
|
||||
@@ -638,7 +654,7 @@ async function createNTestNotes(n, folder, tagIds = null, title = 'note') {
|
||||
return notes;
|
||||
}
|
||||
|
||||
async function createNTestTags(n) {
|
||||
async function createNTestTags(n: number) {
|
||||
const tags = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
const tag = await Tag.save({ title: 'tag' });
|
||||
@@ -648,7 +664,7 @@ async function createNTestTags(n) {
|
||||
return tags;
|
||||
}
|
||||
|
||||
function tempFilePath(ext) {
|
||||
function tempFilePath(ext: string) {
|
||||
return `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}.${ext}`;
|
||||
}
|
||||
|
||||
@@ -675,7 +691,7 @@ function newPluginService(appVersion = '1.4') {
|
||||
return service;
|
||||
}
|
||||
|
||||
function newPluginScript(script) {
|
||||
function newPluginScript(script: string) {
|
||||
return `
|
||||
/* joplin-manifest:
|
||||
{
|
||||
@@ -705,6 +721,11 @@ function newPluginScript(script) {
|
||||
|
||||
// Application for feature integration testing
|
||||
class TestApp extends BaseApplication {
|
||||
|
||||
private hasGui_: boolean;
|
||||
private middlewareCalls_: any[];
|
||||
private logger_: LoggerWrapper;
|
||||
|
||||
constructor(hasGui = true) {
|
||||
super();
|
||||
this.hasGui_ = hasGui;
|
||||
@@ -716,7 +737,7 @@ class TestApp extends BaseApplication {
|
||||
return this.hasGui_;
|
||||
}
|
||||
|
||||
async start(argv) {
|
||||
async start(argv: any[]) {
|
||||
this.logger_.info('Test app starting...');
|
||||
|
||||
if (!argv.includes('--profile')) {
|
||||
@@ -737,7 +758,7 @@ class TestApp extends BaseApplication {
|
||||
this.logger_.info('Test app started...');
|
||||
}
|
||||
|
||||
async generalMiddleware(store, next, action) {
|
||||
async generalMiddleware(store: any, next: any, action: any) {
|
||||
this.middlewareCalls_.push(true);
|
||||
try {
|
||||
await super.generalMiddleware(store, next, action);
|
||||
@@ -771,4 +792,4 @@ class TestApp extends BaseApplication {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
|
||||
module.exports = { exportDir, newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
|
||||
@@ -85,7 +85,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
}
|
||||
editorRef.current.setSelections(newSelections);
|
||||
}
|
||||
editorRef.current.focus();
|
||||
}, []);
|
||||
|
||||
const addListItem = useCallback((string1, defaultText = '') => {
|
||||
@@ -97,7 +96,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
} else {
|
||||
wrapSelectionWithStrings(string1, '', defaultText);
|
||||
}
|
||||
editorRef.current.focus();
|
||||
}
|
||||
}, [wrapSelectionWithStrings]);
|
||||
|
||||
@@ -141,7 +139,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
} else {
|
||||
reg.logger().warn('CodeMirror: unsupported drop item: ', cmd);
|
||||
}
|
||||
} else if (cmd.name === 'focus') {
|
||||
} else if (cmd.name === 'editor.focus') {
|
||||
editorRef.current.focus();
|
||||
} else {
|
||||
commandProcessed = false;
|
||||
@@ -170,6 +168,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
textItalic: () => wrapSelectionWithStrings('*', '*', _('emphasised text')),
|
||||
textLink: async () => {
|
||||
const url = await dialogs.prompt(_('Insert Hyperlink'));
|
||||
editorRef.current.focus();
|
||||
if (url) wrapSelectionWithStrings('[', `](${url})`);
|
||||
},
|
||||
textCode: () => {
|
||||
|
||||
@@ -247,7 +247,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
if (cmd.name === 'insertText') {
|
||||
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, cmd.value, { bodyOnly: true });
|
||||
editor.insertContent(result.html);
|
||||
} else if (cmd.name === 'focus') {
|
||||
} else if (cmd.name === 'editor.focus') {
|
||||
editor.focus();
|
||||
} else if (cmd.name === 'dropItems') {
|
||||
if (cmd.value.type === 'notes') {
|
||||
|
||||
@@ -283,10 +283,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote);
|
||||
|
||||
const introductionPostLinkClick = useCallback(() => {
|
||||
bridge().openExternal('https://www.patreon.com/posts/34246624');
|
||||
}, []);
|
||||
|
||||
const externalEditWatcher_noteChange = useCallback((event) => {
|
||||
if (event.id === formNote.id) {
|
||||
const newFormNote = {
|
||||
@@ -414,9 +410,19 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
throw new Error(`Invalid editor: ${props.bodyEditor}`);
|
||||
}
|
||||
|
||||
const wysiwygBanner = props.bodyEditor !== 'TinyMCE' ? null : (
|
||||
<div style={{ ...styles.warningBanner }}>
|
||||
This is an experimental Rich Text editor for evaluation only. Please do not use with important notes as you may lose some data! See the <a style={styles.urlColor} onClick={introductionPostLinkClick} href="#">introduction post</a> for more information. To switch to the Markdown Editor please press the "Toggle editors" in the top right-hand corner.
|
||||
const onRichTextReadMoreLinkClick = useCallback(() => {
|
||||
bridge().openExternal('https://joplinapp.org/rich_text_editor');
|
||||
}, []);
|
||||
|
||||
const onRichTextDismissLinkClick = useCallback(() => {
|
||||
Setting.setValue('richTextBannerDismissed', true);
|
||||
}, []);
|
||||
|
||||
const wysiwygBanner = props.bodyEditor !== 'TinyMCE' || props.richTextBannerDismissed ? null : (
|
||||
<div style={styles.warningBanner}>
|
||||
{_('This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.')}
|
||||
<a onClick={onRichTextReadMoreLinkClick} style={styles.warningBannerLink} href="#">[ {_('Read more about it')} ]</a>
|
||||
<a onClick={onRichTextDismissLinkClick} style={styles.warningBannerLink} href="#">[ {_('Dismiss')} ]</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -567,6 +573,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
editorNoteStatuses: state.editorNoteStatuses,
|
||||
syncStarted: state.syncStarted,
|
||||
themeId: state.settings.theme,
|
||||
richTextBannerDismissed: state.settings.richTextBannerDismissed,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
notesParentType: state.notesParentType,
|
||||
selectedNoteTags: state.selectedNoteTags,
|
||||
|
||||
@@ -57,6 +57,7 @@ function styles_(props: Props) {
|
||||
fontSize: Math.round(theme.textStyle.fontSize * 1.5),
|
||||
backgroundColor: theme.backgroundColor,
|
||||
border: 'none',
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
titleDate: {
|
||||
|
||||
@@ -92,6 +92,9 @@ const declarations: CommandDeclaration[] = [
|
||||
{
|
||||
name: 'editor.setText',
|
||||
},
|
||||
{
|
||||
name: 'editor.focus',
|
||||
},
|
||||
];
|
||||
|
||||
export default declarations;
|
||||
|
||||
@@ -10,7 +10,7 @@ export const declaration: CommandDeclaration = {
|
||||
export const runtime = (comp: any): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
comp.editorRef.current.execCommand({ name: 'focus' });
|
||||
comp.editorRef.current.execCommand({ name: 'editor.focus' });
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
|
||||
@@ -33,9 +33,16 @@ export default function styles(props: NoteEditorProps) {
|
||||
fontFamily: theme.fontFamily,
|
||||
padding: 10,
|
||||
fontSize: theme.fontSize,
|
||||
lineHeight: '1.6em',
|
||||
marginTop: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
warningBannerLink: {
|
||||
color: theme.color,
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
tinyMCE: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
@@ -55,7 +62,7 @@ export default function styles(props: NoteEditorProps) {
|
||||
padding: 10,
|
||||
marginLeft: 5,
|
||||
marginBottom: 10,
|
||||
color: theme.colorWarn,
|
||||
|
||||
backgroundColor: theme.warningBackgroundColor,
|
||||
},
|
||||
resourceWatchBannerLine: {
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface NoteEditorProps {
|
||||
plugins: PluginStates;
|
||||
toolbarButtonInfos: ToolbarButtonInfo[];
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
richTextBannerDismissed: boolean;
|
||||
}
|
||||
|
||||
export interface NoteBodyEditorProps {
|
||||
|
||||
@@ -161,7 +161,7 @@ export default function useFormNote(dependencies: HookDependencies) {
|
||||
if (Setting.value(focusSettingName) === 'title') {
|
||||
if (titleInputRef.current) titleInputRef.current.focus();
|
||||
} else {
|
||||
if (editorRef.current) editorRef.current.execCommand({ name: 'focus' });
|
||||
if (editorRef.current) editorRef.current.execCommand({ name: 'editor.focus' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
16
packages/app-desktop/package-lock.json
generated
16
packages/app-desktop/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -2119,6 +2119,7 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
|
||||
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
@@ -2128,6 +2129,7 @@
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
@@ -2204,12 +2206,14 @@
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.55.tgz",
|
||||
"integrity": "sha512-6KLe6lkILeRwyyy7yG9rULKJ0sXplUsl98MGoCfpteXf9sPWFWWMknDcsvubcpaTdBuxtsLF6HDUwdApZL/xIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
@@ -2218,7 +2222,8 @@
|
||||
"csstype": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz",
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA=="
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2226,6 +2231,7 @@
|
||||
"version": "0.63.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.34.tgz",
|
||||
"integrity": "sha512-6syTIfUt+DY4mJBoO0Y5i4jsDg0I7v31XIPRgsJZAlHeMY9p9GehtGd4VpQKB/NgzLiGzx9ahJPE8w+0lt/WxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@@ -2306,6 +2312,7 @@
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.4.tgz",
|
||||
"integrity": "sha512-78f5Zuy0v/LTQNOYfpH+CINHpchzMMmAt9amY2YNtSgsk1TmlKm8L2Wijss/mtTrsUAVTm2CdGB8VOM65vA8xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/hoist-non-react-statics": "*",
|
||||
"@types/react": "*",
|
||||
@@ -2316,7 +2323,8 @@
|
||||
"csstype": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.4.tgz",
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA=="
|
||||
"integrity": "sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dist": "node_modules/.bin/electron-builder",
|
||||
"dist": "npm run electronRebuild && npx electron-builder",
|
||||
"build": "gulp build",
|
||||
"postinstall": "npm run build && gulp electronRebuild",
|
||||
"postinstall": "npm run build",
|
||||
"electronRebuild": "gulp electronRebuild",
|
||||
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
|
||||
"watch": "node node_modules/typescript/bin/tsc --watch --project tsconfig.json",
|
||||
"start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools",
|
||||
@@ -98,6 +99,7 @@
|
||||
"@types/node": "^14.14.6",
|
||||
"@types/react": "16.9.55",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/styled-components": "^5.1.4",
|
||||
"ajv": "^6.5.0",
|
||||
"app-builder-bin": "^1.9.11",
|
||||
"babel-cli": "^6.26.0",
|
||||
@@ -122,7 +124,6 @@
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"@joplin/lib": "^1.0.9",
|
||||
"@joplin/renderer": "^1.0.17",
|
||||
"@types/styled-components": "^5.1.4",
|
||||
"async-mutex": "^0.1.3",
|
||||
"codemirror": "^5.56.0",
|
||||
"color": "^3.1.2",
|
||||
|
||||
@@ -2,6 +2,24 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const electron_notarize = require('electron-notarize');
|
||||
|
||||
function execCommand(command) {
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
if (error.signal == 'SIGTERM') {
|
||||
resolve('Process was killed');
|
||||
} else {
|
||||
reject(new Error([stdout.trim(), stderr.trim()].join('\n')));
|
||||
}
|
||||
} else {
|
||||
resolve([stdout.trim(), stderr.trim()].join('\n'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = async function(params) {
|
||||
if (process.platform !== 'darwin') return;
|
||||
|
||||
@@ -48,5 +66,16 @@ module.exports = async function(params) {
|
||||
ascProvider: process.env.APPLE_ASC_PROVIDER,
|
||||
});
|
||||
|
||||
// It appears that electron-notarize doesn't staple the app, but without
|
||||
// this we were still getting the malware warning when launching the app.
|
||||
// Stapling the app means attaching the notarization ticket to it, so that
|
||||
// if the user is offline, macOS can still check if the app was notarized.
|
||||
// So it seems to be more or less optional, but at least in our case it
|
||||
// wasn't.
|
||||
console.log('Staple notarization ticket to the app...');
|
||||
const staplerCmd = `xcrun stapler staple "${appPath}"`;
|
||||
console.log(`> ${staplerCmd}`);
|
||||
console.log(await execCommand(staplerCmd));
|
||||
|
||||
console.log(`Done notarizing ${appId}`);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import net.cozic.joplin.share.SharePackage;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
@@ -68,6 +69,15 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
|
||||
// To allow debugging the webview using the Chrome developer tools.
|
||||
// Open chrome://inspect/#devices to view the device and connect to it
|
||||
// IMPORTANT: USB debugging must be enabled on the device for it to work.
|
||||
// https://github.com/react-native-webview/react-native-webview/blob/master/docs/Debugging.md
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
WebView.setWebContentsDebuggingEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,14 +15,6 @@ interface UseSourceResult {
|
||||
injectedJs: string[];
|
||||
}
|
||||
|
||||
let markupToHtml_: any = null;
|
||||
|
||||
function markupToHtml() {
|
||||
if (markupToHtml_) return markupToHtml_;
|
||||
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
|
||||
return markupToHtml_;
|
||||
}
|
||||
|
||||
function usePrevious(value: any, initialValue: any = null): any {
|
||||
const ref = useRef(initialValue);
|
||||
useEffect(() => {
|
||||
@@ -45,6 +37,10 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
||||
};
|
||||
}, [themeId, paddingBottom]);
|
||||
|
||||
const markupToHtml = useMemo(() => {
|
||||
return markupLanguageUtils.newMarkupToHtml();
|
||||
}, [isFirstRender]);
|
||||
|
||||
// To address https://github.com/laurent22/joplin/issues/433
|
||||
//
|
||||
// If a checkbox in a note is ticked, the body changes, which normally
|
||||
@@ -58,7 +54,7 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
||||
//
|
||||
// IMPORTANT: KEEP noteBody AS THE FIRST dependency in the array as the
|
||||
// below logic rely on this.
|
||||
const effectDependencies = [noteBody, resourceLoadedTime, noteMarkupLanguage, themeId, rendererTheme, highlightedKeywords, noteResources, noteHash, isFirstRender];
|
||||
const effectDependencies = [noteBody, resourceLoadedTime, noteMarkupLanguage, themeId, rendererTheme, highlightedKeywords, noteResources, noteHash, isFirstRender, markupToHtml];
|
||||
const previousDeps = usePrevious(effectDependencies, []);
|
||||
const changedDeps = effectDependencies.reduce((accum: any, dependency: any, index: any) => {
|
||||
if (dependency !== previousDeps[index]) {
|
||||
@@ -94,9 +90,9 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
||||
// it doesn't contain info about the resource download state. Because of that, if we were to use the markupToHtml() cache
|
||||
// it wouldn't re-render at all. We don't need this cache in any way because this hook is only triggered when we know
|
||||
// something has changed.
|
||||
markupToHtml().clearCache(noteMarkupLanguage);
|
||||
markupToHtml.clearCache(noteMarkupLanguage);
|
||||
|
||||
const result = await markupToHtml().render(
|
||||
const result = await markupToHtml.render(
|
||||
noteMarkupLanguage,
|
||||
bodyToRender,
|
||||
rendererTheme,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const RNFS = require('react-native-fs');
|
||||
const FsDriverBase = require('@joplin/lib/fs-driver-base');
|
||||
const FsDriverBase = require('@joplin/lib/fs-driver-base').default;
|
||||
const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
|
||||
class FsDriverRN extends FsDriverBase {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const TurndownService = require('joplin-turndown');
|
||||
const TurndownService = require('@joplin/turndown');
|
||||
const turndownPluginGfm = require('@joplin/turndown-plugin-gfm').gfm;
|
||||
const markdownUtils = require('./markdownUtils').default;
|
||||
|
||||
class HtmlToMd {
|
||||
parse(html, options = {}) {
|
||||
const turndownPluginGfm = require('joplin-turndown-plugin-gfm').gfm;
|
||||
const turndown = new TurndownService({
|
||||
headingStyle: 'atx',
|
||||
anchorNames: options.anchorNames ? options.anchorNames.map(n => n.trim().toLowerCase()) : [],
|
||||
@@ -12,6 +12,7 @@ class HtmlToMd {
|
||||
bulletListMarker: '-',
|
||||
emDelimiter: '*',
|
||||
strongDelimiter: '**',
|
||||
br: '',
|
||||
});
|
||||
turndown.use(turndownPluginGfm);
|
||||
turndown.remove('script');
|
||||
|
||||
@@ -121,6 +121,10 @@ export default class Synchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
private static reportHasErrors(report: any): boolean {
|
||||
return !!report && !!report.errors && !!report.errors.length;
|
||||
}
|
||||
|
||||
static reportToLines(report: any) {
|
||||
const lines = [];
|
||||
if (report.createLocal) lines.push(_('Created local items: %d.', report.createLocal));
|
||||
@@ -132,7 +136,7 @@ export default class Synchronizer {
|
||||
if (report.fetchingTotal && report.fetchingProcessed) lines.push(_('Fetched items: %d/%d.', report.fetchingProcessed, report.fetchingTotal));
|
||||
if (report.cancelling && !report.completedTime) lines.push(_('Cancelling...'));
|
||||
if (report.completedTime) lines.push(_('Completed: %s', time.formatMsToLocal(report.completedTime)));
|
||||
if (report.errors && report.errors.length) lines.push(_('Last error: %s', report.errors[report.errors.length - 1].toString().substr(0, 500)));
|
||||
if (this.reportHasErrors(report)) lines.push(_('Last error: %s', report.errors[report.errors.length - 1].toString().substr(0, 500)));
|
||||
|
||||
return lines;
|
||||
}
|
||||
@@ -193,7 +197,7 @@ export default class Synchronizer {
|
||||
this.logger().info(`Total notes: ${noteCount}`);
|
||||
this.logger().info(`Total resources: ${resourceCount}`);
|
||||
|
||||
if (report.errors && report.errors.length) {
|
||||
if (Synchronizer.reportHasErrors(report)) {
|
||||
this.logger().warn('There was some errors:');
|
||||
for (let i = 0; i < report.errors.length; i++) {
|
||||
const e = report.errors[i];
|
||||
@@ -315,6 +319,7 @@ export default class Synchronizer {
|
||||
const outputContext = Object.assign({}, lastContext);
|
||||
|
||||
this.dispatch({ type: 'SYNC_STARTED' });
|
||||
eventManager.emit('syncStart');
|
||||
|
||||
this.logSyncOperation('starting', null, null, `Starting synchronisation to target ${syncTargetId}... [${synchronizationId}]`);
|
||||
|
||||
@@ -899,11 +904,14 @@ export default class Synchronizer {
|
||||
|
||||
await this.logSyncSummary(this.progressReport_);
|
||||
|
||||
eventManager.emit('syncComplete', {
|
||||
withErrors: Synchronizer.reportHasErrors(this.progressReport_),
|
||||
});
|
||||
|
||||
this.onProgress_ = function() {};
|
||||
this.progressReport_ = {};
|
||||
|
||||
this.dispatch({ type: 'SYNC_COMPLETED', isFullSync: this.isFullSync(syncSteps) });
|
||||
eventManager.emit('syncComplete');
|
||||
|
||||
this.state_ = 'idle';
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ describe('dummy', () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,48 @@
|
||||
const { filename, fileExtension } = require('./path-utils');
|
||||
const time = require('./time').default;
|
||||
const Setting = require('./models/Setting').default;
|
||||
import time from './time';
|
||||
import Setting from './models/Setting';
|
||||
import { filename, fileExtension } from './path-utils';
|
||||
const md5 = require('md5');
|
||||
|
||||
class FsDriverBase {
|
||||
async isDirectory(path) {
|
||||
export interface Stat {
|
||||
birthtime: number;
|
||||
mtime: number;
|
||||
isDirectory(): boolean;
|
||||
path: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface ReadDirStatsOptions {
|
||||
recursive: boolean;
|
||||
}
|
||||
|
||||
export default class FsDriverBase {
|
||||
|
||||
public async stat(_path: string): Promise<Stat> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async readDirStats(_path: string, _options: ReadDirStatsOptions = null): Promise<Stat[]> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async exists(_path: string): Promise<boolean> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async remove(_path: string): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public async isDirectory(path: string) {
|
||||
const stat = await this.stat(path);
|
||||
return !stat ? false : stat.isDirectory();
|
||||
}
|
||||
|
||||
async readDirStatsHandleRecursion_(basePath, stat, output, options) {
|
||||
public async writeFile(_path: string, _content: string, _encoding: string = 'base64'): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
protected async readDirStatsHandleRecursion_(basePath: string, stat: Stat, output: Stat[], options: ReadDirStatsOptions): Promise<Stat[]> {
|
||||
if (options.recursive && stat.isDirectory()) {
|
||||
const subPath = `${basePath}/${stat.path}`;
|
||||
const subStats = await this.readDirStats(subPath, options);
|
||||
@@ -23,7 +56,7 @@ class FsDriverBase {
|
||||
return output;
|
||||
}
|
||||
|
||||
async findUniqueFilename(name, reservedNames = null) {
|
||||
public async findUniqueFilename(name: string, reservedNames: string[] = null): Promise<string> {
|
||||
if (reservedNames === null) {
|
||||
reservedNames = [];
|
||||
}
|
||||
@@ -47,7 +80,7 @@ class FsDriverBase {
|
||||
}
|
||||
}
|
||||
|
||||
async removeAllThatStartWith(dirPath, filenameStart) {
|
||||
public async removeAllThatStartWith(dirPath: string, filenameStart: string) {
|
||||
if (!filenameStart || !dirPath) throw new Error('dirPath and filenameStart cannot be empty');
|
||||
|
||||
const stats = await this.readDirStats(dirPath);
|
||||
@@ -59,7 +92,7 @@ class FsDriverBase {
|
||||
}
|
||||
}
|
||||
|
||||
async waitTillExists(path, timeout = 10000) {
|
||||
public async waitTillExists(path: string, timeout: number = 10000) {
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
@@ -72,7 +105,7 @@ class FsDriverBase {
|
||||
|
||||
// TODO: move out of here and make it part of joplin-renderer
|
||||
// or assign to option using .bind(fsDriver())
|
||||
async cacheCssToFile(cssStrings) {
|
||||
public async cacheCssToFile(cssStrings: string[]) {
|
||||
const cssString = Array.isArray(cssStrings) ? cssStrings.join('\n') : cssStrings;
|
||||
const cssFilePath = `${Setting.value('tempDir')}/${md5(escape(cssString))}.css`;
|
||||
if (!(await this.exists(cssFilePath))) {
|
||||
@@ -84,6 +117,5 @@ class FsDriverBase {
|
||||
mime: 'text/css',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FsDriverBase;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { resolve as nodeResolve } from 'path';
|
||||
import FsDriverBase, { Stat } from './fs-driver-base';
|
||||
import time from './time';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const time = require('./time').default;
|
||||
const FsDriverBase = require('./fs-driver-base');
|
||||
|
||||
export default class FsDriverNode extends FsDriverBase {
|
||||
|
||||
@@ -125,7 +125,7 @@ export default class FsDriverNode extends FsDriverBase {
|
||||
throw this.fsErrorToJsError_(error);
|
||||
}
|
||||
|
||||
let output = [];
|
||||
let output: Stat[] = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const stat = await this.stat(`${path}/${item}`);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
packages/lib/locales/fi_FI.json
Normal file
1
packages/lib/locales/fi_FI.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
const locales = {};
|
||||
const stats = {};
|
||||
var locales = {};
|
||||
var stats = {};
|
||||
locales['en_GB'] = require('./en_GB.json');
|
||||
locales['ar'] = require('./ar.json');
|
||||
locales['bg_BG'] = require('./bg_BG.json');
|
||||
@@ -15,6 +15,7 @@ locales['es_ES'] = require('./es_ES.json');
|
||||
locales['et_EE'] = require('./et_EE.json');
|
||||
locales['eu'] = require('./eu.json');
|
||||
locales['fa'] = require('./fa.json');
|
||||
locales['fi_FI'] = require('./fi_FI.json');
|
||||
locales['fr_FR'] = require('./fr_FR.json');
|
||||
locales['gl_ES'] = require('./gl_ES.json');
|
||||
locales['hr_HR'] = require('./hr_HR.json');
|
||||
@@ -38,42 +39,43 @@ locales['tr_TR'] = require('./tr_TR.json');
|
||||
locales['vi'] = require('./vi.json');
|
||||
locales['zh_CN'] = require('./zh_CN.json');
|
||||
locales['zh_TW'] = require('./zh_TW.json');
|
||||
stats['ar'] = { 'percentDone': 78 };
|
||||
stats['eu'] = { 'percentDone': 33 };
|
||||
stats['bs_BA'] = { 'percentDone': 82 };
|
||||
stats['bg_BG'] = { 'percentDone': 65 };
|
||||
stats['ca'] = { 'percentDone': 95 };
|
||||
stats['hr_HR'] = { 'percentDone': 27 };
|
||||
stats['cs_CZ'] = { 'percentDone': 98 };
|
||||
stats['da_DK'] = { 'percentDone': 72 };
|
||||
stats['de_DE'] = { 'percentDone': 97 };
|
||||
stats['et_EE'] = { 'percentDone': 65 };
|
||||
stats['en_GB'] = { 'percentDone': 100 };
|
||||
stats['en_US'] = { 'percentDone': 100 };
|
||||
stats['es_ES'] = { 'percentDone': 98 };
|
||||
stats['eo'] = { 'percentDone': 37 };
|
||||
stats['fr_FR'] = { 'percentDone': 98 };
|
||||
stats['gl_ES'] = { 'percentDone': 42 };
|
||||
stats['id_ID'] = { 'percentDone': 92 };
|
||||
stats['it_IT'] = { 'percentDone': 98 };
|
||||
stats['nl_NL'] = { 'percentDone': 94 };
|
||||
stats['nl_BE'] = { 'percentDone': 33 };
|
||||
stats['nb_NO'] = { 'percentDone': 87 };
|
||||
stats['fa'] = { 'percentDone': 82 };
|
||||
stats['pl_PL'] = { 'percentDone': 97 };
|
||||
stats['pt_PT'] = { 'percentDone': 98 };
|
||||
stats['pt_BR'] = { 'percentDone': 96 };
|
||||
stats['ro'] = { 'percentDone': 77 };
|
||||
stats['sl_SI'] = { 'percentDone': 41 };
|
||||
stats['sv'] = { 'percentDone': 70 };
|
||||
stats['th_TH'] = { 'percentDone': 52 };
|
||||
stats['vi'] = { 'percentDone': 84 };
|
||||
stats['tr_TR'] = { 'percentDone': 97 };
|
||||
stats['el_GR'] = { 'percentDone': 95 };
|
||||
stats['ru_RU'] = { 'percentDone': 94 };
|
||||
stats['sr_RS'] = { 'percentDone': 70 };
|
||||
stats['zh_CN'] = { 'percentDone': 95 };
|
||||
stats['zh_TW'] = { 'percentDone': 94 };
|
||||
stats['ja_JP'] = { 'percentDone': 99 };
|
||||
stats['ko'] = { 'percentDone': 99 };
|
||||
module.exports = { locales: locales, stats: stats };
|
||||
stats['ar'] = {"percentDone":77};
|
||||
stats['eu'] = {"percentDone":33};
|
||||
stats['bs_BA'] = {"percentDone":79};
|
||||
stats['bg_BG'] = {"percentDone":64};
|
||||
stats['ca'] = {"percentDone":92};
|
||||
stats['hr_HR'] = {"percentDone":26};
|
||||
stats['cs_CZ'] = {"percentDone":96};
|
||||
stats['da_DK'] = {"percentDone":79};
|
||||
stats['de_DE'] = {"percentDone":96};
|
||||
stats['et_EE'] = {"percentDone":63};
|
||||
stats['en_GB'] = {"percentDone":100};
|
||||
stats['en_US'] = {"percentDone":100};
|
||||
stats['es_ES'] = {"percentDone":96};
|
||||
stats['eo'] = {"percentDone":36};
|
||||
stats['fi_FI'] = {"percentDone":97};
|
||||
stats['fr_FR'] = {"percentDone":96};
|
||||
stats['gl_ES'] = {"percentDone":42};
|
||||
stats['id_ID'] = {"percentDone":88};
|
||||
stats['it_IT'] = {"percentDone":97};
|
||||
stats['nl_BE'] = {"percentDone":33};
|
||||
stats['nl_NL'] = {"percentDone":96};
|
||||
stats['nb_NO'] = {"percentDone":85};
|
||||
stats['fa'] = {"percentDone":79};
|
||||
stats['pl_PL'] = {"percentDone":95};
|
||||
stats['pt_BR'] = {"percentDone":93};
|
||||
stats['pt_PT'] = {"percentDone":95};
|
||||
stats['ro'] = {"percentDone":74};
|
||||
stats['sl_SI'] = {"percentDone":41};
|
||||
stats['sv'] = {"percentDone":68};
|
||||
stats['th_TH'] = {"percentDone":50};
|
||||
stats['vi'] = {"percentDone":82};
|
||||
stats['tr_TR'] = {"percentDone":94};
|
||||
stats['el_GR'] = {"percentDone":92};
|
||||
stats['ru_RU'] = {"percentDone":92};
|
||||
stats['sr_RS'] = {"percentDone":69};
|
||||
stats['zh_CN'] = {"percentDone":97};
|
||||
stats['zh_TW'] = {"percentDone":91};
|
||||
stats['ja_JP'] = {"percentDone":97};
|
||||
stats['ko'] = {"percentDone":97};
|
||||
module.exports = { locales: locales, stats: stats };
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -171,7 +171,7 @@ class Note extends BaseItem {
|
||||
useAbsolutePaths: false,
|
||||
}, options);
|
||||
|
||||
const pathsToTry = [];
|
||||
let pathsToTry = [];
|
||||
if (options.useAbsolutePaths) {
|
||||
pathsToTry.push(`file://${Setting.value('resourceDir')}`);
|
||||
pathsToTry.push(`file://${shim.pathRelativeToCwd(Setting.value('resourceDir'))}`);
|
||||
@@ -179,6 +179,21 @@ class Note extends BaseItem {
|
||||
pathsToTry.push(Resource.baseRelativeDirectoryPath());
|
||||
}
|
||||
|
||||
// We support both the escaped and unescaped versions because both
|
||||
// of those paths are valid:
|
||||
//
|
||||
// [](file://C:/I like spaces in paths/abcdefg.jpg)
|
||||
// [](file://C:/I%20like%20spaces%20in%20paths/abcdefg.jpg)
|
||||
//
|
||||
// https://discourse.joplinapp.org/t/12986/4
|
||||
const temp = [];
|
||||
for (const p of pathsToTry) {
|
||||
temp.push(p);
|
||||
temp.push(markdownUtils.escapeLinkUrl(p));
|
||||
}
|
||||
|
||||
pathsToTry = temp;
|
||||
|
||||
this.logger().debug('replaceResourceExternalToInternalLinks', 'options:', options, 'pathsToTry:', pathsToTry);
|
||||
|
||||
for (const basePath of pathsToTry) {
|
||||
|
||||
@@ -350,6 +350,8 @@ class Setting extends BaseModel {
|
||||
// to the last folder that was selected.
|
||||
activeFolderId: { value: '', type: SettingItemType.String, public: false },
|
||||
|
||||
richTextBannerDismissed: { value: false, type: SettingItemType.Bool, public: false },
|
||||
|
||||
firstStart: { value: true, type: SettingItemType.Bool, public: false },
|
||||
locale: {
|
||||
value: defaultLocale(),
|
||||
@@ -594,6 +596,9 @@ class Setting extends BaseModel {
|
||||
'markdown.plugin.fountain': { value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Fountain syntax support')}${wysiwygYes}` },
|
||||
'markdown.plugin.mermaid': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Mermaid diagrams support')}${wysiwygYes}` },
|
||||
|
||||
'markdown.plugin.audioPlayer': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable audio player')}${wysiwygNo}` },
|
||||
'markdown.plugin.videoPlayer': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable video player')}${wysiwygNo}` },
|
||||
'markdown.plugin.pdfViewer': { value: !mobilePlatform, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['desktop'], label: () => `${_('Enable PDF viewer')}${wysiwygNo}` },
|
||||
'markdown.plugin.mark': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ==mark== syntax')}${wysiwygNo}` },
|
||||
'markdown.plugin.footnote': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable footnotes')}${wysiwygNo}` },
|
||||
'markdown.plugin.toc': { value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable table of contents extension')}${wysiwygNo}` },
|
||||
|
||||
997
packages/lib/package-lock.json
generated
997
packages/lib/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -17,14 +17,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.15",
|
||||
"jest": "^26.6.3",
|
||||
"@types/node": "^14.14.6",
|
||||
"jest": "^26.6.3",
|
||||
"typescript": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.8",
|
||||
"@joplin/fork-sax": "^1.2.12",
|
||||
"@joplin/renderer": "^1.0.17",
|
||||
"@joplin/turndown": "^4.0.30",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.12",
|
||||
"async-mutex": "^0.1.3",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"base-64": "^0.1.0",
|
||||
@@ -33,7 +35,6 @@
|
||||
"chokidar": "^3.4.3",
|
||||
"color": "3.1.2",
|
||||
"compare-versions": "^3.6.0",
|
||||
"diacritics": "^1.3.0",
|
||||
"diff-match-patch": "^1.0.4",
|
||||
"es6-promise-pool": "^2.5.0",
|
||||
"file-uri-to-path": "^1.0.0",
|
||||
@@ -45,8 +46,6 @@
|
||||
"image-data-uri": "^2.0.0",
|
||||
"image-type": "^3.0.0",
|
||||
"immer": "^7.0.14",
|
||||
"joplin-turndown": "^4.0.30",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"levenshtein": "^1.0.5",
|
||||
"lodash": "^4.17.20",
|
||||
"markdown-it": "^10.0.0",
|
||||
|
||||
@@ -1,274 +1,16 @@
|
||||
import eventManager from '../eventManager';
|
||||
import shim from '../shim';
|
||||
import { _ } from '../locale';
|
||||
import keysRegExp from './KeymapService_keysRegExp';
|
||||
import keycodeToElectronMap from './KeymapService_keycodeToElectronMap';
|
||||
|
||||
const BaseService = require('./BaseService').default;
|
||||
|
||||
const keysRegExp = /^([0-9A-Z)!@#$%^&*(:+<_>?~{|}";=,\-./`[\\\]']|F1*[1-9]|F10|F2[0-4]|Plus|Space|Tab|Backspace|Delete|Insert|Return|Enter|Up|Down|Left|Right|Home|End|PageUp|PageDown|Escape|Esc|VolumeUp|VolumeDown|VolumeMute|MediaNextTrack|MediaPreviousTrack|MediaStop|MediaPlayPause|PrintScreen|Numlock|Scrolllock|Capslock|num([0-9]|dec|add|sub|mult|div))$/;
|
||||
const modifiersRegExp = {
|
||||
darwin: /^(Ctrl|Option|Shift|Cmd)$/,
|
||||
default: /^(Ctrl|Alt|AltGr|Shift|Super)$/,
|
||||
};
|
||||
|
||||
const keycodeToElectronMap = [
|
||||
'', // [0]
|
||||
'', // [1]
|
||||
'', // [2]
|
||||
'', // [3]
|
||||
'', // [4]
|
||||
'', // [5]
|
||||
'', // [6]
|
||||
'', // [7]
|
||||
'Backspace', // [8]
|
||||
'Tab', // [9]
|
||||
'', // [10]
|
||||
'', // [11]
|
||||
'Clear', // [12]
|
||||
'Enter', // [13]
|
||||
'', // [14]
|
||||
'', // [15]
|
||||
'Shift', // [16]
|
||||
'Ctrl', // [17]
|
||||
'Alt', // [18]
|
||||
'', // [19]
|
||||
'Capslock', // [20]
|
||||
'', // [21]
|
||||
'', // [22]
|
||||
'', // [23]
|
||||
'', // [24]
|
||||
'', // [25]
|
||||
'', // [26]
|
||||
'Esc', // [27]
|
||||
'', // [28]
|
||||
'', // [29]
|
||||
'', // [30]
|
||||
'', // [31]
|
||||
'Space', // [32]
|
||||
'PageUp', // [33]
|
||||
'PageDown', // [34]
|
||||
'End', // [35]
|
||||
'Home', // [36]
|
||||
'Left', // [37]
|
||||
'Up', // [38]
|
||||
'Right', // [39]
|
||||
'Down', // [40]
|
||||
'', // [41]
|
||||
'', // [42]
|
||||
'', // [43]
|
||||
'PrintScreen', // [44]
|
||||
'Insert', // [45]
|
||||
'Delete', // [46]
|
||||
'', // [47]
|
||||
'0', // [48]
|
||||
'1', // [49]
|
||||
'2', // [50]
|
||||
'3', // [51]
|
||||
'4', // [52]
|
||||
'5', // [53]
|
||||
'6', // [54]
|
||||
'7', // [55]
|
||||
'8', // [56]
|
||||
'9', // [57]
|
||||
':', // [58]
|
||||
';', // [59]
|
||||
'<', // [60]
|
||||
'=', // [61]
|
||||
'>', // [62]
|
||||
'?', // [63]
|
||||
'@', // [64]
|
||||
'A', // [65]
|
||||
'B', // [66]
|
||||
'C', // [67]
|
||||
'D', // [68]
|
||||
'E', // [69]
|
||||
'F', // [70]
|
||||
'G', // [71]
|
||||
'H', // [72]
|
||||
'I', // [73]
|
||||
'J', // [74]
|
||||
'K', // [75]
|
||||
'L', // [76]
|
||||
'M', // [77]
|
||||
'N', // [78]
|
||||
'O', // [79]
|
||||
'P', // [80]
|
||||
'Q', // [81]
|
||||
'R', // [82]
|
||||
'S', // [83]
|
||||
'T', // [84]
|
||||
'U', // [85]
|
||||
'V', // [86]
|
||||
'W', // [87]
|
||||
'X', // [88]
|
||||
'Y', // [89]
|
||||
'Z', // [90]
|
||||
'Super', // [91] Super: Windows Key (Windows) or Command Key (Mac)
|
||||
'', // [92]
|
||||
'ContextMenu', // [93]
|
||||
'', // [94]
|
||||
'', // [95]
|
||||
'num0', // [96]
|
||||
'num1', // [97]
|
||||
'num2', // [98]
|
||||
'num3', // [99]
|
||||
'num4', // [100]
|
||||
'num5', // [101]
|
||||
'num6', // [102]
|
||||
'num7', // [103]
|
||||
'num8', // [104]
|
||||
'num9', // [105]
|
||||
'nummult', // [106] *
|
||||
'numadd', // [107] +
|
||||
'', // [108]
|
||||
'numsub', // [109] -
|
||||
'numdec', // [110]
|
||||
'numdiv', // [111] ÷
|
||||
'F1', // [112]
|
||||
'F2', // [113]
|
||||
'F3', // [114]
|
||||
'F4', // [115]
|
||||
'F5', // [116]
|
||||
'F6', // [117]
|
||||
'F7', // [118]
|
||||
'F8', // [119]
|
||||
'F9', // [120]
|
||||
'F10', // [121]
|
||||
'F11', // [122]
|
||||
'F12', // [123]
|
||||
'F13', // [124]
|
||||
'F14', // [125]
|
||||
'F15', // [126]
|
||||
'F16', // [127]
|
||||
'F17', // [128]
|
||||
'F18', // [129]
|
||||
'F19', // [130]
|
||||
'F20', // [131]
|
||||
'F21', // [132]
|
||||
'F22', // [133]
|
||||
'F23', // [134]
|
||||
'F24', // [135]
|
||||
'', // [136]
|
||||
'', // [137]
|
||||
'', // [138]
|
||||
'', // [139]
|
||||
'', // [140]
|
||||
'', // [141]
|
||||
'', // [142]
|
||||
'', // [143]
|
||||
'Numlock', // [144]
|
||||
'Scrolllock', // [145]
|
||||
'', // [146]
|
||||
'', // [147]
|
||||
'', // [148]
|
||||
'', // [149]
|
||||
'', // [150]
|
||||
'', // [151]
|
||||
'', // [152]
|
||||
'', // [153]
|
||||
'', // [154]
|
||||
'', // [155]
|
||||
'', // [156]
|
||||
'', // [157]
|
||||
'', // [158]
|
||||
'', // [159]
|
||||
'', // [160]
|
||||
'!', // [161]
|
||||
'"', // [162]
|
||||
'#', // [163]
|
||||
'$', // [164]
|
||||
'%', // [165]
|
||||
'&', // [166]
|
||||
'_', // [167]
|
||||
'(', // [168]
|
||||
')', // [169]
|
||||
'*', // [170]
|
||||
'Plus', // [171]
|
||||
'|', // [172]
|
||||
'-', // [173]
|
||||
'{', // [174]
|
||||
'}', // [175]
|
||||
'~', // [176]
|
||||
'', // [177]
|
||||
'', // [178]
|
||||
'', // [179]
|
||||
'', // [180]
|
||||
'VolumeMute', // [181]
|
||||
'VolumeDown', // [182]
|
||||
'VolumeUp', // [183]
|
||||
'', // [184]
|
||||
'', // [185]
|
||||
';', // [186]
|
||||
'=', // [187]
|
||||
',', // [188]
|
||||
'-', // [189]
|
||||
'.', // [190]
|
||||
'/', // [191]
|
||||
'`', // [192]
|
||||
'', // [193]
|
||||
'', // [194]
|
||||
'', // [195]
|
||||
'', // [196]
|
||||
'', // [197]
|
||||
'', // [198]
|
||||
'', // [199]
|
||||
'', // [200]
|
||||
'', // [201]
|
||||
'', // [202]
|
||||
'', // [203]
|
||||
'', // [204]
|
||||
'', // [205]
|
||||
'', // [206]
|
||||
'', // [207]
|
||||
'', // [208]
|
||||
'', // [209]
|
||||
'', // [210]
|
||||
'', // [211]
|
||||
'', // [212]
|
||||
'', // [213]
|
||||
'', // [214]
|
||||
'', // [215]
|
||||
'', // [216]
|
||||
'', // [217]
|
||||
'', // [218]
|
||||
'[', // [219]
|
||||
'\\', // [220]
|
||||
']', // [221]
|
||||
'\'', // [222]
|
||||
'', // [223]
|
||||
'', // [224]
|
||||
'AltGr', // [225]
|
||||
'', // [226]
|
||||
'', // [227]
|
||||
'', // [228]
|
||||
'', // [229]
|
||||
'', // [230]
|
||||
'', // [231]
|
||||
'', // [232]
|
||||
'', // [233]
|
||||
'', // [234]
|
||||
'', // [235]
|
||||
'', // [236]
|
||||
'', // [237]
|
||||
'', // [238]
|
||||
'', // [239]
|
||||
'', // [240]
|
||||
'', // [241]
|
||||
'', // [242]
|
||||
'', // [243]
|
||||
'', // [244]
|
||||
'', // [245]
|
||||
'', // [246]
|
||||
'', // [247]
|
||||
'', // [248]
|
||||
'', // [249]
|
||||
'', // [250]
|
||||
'', // [251]
|
||||
'', // [252]
|
||||
'', // [253]
|
||||
'', // [254]
|
||||
'', // [255]
|
||||
];
|
||||
|
||||
const defaultKeymapItems = {
|
||||
darwin: [
|
||||
{ accelerator: 'Cmd+N', command: 'newNote' },
|
||||
|
||||
262
packages/lib/services/KeymapService_keycodeToElectronMap.ts
Normal file
262
packages/lib/services/KeymapService_keycodeToElectronMap.ts
Normal file
@@ -0,0 +1,262 @@
|
||||
// This is the keycode map for Electron.
|
||||
|
||||
const keycodeToElectronMap = [
|
||||
'', // [0]
|
||||
'', // [1]
|
||||
'', // [2]
|
||||
'', // [3]
|
||||
'', // [4]
|
||||
'', // [5]
|
||||
'', // [6]
|
||||
'', // [7]
|
||||
'Backspace', // [8]
|
||||
'Tab', // [9]
|
||||
'', // [10]
|
||||
'', // [11]
|
||||
'Clear', // [12]
|
||||
'Enter', // [13]
|
||||
'', // [14]
|
||||
'', // [15]
|
||||
'Shift', // [16]
|
||||
'Ctrl', // [17]
|
||||
'Alt', // [18]
|
||||
'', // [19]
|
||||
'Capslock', // [20]
|
||||
'', // [21]
|
||||
'', // [22]
|
||||
'', // [23]
|
||||
'', // [24]
|
||||
'', // [25]
|
||||
'', // [26]
|
||||
'Esc', // [27]
|
||||
'', // [28]
|
||||
'', // [29]
|
||||
'', // [30]
|
||||
'', // [31]
|
||||
'Space', // [32]
|
||||
'PageUp', // [33]
|
||||
'PageDown', // [34]
|
||||
'End', // [35]
|
||||
'Home', // [36]
|
||||
'Left', // [37]
|
||||
'Up', // [38]
|
||||
'Right', // [39]
|
||||
'Down', // [40]
|
||||
'', // [41]
|
||||
'', // [42]
|
||||
'', // [43]
|
||||
'PrintScreen', // [44]
|
||||
'Insert', // [45]
|
||||
'Delete', // [46]
|
||||
'', // [47]
|
||||
'0', // [48]
|
||||
'1', // [49]
|
||||
'2', // [50]
|
||||
'3', // [51]
|
||||
'4', // [52]
|
||||
'5', // [53]
|
||||
'6', // [54]
|
||||
'7', // [55]
|
||||
'8', // [56]
|
||||
'9', // [57]
|
||||
':', // [58]
|
||||
';', // [59]
|
||||
'<', // [60]
|
||||
'=', // [61]
|
||||
'>', // [62]
|
||||
'?', // [63]
|
||||
'@', // [64]
|
||||
'A', // [65]
|
||||
'B', // [66]
|
||||
'C', // [67]
|
||||
'D', // [68]
|
||||
'E', // [69]
|
||||
'F', // [70]
|
||||
'G', // [71]
|
||||
'H', // [72]
|
||||
'I', // [73]
|
||||
'J', // [74]
|
||||
'K', // [75]
|
||||
'L', // [76]
|
||||
'M', // [77]
|
||||
'N', // [78]
|
||||
'O', // [79]
|
||||
'P', // [80]
|
||||
'Q', // [81]
|
||||
'R', // [82]
|
||||
'S', // [83]
|
||||
'T', // [84]
|
||||
'U', // [85]
|
||||
'V', // [86]
|
||||
'W', // [87]
|
||||
'X', // [88]
|
||||
'Y', // [89]
|
||||
'Z', // [90]
|
||||
'Super', // [91] Super: Windows Key (Windows) or Command Key (Mac)
|
||||
'', // [92]
|
||||
'ContextMenu', // [93]
|
||||
'', // [94]
|
||||
'', // [95]
|
||||
'num0', // [96]
|
||||
'num1', // [97]
|
||||
'num2', // [98]
|
||||
'num3', // [99]
|
||||
'num4', // [100]
|
||||
'num5', // [101]
|
||||
'num6', // [102]
|
||||
'num7', // [103]
|
||||
'num8', // [104]
|
||||
'num9', // [105]
|
||||
'nummult', // [106] *
|
||||
'numadd', // [107] +
|
||||
'', // [108]
|
||||
'numsub', // [109] -
|
||||
'numdec', // [110]
|
||||
'numdiv', // [111] ÷
|
||||
'F1', // [112]
|
||||
'F2', // [113]
|
||||
'F3', // [114]
|
||||
'F4', // [115]
|
||||
'F5', // [116]
|
||||
'F6', // [117]
|
||||
'F7', // [118]
|
||||
'F8', // [119]
|
||||
'F9', // [120]
|
||||
'F10', // [121]
|
||||
'F11', // [122]
|
||||
'F12', // [123]
|
||||
'F13', // [124]
|
||||
'F14', // [125]
|
||||
'F15', // [126]
|
||||
'F16', // [127]
|
||||
'F17', // [128]
|
||||
'F18', // [129]
|
||||
'F19', // [130]
|
||||
'F20', // [131]
|
||||
'F21', // [132]
|
||||
'F22', // [133]
|
||||
'F23', // [134]
|
||||
'F24', // [135]
|
||||
'', // [136]
|
||||
'', // [137]
|
||||
'', // [138]
|
||||
'', // [139]
|
||||
'', // [140]
|
||||
'', // [141]
|
||||
'', // [142]
|
||||
'', // [143]
|
||||
'Numlock', // [144]
|
||||
'Scrolllock', // [145]
|
||||
'', // [146]
|
||||
'', // [147]
|
||||
'', // [148]
|
||||
'', // [149]
|
||||
'', // [150]
|
||||
'', // [151]
|
||||
'', // [152]
|
||||
'', // [153]
|
||||
'', // [154]
|
||||
'', // [155]
|
||||
'', // [156]
|
||||
'', // [157]
|
||||
'', // [158]
|
||||
'', // [159]
|
||||
'', // [160]
|
||||
'!', // [161]
|
||||
'"', // [162]
|
||||
'#', // [163]
|
||||
'$', // [164]
|
||||
'%', // [165]
|
||||
'&', // [166]
|
||||
'_', // [167]
|
||||
'(', // [168]
|
||||
')', // [169]
|
||||
'*', // [170]
|
||||
'Plus', // [171]
|
||||
'|', // [172]
|
||||
'-', // [173]
|
||||
'{', // [174]
|
||||
'}', // [175]
|
||||
'~', // [176]
|
||||
'', // [177]
|
||||
'', // [178]
|
||||
'', // [179]
|
||||
'', // [180]
|
||||
'VolumeMute', // [181]
|
||||
'VolumeDown', // [182]
|
||||
'VolumeUp', // [183]
|
||||
'', // [184]
|
||||
'', // [185]
|
||||
';', // [186]
|
||||
'=', // [187]
|
||||
',', // [188]
|
||||
'-', // [189]
|
||||
'.', // [190]
|
||||
'/', // [191]
|
||||
'`', // [192]
|
||||
'', // [193]
|
||||
'', // [194]
|
||||
'', // [195]
|
||||
'', // [196]
|
||||
'', // [197]
|
||||
'', // [198]
|
||||
'', // [199]
|
||||
'', // [200]
|
||||
'', // [201]
|
||||
'', // [202]
|
||||
'', // [203]
|
||||
'', // [204]
|
||||
'', // [205]
|
||||
'', // [206]
|
||||
'', // [207]
|
||||
'', // [208]
|
||||
'', // [209]
|
||||
'', // [210]
|
||||
'', // [211]
|
||||
'', // [212]
|
||||
'', // [213]
|
||||
'', // [214]
|
||||
'', // [215]
|
||||
'', // [216]
|
||||
'', // [217]
|
||||
'', // [218]
|
||||
'[', // [219]
|
||||
'\\', // [220]
|
||||
']', // [221]
|
||||
'\'', // [222]
|
||||
'', // [223]
|
||||
'', // [224]
|
||||
'AltGr', // [225]
|
||||
'', // [226]
|
||||
'', // [227]
|
||||
'', // [228]
|
||||
'', // [229]
|
||||
'', // [230]
|
||||
'', // [231]
|
||||
'', // [232]
|
||||
'', // [233]
|
||||
'', // [234]
|
||||
'', // [235]
|
||||
'', // [236]
|
||||
'', // [237]
|
||||
'', // [238]
|
||||
'', // [239]
|
||||
'', // [240]
|
||||
'', // [241]
|
||||
'', // [242]
|
||||
'', // [243]
|
||||
'', // [244]
|
||||
'', // [245]
|
||||
'', // [246]
|
||||
'', // [247]
|
||||
'', // [248]
|
||||
'', // [249]
|
||||
'', // [250]
|
||||
'', // [251]
|
||||
'', // [252]
|
||||
'', // [253]
|
||||
'', // [254]
|
||||
'', // [255]
|
||||
];
|
||||
|
||||
export default keycodeToElectronMap;
|
||||
9
packages/lib/services/KeymapService_keysRegExp.ts
Normal file
9
packages/lib/services/KeymapService_keysRegExp.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// We move this regex outside KeymapService because it makes gettext parsing
|
||||
// fail. In fact it doesn't fail at the regex itself but at the next backtick
|
||||
// into the code. Probably their parser see a backtick in the regex and opens a
|
||||
// JS template string, while it shouldn't.
|
||||
// https://discourse.joplinapp.org/t/translations/12832?u=laurent
|
||||
|
||||
const keysRegExp = /^([0-9A-Z)!@#$%^&*(:+<_>?~{|}";=,\-./`[\\\]']|F1*[1-9]|F10|F2[0-4]|Plus|Space|Tab|Backspace|Delete|Insert|Return|Enter|Up|Down|Left|Right|Home|End|PageUp|PageDown|Escape|Esc|VolumeUp|VolumeDown|VolumeMute|MediaNextTrack|MediaPreviousTrack|MediaStop|MediaPlayPause|PrintScreen|Numlock|Scrolllock|Capslock|num([0-9]|dec|add|sub|mult|div))$/;
|
||||
|
||||
export default keysRegExp;
|
||||
@@ -23,7 +23,7 @@ export default class KvStore extends BaseService {
|
||||
this.instance_ = null;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.incMutex_ = new Mutex();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,30 @@ export default class ToolbarButtonUtils {
|
||||
return this.service_;
|
||||
}
|
||||
|
||||
// Editor commands will focus the editor after they're executed
|
||||
private isEditorCommand(commandName: string) {
|
||||
return (commandName.indexOf('editor.') === 0 ||
|
||||
// These commands are grandfathered in, but in the future
|
||||
// all editor commands should start with "editor."
|
||||
// WARNING: Some commands such as textLink are not defined here
|
||||
// because they are more complex and handle focus manually
|
||||
commandName === 'textCopy' ||
|
||||
commandName === 'textCut' ||
|
||||
commandName === 'textPaste' ||
|
||||
commandName === 'textSelectAll' ||
|
||||
commandName === 'textBold' ||
|
||||
commandName === 'textItalic' ||
|
||||
commandName === 'textCode' ||
|
||||
commandName === 'attachFile' ||
|
||||
commandName === 'textNumberedList' ||
|
||||
commandName === 'textBulletedList' ||
|
||||
commandName === 'textCheckbox' ||
|
||||
commandName === 'textHeading' ||
|
||||
commandName === 'textHorizontalRule' ||
|
||||
commandName === 'insertDateTime'
|
||||
);
|
||||
}
|
||||
|
||||
private commandToToolbarButton(commandName: string, whenClauseContext: any): ToolbarButtonInfo {
|
||||
const newEnabled = this.service.isEnabled(commandName, whenClauseContext);
|
||||
const newTitle = this.service.title(commandName);
|
||||
@@ -52,8 +76,11 @@ export default class ToolbarButtonUtils {
|
||||
tooltip: this.service.label(commandName),
|
||||
iconName: command.declaration.iconName,
|
||||
enabled: newEnabled,
|
||||
onClick: () => {
|
||||
onClick: async () => {
|
||||
void this.service.execute(commandName);
|
||||
if (this.isEditorCommand(commandName)) {
|
||||
void this.service.execute('editor.focus');
|
||||
}
|
||||
},
|
||||
title: newTitle,
|
||||
};
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { _ } from '../../locale';
|
||||
const InteropService_Exporter_Base = require('./InteropService_Exporter_Base').default;
|
||||
const InteropService_Exporter_Raw = require('./InteropService_Exporter_Raw').default;
|
||||
import InteropService_Exporter_Base from './InteropService_Exporter_Base';
|
||||
import InteropService_Exporter_Raw from './InteropService_Exporter_Raw';
|
||||
import shim from '../../shim';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const shim = require('../../shim').default;
|
||||
|
||||
export default class InteropService_Exporter_Jex extends InteropService_Exporter_Base {
|
||||
async init(destPath: string) {
|
||||
|
||||
private tempDir_: string;
|
||||
private destPath_: string;
|
||||
private rawExporter_: InteropService_Exporter_Raw;
|
||||
|
||||
public async init(destPath: string) {
|
||||
if (await shim.fsDriver().isDirectory(destPath)) throw new Error(`Path is a directory: ${destPath}`);
|
||||
|
||||
this.tempDir_ = await this.temporaryDirectory_(false);
|
||||
@@ -14,15 +20,15 @@ export default class InteropService_Exporter_Jex extends InteropService_Exporter
|
||||
await this.rawExporter_.init(this.tempDir_);
|
||||
}
|
||||
|
||||
async processItem(itemType: number, item: any) {
|
||||
public async processItem(itemType: number, item: any) {
|
||||
return this.rawExporter_.processItem(itemType, item);
|
||||
}
|
||||
|
||||
async processResource(resource: any, filePath: string) {
|
||||
public async processResource(resource: any, filePath: string) {
|
||||
return this.rawExporter_.processResource(resource, filePath);
|
||||
}
|
||||
|
||||
async close() {
|
||||
public async close() {
|
||||
const stats = await shim.fsDriver().readDirStats(this.tempDir_, { recursive: true });
|
||||
const filePaths = stats.filter((a: any) => !a.isDirectory()).map((a: any) => a.path);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class JoplinViewsMenuItems {
|
||||
private store: any;
|
||||
private plugin: Plugin;
|
||||
|
||||
constructor(plugin: Plugin, store: any) {
|
||||
public constructor(plugin: Plugin, store: any) {
|
||||
this.store = store;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export default class JoplinViewsMenuItems {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
async create(id: string, commandName: string, location: MenuItemLocation = MenuItemLocation.Tools, options: CreateMenuItemOptions = null) {
|
||||
public async create(id: string, commandName: string, location: MenuItemLocation = MenuItemLocation.Tools, options: CreateMenuItemOptions = null) {
|
||||
if (typeof location !== 'string') {
|
||||
this.plugin.deprecationNotice('1.5', 'Creating a view without an ID is deprecated. To fix it, change your call to `joplin.views.menuItem.create("my-unique-id", ...)`');
|
||||
options = location as any;
|
||||
@@ -35,8 +35,17 @@ export default class JoplinViewsMenuItems {
|
||||
const controller = new MenuItemController(handle, this.plugin.id, this.store, commandName, location);
|
||||
this.plugin.addViewController(controller);
|
||||
|
||||
// Register the command with the keymap service - not that if no
|
||||
// accelerator is provided, we still register the command, so that
|
||||
// it appears in the keymap editor, which will allow the user to
|
||||
// set a custom shortcut.
|
||||
//
|
||||
// https://discourse.joplinapp.org/t/plugin-note-tabs/12752/39
|
||||
|
||||
if (options && options.accelerator) {
|
||||
KeymapService.instance().registerCommandAccelerator(commandName, options.accelerator);
|
||||
} else {
|
||||
KeymapService.instance().registerCommandAccelerator(commandName, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,14 @@ interface ItemChangeEvent {
|
||||
event: ItemChangeEventType;
|
||||
}
|
||||
|
||||
interface SyncStartEvent {
|
||||
// Tells whether there were errors during sync or not. The log will
|
||||
// have the complete information about any error.
|
||||
withErrors: boolean;
|
||||
}
|
||||
|
||||
type ItemChangeHandler = (event: ItemChangeEvent)=> void;
|
||||
type SyncStartHandler = (event: SyncStartEvent)=> void;
|
||||
|
||||
/**
|
||||
* The workspace service provides access to all the parts of Joplin that
|
||||
@@ -80,8 +87,15 @@ export default class JoplinWorkspace {
|
||||
/**
|
||||
* Called when an alarm associated with a to-do is triggered.
|
||||
*/
|
||||
public async onNoteAlarmTrigger(callback: Function): Promise<Disposable> {
|
||||
return makeListener(eventManager, 'noteAlarmTrigger', callback);
|
||||
public async onNoteAlarmTrigger(handler: Function): Promise<Disposable> {
|
||||
return makeListener(eventManager, 'noteAlarmTrigger', handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the synchronisation process is starting.
|
||||
*/
|
||||
public async onSyncStart(handler: SyncStartHandler): Promise<Disposable> {
|
||||
return makeListener(eventManager, 'syncStart', handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,7 @@ const Setting = require('../../models/Setting').default;
|
||||
const Note = require('../../models/Note.js');
|
||||
const BaseModel = require('../../BaseModel').default;
|
||||
const ItemChangeUtils = require('../ItemChangeUtils');
|
||||
const { pregQuote, scriptType } = require('../../string-utils.js');
|
||||
const removeDiacritics = require('diacritics').remove;
|
||||
const { pregQuote, scriptType, removeDiacritics } = require('../../string-utils.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const filterParser = require('./filterParser').default;
|
||||
const queryBuilder = require('./queryBuilder').default;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user