You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-27 20:29:45 +02:00
Compare commits
49 Commits
android-v1
...
android-v1
Author | SHA1 | Date | |
---|---|---|---|
|
0cc544a95f | ||
|
03b7402dc5 | ||
|
dd5c400c24 | ||
|
abc702c21f | ||
|
cc3e1bdee8 | ||
|
10cfa773ca | ||
|
8dc42b1f41 | ||
|
3847831d80 | ||
|
4d20589773 | ||
|
08c2a7ad64 | ||
|
d19796f14c | ||
|
e4f53a48d2 | ||
|
154163bd6c | ||
|
4502414934 | ||
|
86c471afcd | ||
|
8cfe4b0f82 | ||
|
fd90a490c0 | ||
|
4a184721e4 | ||
|
1e3bd937ed | ||
|
0f6932f1e8 | ||
|
cab98776db | ||
|
fbc1e3ed3b | ||
|
805d16abda | ||
|
f133229287 | ||
|
8f4031572a | ||
|
d25fa796c0 | ||
|
089d6a5c9e | ||
|
3f83355d9f | ||
|
40380e3066 | ||
|
a6748bafb3 | ||
|
b52f6eb77c | ||
|
98c933fdb7 | ||
|
ece7ffadd6 | ||
|
591bceb8ef | ||
|
04cfd07176 | ||
|
db2282a351 | ||
|
0ec3d6ca9d | ||
|
442b7ce0d3 | ||
|
6a068a90b2 | ||
|
9a6f6c8b39 | ||
|
52d5c32950 | ||
|
de47cff86d | ||
|
a459174f98 | ||
|
75d5aa3a77 | ||
|
c254ca524f | ||
|
51934b8d8d | ||
|
a5dd686bb2 | ||
|
4cbfd04522 | ||
|
6d5d9323bd |
@@ -105,7 +105,11 @@ ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newSubFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/openFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/openNote.js
|
||||
ElectronClient/gui/MainScreen/commands/openTag.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/renameTag.js
|
||||
@@ -122,6 +126,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/menuCommandNames.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -99,7 +99,11 @@ ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newSubFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/openFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/openNote.js
|
||||
ElectronClient/gui/MainScreen/commands/openTag.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/renameTag.js
|
||||
@@ -116,6 +120,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/menuCommandNames.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
|
5
.ignore
5
.ignore
@@ -48,7 +48,11 @@ ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newSubFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/openFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/openNote.js
|
||||
ElectronClient/gui/MainScreen/commands/openTag.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/renameTag.js
|
||||
@@ -65,6 +69,7 @@ ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/menuCommandNames.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
|
3
CliClient/.gitignore
vendored
3
CliClient/.gitignore
vendored
@@ -22,4 +22,5 @@ yarn-error.log
|
||||
tests/support/dropbox-auth.txt
|
||||
tests/support/nextcloud-auth.json
|
||||
tests/support/onedrive-auth.txt
|
||||
build/
|
||||
build/
|
||||
patches/
|
@@ -45,10 +45,12 @@ class Command extends BaseCommand {
|
||||
|
||||
const startDecryption = async () => {
|
||||
this.stdout(_('Starting decryption... Please wait as it may take several minutes depending on how much there is to decrypt.'));
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const result = await DecryptionWorker.instance().start();
|
||||
|
||||
if (result.error) throw result.error;
|
||||
|
||||
const line = [];
|
||||
line.push(_('Decrypted items: %d', result.decryptedItemCount));
|
||||
if (result.skippedItemCount) line.push(_('Skipped items: %d (use --retry-failed-items to retry decrypting them)', result.skippedItemCount));
|
||||
|
@@ -17,12 +17,23 @@ tasks.prepareBuild = {
|
||||
excluded: ['node_modules'],
|
||||
});
|
||||
await utils.copyDir(`${__dirname}/locales-build`, `${buildDir}/locales`);
|
||||
await utils.copyDir(`${__dirname}/../patches`, `${buildDir}/patches`);
|
||||
await tasks.copyLib.fn();
|
||||
await utils.copyFile(`${__dirname}/package.json`, `${buildDir}/package.json`);
|
||||
await utils.copyFile(`${__dirname}/package-lock.json`, `${buildDir}/package-lock.json`);
|
||||
await utils.copyFile(`${__dirname}/gulpfile.js`, `${buildDir}/gulpfile.js`);
|
||||
|
||||
// Import all the patches inside the CliClient directory
|
||||
// and build file. Needs to be in CliClient dir for when running
|
||||
// in dev mode, and in build dir for production.
|
||||
const localPatchDir = `${buildDir}/patches`;
|
||||
await fs.remove(localPatchDir);
|
||||
await fs.mkdirp(localPatchDir);
|
||||
await utils.copyDir(`${__dirname}/../patches/shared`, `${localPatchDir}`, { delete: false });
|
||||
await utils.copyDir(`${__dirname}/../patches/node`, `${localPatchDir}`, { delete: false });
|
||||
|
||||
await fs.remove(`${__dirname}/patches`);
|
||||
await utils.copyDir(`${localPatchDir}`, `${__dirname}/patches`);
|
||||
|
||||
const packageRaw = await fs.readFile(`${buildDir}/package.json`);
|
||||
const package = JSON.parse(packageRaw.toString());
|
||||
package.scripts.postinstall = 'patch-package';
|
||||
|
@@ -9,12 +9,12 @@
|
||||
# Germán Martín <gmag11@gmail.com>, 2019.
|
||||
# Fernando Pindado <fpindado@gmail.com>, 2020.
|
||||
# Andros Fenollosa <andros@fenollosa.email>, 2020.
|
||||
#
|
||||
# Mario Campo <mario.campo@gmail.com>, 2020
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Joplin-CLI 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: Andros Fenollosa <andros@fenollosa.email>\n"
|
||||
"Last-Translator: Mario Campo <mario.campo@gmail.com>\n"
|
||||
"Language-Team: Spanish <lucas.vieites@gmail.com>\n"
|
||||
"Language: es_ES\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -279,13 +279,13 @@ msgstr "Las notas han sido importadas: %s"
|
||||
#: ElectronClient/gui/ConfigScreen.min.js:84
|
||||
#: ElectronClient/gui/ConfigScreen/ConfigScreen.js:99
|
||||
msgid "This will open a new screen. Save your current changes?"
|
||||
msgstr "Esto abrirá una nueva pantalla. ¿Quieres salvar los cambios actuales?"
|
||||
msgstr "Esto abrirá una nueva pantalla. ¿Quieres guardar los cambios actuales?"
|
||||
|
||||
#: ElectronClient/gui/ConfigScreen.min.js:172
|
||||
#: ElectronClient/gui/ConfigScreen/ConfigScreen.js:162
|
||||
#: ReactNativeClient/lib/components/screens/config.js:307
|
||||
msgid "Check synchronisation configuration"
|
||||
msgstr "Comprobar sincronización"
|
||||
msgstr "Comprobar sincronización de la configuración"
|
||||
|
||||
#: ElectronClient/gui/ConfigScreen.min.js:181
|
||||
#: ElectronClient/gui/ConfigScreen/ConfigScreen.js:167
|
||||
@@ -1050,7 +1050,7 @@ msgstr "Descifrando elementos: %d/%d"
|
||||
#: ReactNativeClient/lib/components/side-menu-content.js:330
|
||||
#, javascript-format
|
||||
msgid "Fetching resources: %d/%d"
|
||||
msgstr "Obteniendo refuersos: %d/%d"
|
||||
msgstr "Obteniendo recursos: %d/%d"
|
||||
|
||||
#: ElectronClient/gui/SideBar/commands/focusElementSideBar.js:16
|
||||
msgid "Sidebar"
|
||||
|
@@ -15,6 +15,8 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
|
||||
#: ElectronClient/services/plugins/UserWebviewDialogButtonBar.js:20
|
||||
#: ElectronClient/checkForUpdates.js:139
|
||||
@@ -121,14 +123,14 @@ msgid "Could not export notes: %s"
|
||||
msgstr "노트를 내보낼 수 없습니다: %s"
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.js:431
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Type a note title or part of its content to jump to it. Or type # followed "
|
||||
"by a tag name, or @ followed by a notebook name. Or type : to search for "
|
||||
"commands."
|
||||
msgstr ""
|
||||
"노트 제목을 적거나 혹은 건너뛰세요. 혹은 #으로 시작하는 태그 이름이나, @로 시"
|
||||
"작하는 노트북 이름을 적으세요."
|
||||
"노트 제목이나 내용의 일부를 입력하여 바로 이동하세요. 혹은 #으로 시작하는 태"
|
||||
"그 이름이나 @로 시작하는 노트북 이름을 입력하거나, :을 입력하여 명령을 검색하"
|
||||
"세요.\""
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.js:456
|
||||
#: ElectronClient/plugins/GotoAnything.min.js:499
|
||||
@@ -138,9 +140,8 @@ msgstr "가고자 하는 곳으로 이동..."
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.js:463
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:28
|
||||
#, fuzzy
|
||||
msgid "Command palette"
|
||||
msgstr "명령"
|
||||
msgstr "명령 팔레트"
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.min.js:459
|
||||
msgid ""
|
||||
@@ -2709,11 +2710,14 @@ msgid ""
|
||||
"\n"
|
||||
"You may turn off this option at any time in the Configuration screen."
|
||||
msgstr ""
|
||||
"노트에 지리적 위치 정보를 포함하기 위해서는 위치 정보에 접근이 필요하고, 이"
|
||||
"를 위해선 권한이 요구됩니다.\n"
|
||||
"\n"
|
||||
"설정 화면에서 언제든지 이 설정을 끌 수 있습니다."
|
||||
|
||||
#: ReactNativeClient/lib/components/screens/Note.js:341
|
||||
#, fuzzy
|
||||
msgid "Permission needed"
|
||||
msgstr "카메라 사용 허가"
|
||||
msgstr "권한 요구"
|
||||
|
||||
#: ReactNativeClient/lib/components/screens/Note.js:501
|
||||
#: ReactNativeClient/lib/shim-init-node.js:91
|
||||
@@ -2971,9 +2975,8 @@ msgstr ""
|
||||
"다:"
|
||||
|
||||
#: ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js:28
|
||||
#, fuzzy
|
||||
msgid "Open"
|
||||
msgstr "열기..."
|
||||
msgstr "열기"
|
||||
|
||||
#: ReactNativeClient/lib/components/note-list.js:97
|
||||
msgid "You currently have no notebooks."
|
||||
@@ -3182,9 +3185,8 @@ msgid "AWS S3 bucket"
|
||||
msgstr "AWS S3 버킷"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:226
|
||||
#, fuzzy
|
||||
msgid "AWS S3 URL"
|
||||
msgstr "AWS S3"
|
||||
msgstr "AWS S3 URL"
|
||||
|
||||
#: ReactNativeClient/lib/models/Setting.js:237
|
||||
msgid "AWS key"
|
||||
|
@@ -141,7 +141,7 @@ msgstr "Ir para qualquer coisa..."
|
||||
#: ElectronClient/plugins/GotoAnything.js:463
|
||||
#: ElectronClient/gui/KeymapConfig/utils/getLabel.js:28
|
||||
msgid "Command palette"
|
||||
msgstr ""
|
||||
msgstr "Paleta de Comandos"
|
||||
|
||||
#: ElectronClient/plugins/GotoAnything.min.js:459
|
||||
msgid ""
|
||||
@@ -549,6 +549,8 @@ msgid ""
|
||||
"The attachments will no longer be watched when you switch to a different "
|
||||
"note."
|
||||
msgstr ""
|
||||
"Os anexos não serão mais observados quando você mudar para uma nota"
|
||||
"diferente."
|
||||
|
||||
#: ElectronClient/gui/NoteEditor/NoteEditor.js:387
|
||||
#, javascript-format
|
||||
@@ -665,6 +667,9 @@ msgid ""
|
||||
"may take a few minutes to complete and the app needs to be restarted. To "
|
||||
"proceed please click on the link."
|
||||
msgstr ""
|
||||
"O alvo da sincroniação precisa ser atualizado antes que o Joplin possa sincronizar. A operação"
|
||||
"pode levar alguns minutos para completar e o app terá que ser reiniciado. Para"
|
||||
"proceder, por favor, clique neste link."
|
||||
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.js:416
|
||||
#: ElectronClient/gui/MainScreen/MainScreen.min.js:306
|
||||
@@ -1411,13 +1416,15 @@ msgstr "Anexos da nota"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49
|
||||
msgid "Press the shortcut"
|
||||
msgstr ""
|
||||
msgstr "Digite o atalho"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/ShortcutRecorder.js:49
|
||||
msgid ""
|
||||
"Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the "
|
||||
"shortcut."
|
||||
msgstr ""
|
||||
"Digite o atalho e então aperte ENTER. Ou, pressione BARRA DE ESPAÇO para limpar o"
|
||||
"atalho"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:63
|
||||
#: ReactNativeClient/lib/services/KeymapService.js:144
|
||||
@@ -1427,7 +1434,7 @@ msgstr "Erro"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:126
|
||||
msgid "Command"
|
||||
msgstr ""
|
||||
msgstr "Command"
|
||||
|
||||
#: ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js:127
|
||||
#, fuzzy
|
||||
@@ -1487,6 +1494,7 @@ msgstr "Estatísticas"
|
||||
msgid ""
|
||||
"The app is now going to close. Please relaunch it to complete the process."
|
||||
msgstr ""
|
||||
"O app irá fechar agora. Por favor, reabra-o para completar o processo."
|
||||
|
||||
#: ElectronClient/app.js:340
|
||||
#, javascript-format
|
||||
@@ -1514,7 +1522,7 @@ msgstr "Encerrar edição externa"
|
||||
|
||||
#: ElectronClient/commands/toggleExternalEditing.js:37
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
msgstr "Parar"
|
||||
|
||||
#: ElectronClient/commands/stopExternalEditing.js:18
|
||||
msgid "Stop external editing"
|
||||
@@ -1741,7 +1749,7 @@ msgstr ""
|
||||
|
||||
#: CliClient/app/command-sync.js:35
|
||||
msgid "Upgrade the sync target to the latest version."
|
||||
msgstr ""
|
||||
msgstr "Atualizar o alvo de sincronização para a última versão."
|
||||
|
||||
#: CliClient/app/command-sync.js:105
|
||||
#, javascript-format
|
||||
|
44
CliClient/package-lock.json
generated
44
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -23,9 +23,9 @@
|
||||
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
|
||||
"integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg=="
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
@@ -33,9 +33,9 @@
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
|
||||
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA=="
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "4.3.4",
|
||||
@@ -47,9 +47,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2964,9 +2964,9 @@
|
||||
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz",
|
||||
"integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg=="
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
|
||||
"integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g=="
|
||||
},
|
||||
"homedir-polyfill": {
|
||||
"version": "1.0.3",
|
||||
@@ -3227,6 +3227,11 @@
|
||||
"file-type": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"immer": {
|
||||
"version": "7.0.14",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-7.0.14.tgz",
|
||||
"integrity": "sha512-BxCs6pJwhgSEUEOZjywW7OA8DXVzfHjkBelSEl0A+nEu0+zS4cFVdNOONvt55N4WOm8Pu4xqSPYxhm1Lv2iBBA=="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@@ -3650,9 +3655,9 @@
|
||||
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
|
||||
},
|
||||
"joplin-turndown": {
|
||||
"version": "4.0.29",
|
||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.29.tgz",
|
||||
"integrity": "sha512-rVGu8u4TpSRETo59/jiVW9iaXnpdxxpBHjb7nyCflkDfWhg1Kska4uagBQGw7cD2yxw7mB2YUIB/fAgtlIzcDQ==",
|
||||
"version": "4.0.30",
|
||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.30.tgz",
|
||||
"integrity": "sha512-OrGdNTsjI6/cbx/es9Hl0YI3YTql4SopduFcYCnWTZgqT0SJqILnF2JQxSNnbPnkSDIIRdNOG4+iNzlY6bS1nw==",
|
||||
"requires": {
|
||||
"css": "^2.2.4",
|
||||
"html-entities": "^1.2.1",
|
||||
@@ -5568,13 +5573,6 @@
|
||||
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"request-promise-native": {
|
||||
|
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"test": "gulp buildTests -L && node node_modules/jasmine/bin/jasmine.js --fail-fast=true --config=tests/support/jasmine.json",
|
||||
"test-ci": "gulp buildTests -L && node node_modules/jasmine/bin/jasmine.js --config=tests/support/jasmine.json",
|
||||
"postinstall": "npm run build && patch-package --patch-dir ../patches/shared && patch-package --patch-dir ../patches/node",
|
||||
"postinstall": "npm run build && patch-package --patch-dir ./patches",
|
||||
"build": "gulp build",
|
||||
"start": "gulp build -L && node 'build/main.js' --stack-trace-enabled --log-level debug --env dev"
|
||||
},
|
||||
@@ -28,7 +28,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.3",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
@@ -52,13 +52,14 @@
|
||||
"font-awesome-filetypes": "^2.1.0",
|
||||
"form-data": "^2.1.4",
|
||||
"fs-extra": "^5.0.0",
|
||||
"highlight.js": "10.1.1",
|
||||
"highlight.js": "^10.2.1",
|
||||
"html-entities": "^1.2.1",
|
||||
"html-minifier": "^3.5.15",
|
||||
"htmlparser2": "^4.1.0",
|
||||
"image-data-uri": "^2.0.0",
|
||||
"image-type": "^3.0.0",
|
||||
"joplin-turndown": "^4.0.29",
|
||||
"immer": "^7.0.14",
|
||||
"joplin-turndown": "^4.0.30",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"jssha": "^2.3.0",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<en-note>
|
||||
<div><a href="joplin://21ca2b948f222a38802940ec7e2e5de3" hash="21ca2b948f222a38802940ec7e2e5de3" type="application/pdf" style="cursor:pointer;" alt="attachment-1">attachment-1</a></div>
|
||||
<div><a href=":/21ca2b948f222a38802940ec7e2e5de3" hash="21ca2b948f222a38802940ec7e2e5de3" type="application/pdf" style="cursor:pointer;" alt="attachment-1">attachment-1</a></div>
|
||||
<div>
|
||||
<br>
|
||||
</div>
|
||||
|
1
CliClient/tests/html_to_md/anchor_with_brackets.html
Normal file
1
CliClient/tests/html_to_md/anchor_with_brackets.html
Normal file
@@ -0,0 +1 @@
|
||||
<a href="https://images.macrumors.com/filters:quality(90)/article.html"/>testing</a>
|
1
CliClient/tests/html_to_md/anchor_with_brackets.md
Normal file
1
CliClient/tests/html_to_md/anchor_with_brackets.md
Normal file
@@ -0,0 +1 @@
|
||||
[testing](https://images.macrumors.com/filters:quality%2890%29/article.html)
|
1
CliClient/tests/html_to_md/image_with_brackets.html
Normal file
1
CliClient/tests/html_to_md/image_with_brackets.html
Normal file
@@ -0,0 +1 @@
|
||||
<img src="https://images.macrumors.com/filters:quality(90)/article/test.jpg?high"/>
|
1
CliClient/tests/html_to_md/image_with_brackets.md
Normal file
1
CliClient/tests/html_to_md/image_with_brackets.md
Normal file
@@ -0,0 +1 @@
|
||||

|
1
CliClient/tests/html_to_md/image_with_title1.html
Normal file
1
CliClient/tests/html_to_md/image_with_title1.html
Normal file
@@ -0,0 +1 @@
|
||||
<img src="https://example.com/test.jpg" title="hello"/>
|
1
CliClient/tests/html_to_md/image_with_title1.md
Normal file
1
CliClient/tests/html_to_md/image_with_title1.md
Normal file
@@ -0,0 +1 @@
|
||||

|
1
CliClient/tests/html_to_md/image_with_title2.html
Normal file
1
CliClient/tests/html_to_md/image_with_title2.html
Normal file
@@ -0,0 +1 @@
|
||||
<img src="https://example.com/test.jpg" title='hello & "(ouch)"'/>
|
1
CliClient/tests/html_to_md/image_with_title2.md
Normal file
1
CliClient/tests/html_to_md/image_with_title2.md
Normal file
@@ -0,0 +1 @@
|
||||
"")
|
@@ -104,4 +104,14 @@ describe('models_BaseItem', function() {
|
||||
|
||||
expect(noteAfter).toEqual(noteBefore);
|
||||
}));
|
||||
|
||||
it('should serialize and unserialize properties that contain new lines', asyncTest(async () => {
|
||||
const note = await Note.save({ title: 'note', source_url: '\nhttps://joplinapp.org/\n' });
|
||||
|
||||
const noteBefore = await Note.load(note.id);
|
||||
const serialized = await Note.serialize(noteBefore);
|
||||
const noteAfter = await Note.unserialize(serialized);
|
||||
|
||||
expect(noteAfter).toEqual(noteBefore);
|
||||
}));
|
||||
});
|
||||
|
@@ -3,6 +3,7 @@ require('app-module-path').addPath(__dirname);
|
||||
const { tempFilePath } = require('test-utils.js');
|
||||
const KeymapService = require('lib/services/KeymapService').default;
|
||||
const keymapService = KeymapService.instance();
|
||||
keymapService.initialize([]);
|
||||
|
||||
describe('services_KeymapService', () => {
|
||||
describe('validateAccelerator', () => {
|
||||
@@ -31,7 +32,7 @@ describe('services_KeymapService', () => {
|
||||
};
|
||||
|
||||
Object.entries(testCases).forEach(([platform, accelerators]) => {
|
||||
keymapService.initialize(platform);
|
||||
keymapService.initialize([], platform);
|
||||
accelerators.forEach(accelerator => {
|
||||
expect(() => keymapService.validateAccelerator(accelerator)).not.toThrow();
|
||||
});
|
||||
@@ -69,7 +70,7 @@ describe('services_KeymapService', () => {
|
||||
};
|
||||
|
||||
Object.entries(testCases).forEach(([platform, accelerators]) => {
|
||||
keymapService.initialize(platform);
|
||||
keymapService.initialize([], platform);
|
||||
accelerators.forEach(accelerator => {
|
||||
expect(() => keymapService.validateAccelerator(accelerator)).toThrow();
|
||||
});
|
||||
@@ -81,12 +82,12 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should allow registering new commands', async () => {
|
||||
keymapService.initialize('linux');
|
||||
keymapService.initialize([], 'linux');
|
||||
keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B');
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B');
|
||||
|
||||
// Check that macOS key conversion is working
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B');
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B');
|
||||
keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X');
|
||||
@@ -95,7 +96,7 @@ describe('services_KeymapService', () => {
|
||||
const keymapFilePath = tempFilePath('json');
|
||||
await keymapService.saveCustomKeymap(keymapFilePath);
|
||||
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
await keymapService.loadCustomKeymap(keymapFilePath);
|
||||
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X');
|
||||
@@ -106,17 +107,17 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should return the platform-specific default Accelerator', () => {
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
expect(keymapService.getAccelerator('newNote')).toEqual('Cmd+N');
|
||||
expect(keymapService.getAccelerator('synchronize')).toEqual('Cmd+S');
|
||||
expect(keymapService.getAccelerator('textSelectAll')).toEqual('Cmd+A');
|
||||
expect(keymapService.getAccelerator('textBold')).toEqual('Cmd+B');
|
||||
|
||||
keymapService.initialize('linux');
|
||||
keymapService.initialize([], 'linux');
|
||||
expect(keymapService.getAccelerator('newNote')).toEqual('Ctrl+N');
|
||||
expect(keymapService.getAccelerator('synchronize')).toEqual('Ctrl+S');
|
||||
|
||||
keymapService.initialize('win32');
|
||||
keymapService.initialize([], 'win32');
|
||||
expect(keymapService.getAccelerator('textSelectAll')).toEqual('Ctrl+A');
|
||||
expect(keymapService.getAccelerator('textBold')).toEqual('Ctrl+B');
|
||||
});
|
||||
@@ -130,7 +131,7 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should update the Accelerator', () => {
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize(['print'], 'darwin');
|
||||
const testCases_Darwin = [
|
||||
{ command: 'newNote', accelerator: 'Ctrl+Option+Shift+N' },
|
||||
{ command: 'synchronize', accelerator: 'F11' },
|
||||
@@ -147,7 +148,7 @@ describe('services_KeymapService', () => {
|
||||
expect(keymapService.getAccelerator(command)).toEqual(accelerator);
|
||||
});
|
||||
|
||||
keymapService.initialize('linux');
|
||||
keymapService.initialize(['print'], 'linux');
|
||||
const testCases_Linux = [
|
||||
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
|
||||
{ command: 'synchronize', accelerator: 'F15' },
|
||||
@@ -167,7 +168,7 @@ describe('services_KeymapService', () => {
|
||||
});
|
||||
|
||||
describe('getDefaultAccelerator', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
beforeEach(() => keymapService.initialize(['print', 'linux']));
|
||||
|
||||
it('should return the default accelerator', () => {
|
||||
const testCases = [
|
||||
@@ -196,7 +197,7 @@ describe('services_KeymapService', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should update the keymap', () => {
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.initialize([], 'darwin');
|
||||
const customKeymapItems_Darwin = [
|
||||
{ command: 'newNote', accelerator: 'Option+Shift+Cmd+N' },
|
||||
{ command: 'synchronize', accelerator: 'Ctrl+F11' },
|
||||
@@ -217,7 +218,7 @@ describe('services_KeymapService', () => {
|
||||
expect(keymapService.getAccelerator(command)).toEqual(accelerator);
|
||||
});
|
||||
|
||||
keymapService.initialize('win32');
|
||||
keymapService.initialize([], 'win32');
|
||||
const customKeymapItems_Win32 = [
|
||||
{ command: 'newNote', accelerator: 'Ctrl+Alt+Shift+N' },
|
||||
{ command: 'synchronize', accelerator: 'Ctrl+F11' },
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'",
|
||||
|
@@ -55,7 +55,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
"build": "SKIP_PREFLIGHT_CHECK=true node scripts/build.js",
|
||||
"build": "node scripts/build.js SKIP_PREFLIGHT_CHECK",
|
||||
"test": "node scripts/test.js --env=jsdom",
|
||||
"watch": "cra-build-watch",
|
||||
"postinstall": "node postinstall.js && npm run build"
|
||||
|
@@ -1,5 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
if (process.argv && process.argv.indexOf('SKIP_PREFLIGHT_CHECK') >= 0) {
|
||||
process.env.SKIP_PREFLIGHT_CHECK = 'true';
|
||||
}
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
@@ -148,10 +148,10 @@ class Bridge {
|
||||
this.dispatch({ type: 'CLIPPER_SERVER_SET', foundState: 'found', port: state.port });
|
||||
|
||||
const folders = await this.folderTree();
|
||||
this.dispatch({ type: 'FOLDERS_SET', folders: folders });
|
||||
this.dispatch({ type: 'FOLDERS_SET', folders: folders.items ? folders.items : folders });
|
||||
|
||||
const tags = await this.clipperApiExec('GET', 'tags');
|
||||
this.dispatch({ type: 'TAGS_SET', tags: tags });
|
||||
this.dispatch({ type: 'TAGS_SET', tags: tags.items ? tags.items : tags });
|
||||
|
||||
bridge().restoreState();
|
||||
return;
|
||||
@@ -245,7 +245,7 @@ class Bridge {
|
||||
}
|
||||
|
||||
async folderTree() {
|
||||
return this.clipperApiExec('GET', 'folders');
|
||||
return this.clipperApiExec('GET', 'folders', { as_tree: 1 });
|
||||
}
|
||||
|
||||
async storageSet(keys) {
|
||||
|
@@ -14,6 +14,7 @@ import Setting from 'lib/models/Setting';
|
||||
import actionApi from 'lib/services/rest/actionApi.desktop';
|
||||
import BaseApplication from 'lib/BaseApplication';
|
||||
import { _, setLocale } from 'lib/locale';
|
||||
import menuCommandNames from './gui/menuCommandNames';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
@@ -46,6 +47,7 @@ const commands = [
|
||||
require('./gui/MainScreen/commands/moveToFolder'),
|
||||
require('./gui/MainScreen/commands/newNote'),
|
||||
require('./gui/MainScreen/commands/newFolder'),
|
||||
require('./gui/MainScreen/commands/newSubFolder'),
|
||||
require('./gui/MainScreen/commands/newTodo'),
|
||||
require('./gui/MainScreen/commands/print'),
|
||||
require('./gui/MainScreen/commands/renameFolder'),
|
||||
@@ -61,6 +63,9 @@ const commands = [
|
||||
require('./gui/MainScreen/commands/toggleSideBar'),
|
||||
require('./gui/MainScreen/commands/toggleVisiblePanes'),
|
||||
require('./gui/MainScreen/commands/toggleEditors'),
|
||||
require('./gui/MainScreen/commands/openNote'),
|
||||
require('./gui/MainScreen/commands/openFolder'),
|
||||
require('./gui/MainScreen/commands/openTag'),
|
||||
require('./gui/NoteEditor/commands/focusElementNoteBody'),
|
||||
require('./gui/NoteEditor/commands/focusElementNoteTitle'),
|
||||
require('./gui/NoteEditor/commands/showLocalSearch'),
|
||||
@@ -491,14 +496,6 @@ class Application extends BaseApplication {
|
||||
const filename = Setting.custom_css_files.JOPLIN_APP;
|
||||
await CssUtils.injectCustomStyles(`${dir}/${filename}`);
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
try {
|
||||
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
|
||||
} catch (err) {
|
||||
reg.logger().error(err.message);
|
||||
}
|
||||
|
||||
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
||||
AlarmService.setLogger(reg.logger());
|
||||
|
||||
@@ -529,9 +526,21 @@ class Application extends BaseApplication {
|
||||
CommandService.instance().registerDeclaration(declaration);
|
||||
}
|
||||
|
||||
// Since the settings need to be loaded before the store is created, it will never
|
||||
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
|
||||
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
||||
const keymapService = KeymapService.instance();
|
||||
// We only add the commands that appear in the menu because only
|
||||
// those can have a shortcut associated with them.
|
||||
keymapService.initialize(menuCommandNames());
|
||||
|
||||
try {
|
||||
await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`);
|
||||
} catch (error) {
|
||||
reg.logger().error(error);
|
||||
}
|
||||
|
||||
// Since the settings need to be loaded before the store is
|
||||
// created, it will never receive the SETTING_UPDATE_ALL even,
|
||||
// which mean state.settings will not be initialised. So we
|
||||
// manually call dispatchUpdateAll() to force an update.
|
||||
Setting.dispatchUpdateAll();
|
||||
|
||||
await FoldersScreenUtils.refreshFolders();
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import KeymapService, { KeymapItem } from '../../lib/services/KeymapService';
|
||||
import KeymapService, { KeymapItem } from 'lib/services/KeymapService';
|
||||
import { ShortcutRecorder } from './ShortcutRecorder';
|
||||
import getLabel from './utils/getLabel';
|
||||
import useKeymap from './utils/useKeymap';
|
||||
@@ -106,7 +106,7 @@ export const KeymapConfigScreen = ({ themeId }: KeymapConfigScreenProps) => {
|
||||
|
||||
const renderError = (error: Error) => {
|
||||
return (
|
||||
<div style={styles.warning}>
|
||||
<div style={{ ...styles.warning, position: 'absolute', top: 0 }}>
|
||||
<p style={styles.text}>
|
||||
<span>
|
||||
{error.message}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, KeyboardEvent } from 'react';
|
||||
|
||||
import KeymapService from '../../lib/services/KeymapService';
|
||||
import KeymapService from 'lib/services/KeymapService';
|
||||
import styles_ from './styles';
|
||||
|
||||
import { _ } from 'lib/locale';
|
||||
@@ -41,7 +41,7 @@ export const ShortcutRecorder = ({ onSave, onReset, onCancel, onError, initialAc
|
||||
}
|
||||
}, [accelerator]);
|
||||
|
||||
const handleKeydown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const newAccelerator = keymapService.domToElectronAccelerator(event);
|
||||
|
||||
@@ -64,7 +64,7 @@ export const ShortcutRecorder = ({ onSave, onReset, onCancel, onError, initialAc
|
||||
<input
|
||||
value={accelerator}
|
||||
placeholder={_('Press the shortcut')}
|
||||
onKeyDown={handleKeydown}
|
||||
onKeyDown={handleKeyDown}
|
||||
style={styles.recorderInput}
|
||||
title={_('Press the shortcut and then press ENTER. Or, press BACKSPACE to clear the shortcut.')}
|
||||
readOnly
|
||||
|
@@ -5,7 +5,7 @@ import { _ } from 'lib/locale';
|
||||
|
||||
const commandService = CommandService.instance();
|
||||
|
||||
const getLabel = (commandName: string) => {
|
||||
const getLabel = (commandName: string):string => {
|
||||
if (commandService.exists(commandName)) return commandService.label(commandName, true);
|
||||
|
||||
// Some commands are not registered in CommandService at the moment
|
||||
|
@@ -1,11 +1,22 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import KeymapService, { KeymapItem } from '../../../lib/services/KeymapService';
|
||||
import KeymapService, { KeymapItem } from 'lib/services/KeymapService';
|
||||
import getLabel from './getLabel';
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
// This custom hook provides a synchronized snapshot of the keymap residing at KeymapService
|
||||
// All the logic regarding altering and interacting with the keymap is isolated from the components
|
||||
|
||||
function allKeymapItems() {
|
||||
const output = keymapService.getKeymapItems().slice();
|
||||
|
||||
output.sort((a:KeymapItem, b:KeymapItem) => {
|
||||
return getLabel(a.command).toLocaleLowerCase() < getLabel(b.command).toLocaleLowerCase() ? -1 : +1;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
const useKeymap = (): [
|
||||
KeymapItem[],
|
||||
Error,
|
||||
@@ -13,7 +24,7 @@ const useKeymap = (): [
|
||||
(commandName: string, accelerator: string) => void,
|
||||
(commandName: string) => void
|
||||
] => {
|
||||
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => keymapService.getKeymapItems());
|
||||
const [keymapItems, setKeymapItems] = useState<KeymapItem[]>(() => allKeymapItems());
|
||||
const [keymapError, setKeymapError] = useState<Error>(null);
|
||||
const [mustSave, setMustSave] = useState(false);
|
||||
|
||||
@@ -42,7 +53,7 @@ const useKeymap = (): [
|
||||
|
||||
const overrideKeymapItems = (customKeymapItems: KeymapItem[]) => {
|
||||
const oldKeymapItems = [...customKeymapItems];
|
||||
keymapService.initialize(); // Start with a fresh keymap
|
||||
keymapService.resetKeymap(); // Start with a fresh keymap
|
||||
|
||||
try {
|
||||
// First, try to update the in-memory keymap of KeymapService
|
||||
@@ -70,7 +81,8 @@ const useKeymap = (): [
|
||||
await keymapService.saveCustomKeymap();
|
||||
setKeymapError(null);
|
||||
} catch (err) {
|
||||
setKeymapError(err);
|
||||
const error = new Error(`Could not save file: ${err.message}`);
|
||||
setKeymapError(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -48,6 +48,7 @@ const commands = [
|
||||
require('./commands/moveToFolder'),
|
||||
require('./commands/newNote'),
|
||||
require('./commands/newFolder'),
|
||||
require('./commands/newSubFolder'),
|
||||
require('./commands/newTodo'),
|
||||
require('./commands/print'),
|
||||
require('./commands/renameFolder'),
|
||||
@@ -63,6 +64,9 @@ const commands = [
|
||||
require('./commands/toggleNoteList'),
|
||||
require('./commands/toggleSideBar'),
|
||||
require('./commands/toggleVisiblePanes'),
|
||||
require('./commands/openNote'),
|
||||
require('./commands/openFolder'),
|
||||
require('./commands/openTag'),
|
||||
];
|
||||
|
||||
class MainScreenComponent extends React.Component<any, any> {
|
||||
|
17
ElectronClient/gui/MainScreen/commands/newSubFolder.ts
Normal file
17
ElectronClient/gui/MainScreen/commands/newSubFolder.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import CommandService, { CommandContext, CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'newSubFolder',
|
||||
label: () => _('New sub-notebook'),
|
||||
iconName: 'fa-book',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, parentId:string = null) => {
|
||||
parentId = parentId || context.state.selectedFolderId;
|
||||
return CommandService.instance().execute('newFolder', parentId);
|
||||
},
|
||||
};
|
||||
};
|
16
ElectronClient/gui/MainScreen/commands/openFolder.ts
Normal file
16
ElectronClient/gui/MainScreen/commands/openFolder.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'openFolder',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, folderId:string) => {
|
||||
context.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folderId,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
26
ElectronClient/gui/MainScreen/commands/openNote.ts
Normal file
26
ElectronClient/gui/MainScreen/commands/openNote.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
const Note = require('lib/models/Note');
|
||||
const Folder = require('lib/models/Folder');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'openNote',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, noteId:string, hash:string = null) => {
|
||||
const note = await Note.load(noteId);
|
||||
if (!note) throw new Error(`No such note: ${noteId}`);
|
||||
|
||||
const folder = await Folder.load(note.parent_id);
|
||||
if (!folder) throw new Error(`Note parent notebook does not exist: ${JSON.stringify(note)}`);
|
||||
|
||||
context.dispatch({
|
||||
type: 'FOLDER_AND_NOTE_SELECT',
|
||||
folderId: folder.id,
|
||||
noteId: note.id,
|
||||
hash,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
16
ElectronClient/gui/MainScreen/commands/openTag.ts
Normal file
16
ElectronClient/gui/MainScreen/commands/openTag.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'openTag',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (context:CommandContext, tagId:string) => {
|
||||
context.dispatch({
|
||||
type: 'TAG_SELECT',
|
||||
id: tagId,
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
@@ -14,6 +14,7 @@ import InteropServiceHelper from '../InteropServiceHelper';
|
||||
import { _ } from 'lib/locale';
|
||||
import { MenuItem, MenuItemLocation } from 'lib/services/plugins/api/types';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
import menuCommandNames from './menuCommandNames';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { reg } = require('lib/registry.js');
|
||||
@@ -86,38 +87,7 @@ interface Props {
|
||||
pluginMenus: any[],
|
||||
}
|
||||
|
||||
const commandNames:string[] = [
|
||||
'focusElementSideBar',
|
||||
'focusElementNoteList',
|
||||
'focusElementNoteTitle',
|
||||
'focusElementNoteBody',
|
||||
'exportPdf',
|
||||
'newNote',
|
||||
'newTodo',
|
||||
'newFolder',
|
||||
'print',
|
||||
'synchronize',
|
||||
'textCopy',
|
||||
'textCut',
|
||||
'textPaste',
|
||||
'textSelectAll',
|
||||
'textBold',
|
||||
'textItalic',
|
||||
'textLink',
|
||||
'textCode',
|
||||
'insertDateTime',
|
||||
'attachFile',
|
||||
'focusSearch',
|
||||
'showLocalSearch',
|
||||
'toggleSideBar',
|
||||
'toggleNoteList',
|
||||
'toggleVisiblePanes',
|
||||
'toggleExternalEditing',
|
||||
'setTags',
|
||||
'showNoteContentProperties',
|
||||
'copyDevCommand',
|
||||
'openProfileDirectory',
|
||||
];
|
||||
const commandNames:string[] = menuCommandNames();
|
||||
|
||||
function menuItemSetChecked(id:string, checked:boolean) {
|
||||
const menu = Menu.getApplicationMenu();
|
||||
@@ -248,10 +218,8 @@ function useMenu(props:Props) {
|
||||
menuItemDic.focusElementNoteBody,
|
||||
];
|
||||
|
||||
let toolsItems:any[] = [];
|
||||
const importItems = [];
|
||||
const exportItems = [];
|
||||
const toolsItemsFirst = [];
|
||||
const templateItems:any[] = [];
|
||||
const ioService = InteropService.instance();
|
||||
const ioModules = ioService.modules();
|
||||
@@ -298,15 +266,18 @@ function useMenu(props:Props) {
|
||||
},
|
||||
};
|
||||
|
||||
const separator = () => {
|
||||
return {
|
||||
type: 'separator',
|
||||
};
|
||||
};
|
||||
|
||||
const newNoteItem = menuItemDic.newNote;
|
||||
const newTodoItem = menuItemDic.newTodo;
|
||||
const newFolderItem = menuItemDic.newFolder;
|
||||
const newSubFolderItem = menuItemDic.newSubFolder;
|
||||
const printItem = menuItemDic.print;
|
||||
|
||||
toolsItemsFirst.push(syncStatusItem, {
|
||||
type: 'separator',
|
||||
});
|
||||
|
||||
templateItems.push({
|
||||
label: _('Create note from template'),
|
||||
click: () => {
|
||||
@@ -340,18 +311,22 @@ function useMenu(props:Props) {
|
||||
},
|
||||
});
|
||||
|
||||
let toolsItems:any[] = [];
|
||||
|
||||
// we need this workaround, because on macOS the menu is different
|
||||
const toolsItemsWindowsLinux:any[] = toolsItemsFirst.concat([{
|
||||
label: _('Options'),
|
||||
visible: !shim.isMac(),
|
||||
accelerator: !shim.isMac() && keymapService.getAccelerator('config'),
|
||||
click: () => {
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Config',
|
||||
});
|
||||
const toolsItemsWindowsLinux:any[] = [
|
||||
{
|
||||
label: _('Options'),
|
||||
accelerator: keymapService.getAccelerator('config'),
|
||||
click: () => {
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Config',
|
||||
});
|
||||
},
|
||||
},
|
||||
} as any]);
|
||||
separator(),
|
||||
];
|
||||
|
||||
// the following menu items will be available for all OS under Tools
|
||||
const toolsItemsAll = [{
|
||||
@@ -423,7 +398,9 @@ function useMenu(props:Props) {
|
||||
},
|
||||
shim.isMac() ? noItem : newNoteItem,
|
||||
shim.isMac() ? noItem : newTodoItem,
|
||||
shim.isMac() ? noItem : newFolderItem, {
|
||||
shim.isMac() ? noItem : newFolderItem,
|
||||
shim.isMac() ? noItem : newSubFolderItem,
|
||||
{
|
||||
type: 'separator',
|
||||
visible: shim.isMac() ? false : true,
|
||||
}, {
|
||||
@@ -447,9 +424,7 @@ function useMenu(props:Props) {
|
||||
|
||||
menuItemDic.synchronize,
|
||||
|
||||
shim.isMac() ? syncStatusItem : noItem, {
|
||||
type: 'separator',
|
||||
}, shim.isMac() ? noItem : printItem, {
|
||||
shim.isMac() ? noItem : printItem, {
|
||||
type: 'separator',
|
||||
platforms: ['darwin'],
|
||||
},
|
||||
@@ -473,7 +448,9 @@ function useMenu(props:Props) {
|
||||
submenu: [
|
||||
newNoteItem,
|
||||
newTodoItem,
|
||||
newFolderItem, {
|
||||
newFolderItem,
|
||||
newSubFolderItem,
|
||||
{
|
||||
label: _('Close Window'),
|
||||
platforms: ['darwin'],
|
||||
accelerator: shim.isMac() && keymapService.getAccelerator('closeWindow'),
|
||||
@@ -512,12 +489,6 @@ function useMenu(props:Props) {
|
||||
});
|
||||
}
|
||||
|
||||
const separator = () => {
|
||||
return {
|
||||
type: 'separator',
|
||||
};
|
||||
};
|
||||
|
||||
const rootMenus:any = {
|
||||
edit: {
|
||||
id: 'edit',
|
||||
@@ -580,11 +551,6 @@ function useMenu(props:Props) {
|
||||
},
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
label: _('Focus'),
|
||||
submenu: focusItems,
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
label: _('Actual Size'),
|
||||
click: () => {
|
||||
@@ -617,6 +583,18 @@ function useMenu(props:Props) {
|
||||
accelerator: 'CommandOrControl+-',
|
||||
}],
|
||||
},
|
||||
go: {
|
||||
label: _('&Go'),
|
||||
submenu: [
|
||||
menuItemDic.historyBackward,
|
||||
menuItemDic.historyForward,
|
||||
separator(),
|
||||
{
|
||||
label: _('Focus'),
|
||||
submenu: focusItems,
|
||||
},
|
||||
],
|
||||
},
|
||||
note: {
|
||||
label: _('&Note'),
|
||||
submenu: [
|
||||
@@ -649,6 +627,8 @@ function useMenu(props:Props) {
|
||||
click: () => _checkForUpdates(),
|
||||
},
|
||||
separator(),
|
||||
syncStatusItem,
|
||||
separator(),
|
||||
{
|
||||
id: 'help:toggleDevTools',
|
||||
label: _('Toggle development tools'),
|
||||
@@ -703,6 +683,7 @@ function useMenu(props:Props) {
|
||||
const pluginMenuItems = PluginManager.instance().menuItems();
|
||||
for (const item of pluginMenuItems) {
|
||||
const itemParent = rootMenus[item.parent] ? rootMenus[item.parent] : 'tools';
|
||||
itemParent.submenu.push(separator());
|
||||
itemParent.submenu.push(item);
|
||||
}
|
||||
}
|
||||
@@ -735,6 +716,7 @@ function useMenu(props:Props) {
|
||||
rootMenus.file,
|
||||
rootMenus.edit,
|
||||
rootMenus.view,
|
||||
rootMenus.go,
|
||||
rootMenus.note,
|
||||
rootMenus.tools,
|
||||
rootMenus.help,
|
||||
@@ -742,52 +724,21 @@ function useMenu(props:Props) {
|
||||
|
||||
if (shim.isMac()) template.splice(0, 0, rootMenus.macOsApp);
|
||||
|
||||
// TODO
|
||||
|
||||
// function isEmptyMenu(template:any[]) {
|
||||
// for (let i = 0; i < template.length; i++) {
|
||||
// const t = template[i];
|
||||
// if (t.type !== 'separator') return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// function removeUnwantedItems(template:any[], screen:string) {
|
||||
// const platform = shim.platformName();
|
||||
|
||||
// let output = [];
|
||||
// for (let i = 0; i < template.length; i++) {
|
||||
// const t = Object.assign({}, template[i]);
|
||||
// if (t.screens && t.screens.indexOf(screen) < 0) continue;
|
||||
// if (t.platforms && t.platforms.indexOf(platform) < 0) continue;
|
||||
// if (t.submenu) t.submenu = removeUnwantedItems(t.submenu, screen);
|
||||
// if (('submenu' in t) && isEmptyMenu(t.submenu)) continue;
|
||||
// output.push(t);
|
||||
// }
|
||||
|
||||
// // Remove empty separator for now empty sections
|
||||
// const temp = [];
|
||||
// let previous = null;
|
||||
// for (let i = 0; i < output.length; i++) {
|
||||
// const t = Object.assign({}, output[i]);
|
||||
// if (t.type === 'separator') {
|
||||
// if (!previous) continue;
|
||||
// if (previous.type === 'separator') continue;
|
||||
// }
|
||||
// temp.push(t);
|
||||
// previous = t;
|
||||
// }
|
||||
// output = temp;
|
||||
|
||||
// return output;
|
||||
// }
|
||||
|
||||
if (props.routeName !== 'Main') {
|
||||
setMenu(Menu.buildFromTemplate([
|
||||
{
|
||||
label: _('&File'),
|
||||
submenu: [quitMenuItem],
|
||||
},
|
||||
{
|
||||
label: _('&Edit'),
|
||||
submenu: [
|
||||
menuItemDic.textCopy,
|
||||
menuItemDic.textCut,
|
||||
menuItemDic.textPaste,
|
||||
menuItemDic.textSelectAll,
|
||||
],
|
||||
},
|
||||
]));
|
||||
} else {
|
||||
setMenu(Menu.buildFromTemplate(template));
|
||||
|
37
ElectronClient/gui/menuCommandNames.ts
Normal file
37
ElectronClient/gui/menuCommandNames.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export default function() {
|
||||
return [
|
||||
'attachFile',
|
||||
'copyDevCommand',
|
||||
'exportPdf',
|
||||
'focusElementNoteBody',
|
||||
'focusElementNoteList',
|
||||
'focusElementNoteTitle',
|
||||
'focusElementSideBar',
|
||||
'focusSearch',
|
||||
'historyBackward',
|
||||
'historyForward',
|
||||
'insertDateTime',
|
||||
'newFolder',
|
||||
'newNote',
|
||||
'newSubFolder',
|
||||
'newTodo',
|
||||
'openProfileDirectory',
|
||||
'print',
|
||||
'setTags',
|
||||
'showLocalSearch',
|
||||
'showNoteContentProperties',
|
||||
'synchronize',
|
||||
'textBold',
|
||||
'textCode',
|
||||
'textCopy',
|
||||
'textCut',
|
||||
'textItalic',
|
||||
'textLink',
|
||||
'textPaste',
|
||||
'textSelectAll',
|
||||
'toggleExternalEditing',
|
||||
'toggleNoteList',
|
||||
'toggleSideBar',
|
||||
'toggleVisiblePanes',
|
||||
];
|
||||
}
|
59
ElectronClient/package-lock.json
generated
59
ElectronClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.15",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -644,9 +644,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
|
||||
"integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg=="
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
@@ -654,9 +654,9 @@
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
|
||||
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA=="
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "4.3.4",
|
||||
@@ -668,9 +668,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6770,9 +6770,9 @@
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz",
|
||||
"integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg=="
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
|
||||
"integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g=="
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.5.0",
|
||||
@@ -7312,9 +7312,9 @@
|
||||
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
|
||||
},
|
||||
"joplin-turndown": {
|
||||
"version": "4.0.29",
|
||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.29.tgz",
|
||||
"integrity": "sha512-rVGu8u4TpSRETo59/jiVW9iaXnpdxxpBHjb7nyCflkDfWhg1Kska4uagBQGw7cD2yxw7mB2YUIB/fAgtlIzcDQ==",
|
||||
"version": "4.0.30",
|
||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.30.tgz",
|
||||
"integrity": "sha512-OrGdNTsjI6/cbx/es9Hl0YI3YTql4SopduFcYCnWTZgqT0SJqILnF2JQxSNnbPnkSDIIRdNOG4+iNzlY6bS1nw==",
|
||||
"requires": {
|
||||
"css": "^2.2.4",
|
||||
"html-entities": "^1.2.1",
|
||||
@@ -7387,9 +7387,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.3",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
|
||||
"integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -7398,9 +7398,9 @@
|
||||
}
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
|
||||
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
||||
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
@@ -7408,11 +7408,11 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"requires": {
|
||||
"ajv": "^6.5.5",
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
@@ -10276,13 +10276,6 @@
|
||||
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"request-promise-native": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.15",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -139,13 +139,13 @@
|
||||
"form-data": "^2.3.2",
|
||||
"formatcoords": "^1.1.3",
|
||||
"fs-extra": "^5.0.0",
|
||||
"highlight.js": "^10.1.1",
|
||||
"highlight.js": "^10.2.1",
|
||||
"html-entities": "^1.2.1",
|
||||
"html-minifier": "^4.0.0",
|
||||
"htmlparser2": "^4.1.0",
|
||||
"image-type": "^3.0.0",
|
||||
"immer": "^7.0.5",
|
||||
"joplin-turndown": "^4.0.29",
|
||||
"joplin-turndown": "^4.0.30",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"jssha": "^2.3.1",
|
||||
|
@@ -421,7 +421,7 @@ class Dialog extends React.PureComponent<Props, State> {
|
||||
listItem_onClick(event:any) {
|
||||
const itemId = event.currentTarget.getAttribute('data-id');
|
||||
const parentId = event.currentTarget.getAttribute('data-parent-id');
|
||||
const itemType = event.currentTarget.getAttribute('data-type');
|
||||
const itemType = Number(event.currentTarget.getAttribute('data-type'));
|
||||
|
||||
this.gotoItem({
|
||||
id: itemId,
|
||||
@@ -557,7 +557,7 @@ GotoAnything.manifest = {
|
||||
menuItems: [
|
||||
{
|
||||
name: 'main',
|
||||
parent: 'tools',
|
||||
parent: 'go',
|
||||
label: _('Goto Anything...'),
|
||||
accelerator: () => KeymapService.instance().getAccelerator('gotoAnything'),
|
||||
screens: ['Main'],
|
||||
|
@@ -2,4 +2,5 @@ import { AppState } from '../../app';
|
||||
|
||||
export interface DesktopCommandContext {
|
||||
state: AppState,
|
||||
dispatch: Function,
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/
|
||||
|
||||
Operating System | Download | Alt. Download
|
||||
-----------------|----------|----------------
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplinapp.org/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.3.9/joplin-v1.3.9.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.3.9/joplin-v1.3.9-32bit.apk)
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplinapp.org/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.3.10/joplin-v1.3.10.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v1.3.10/joplin-v1.3.10-32bit.apk)
|
||||
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplinapp.org/images/BadgeIOS.png'/></a> | -
|
||||
|
||||
## Terminal application
|
||||
|
@@ -132,8 +132,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097597
|
||||
versionName "1.3.9"
|
||||
versionCode 2097599
|
||||
versionName "1.3.11"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ export default function useOnMessage(onCheckboxChange:Function, noteBody:string,
|
||||
onJoplinLinkClick(msg);
|
||||
} else if (msg.startsWith('error:')) {
|
||||
console.error(`Webview injected script error: ${msg}`);
|
||||
} else {
|
||||
onJoplinLinkClick(msg);
|
||||
}
|
||||
}, [onCheckboxChange, noteBody, onMarkForDownload, onJoplinLinkClick, onResourceLongPress]);
|
||||
}
|
||||
|
@@ -116,6 +116,7 @@ export default function useSource(noteBody:string, noteMarkupLanguage:number, th
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
${assetsToHeaders(result.pluginAssets, { asHtml: true })}
|
||||
</head>
|
||||
|
@@ -211,7 +211,9 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
};
|
||||
|
||||
this.useBetaEditor = () => {
|
||||
return Setting.value('editor.beta') && Platform.OS !== 'android';
|
||||
// Disable for now
|
||||
return false;
|
||||
// return Setting.value('editor.beta') && Platform.OS !== 'android';
|
||||
};
|
||||
|
||||
this.takePhoto_onPress = this.takePhoto_onPress.bind(this);
|
||||
|
@@ -42,6 +42,7 @@ class HtmlUtils {
|
||||
return selfClosingElements.includes(tagName.toLowerCase());
|
||||
}
|
||||
|
||||
// Returns the **encoded** URLs, so to be useful they should be decoded again before use.
|
||||
extractImageUrls(html) {
|
||||
if (!html) return [];
|
||||
|
||||
|
@@ -474,13 +474,13 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
|
||||
note.latitude = noteAttributes.latitude;
|
||||
note.longitude = noteAttributes.longitude;
|
||||
note.altitude = noteAttributes.altitude;
|
||||
note.author = noteAttributes.author;
|
||||
note.author = noteAttributes.author ? noteAttributes.author.trim() : '';
|
||||
note.is_todo = noteAttributes['reminder-order'] !== '0' && !!noteAttributes['reminder-order'];
|
||||
note.todo_due = dateToTimestamp(noteAttributes['reminder-time'], true);
|
||||
note.todo_completed = dateToTimestamp(noteAttributes['reminder-done-time'], true);
|
||||
note.order = dateToTimestamp(noteAttributes['reminder-order'], true);
|
||||
note.source = noteAttributes.source ? `evernote.${noteAttributes.source}` : 'evernote';
|
||||
note.source_url = noteAttributes['source-url'] ? noteAttributes['source-url'] : '';
|
||||
note.source = noteAttributes.source ? `evernote.${noteAttributes.source.trim()}` : 'evernote';
|
||||
note.source_url = noteAttributes['source-url'] ? noteAttributes['source-url'].trim() : '';
|
||||
|
||||
noteAttributes = null;
|
||||
} else if (n == 'resource') {
|
||||
@@ -488,9 +488,9 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
|
||||
id: noteResource.id,
|
||||
dataFilePath: noteResource.dataFilePath,
|
||||
dataEncoding: noteResource.dataEncoding,
|
||||
mime: noteResource.mime,
|
||||
title: noteResource.filename ? noteResource.filename : '',
|
||||
filename: noteResource.filename ? noteResource.filename : '',
|
||||
mime: noteResource.mime ? noteResource.mime.trim() : '',
|
||||
title: noteResource.filename ? noteResource.filename.trim() : '',
|
||||
filename: noteResource.filename ? noteResource.filename.trim() : '',
|
||||
});
|
||||
|
||||
noteResource = null;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// This plugin is used only on mobile, to highlight search results.
|
||||
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
import { RuleOptions } from 'lib/joplin-renderer/MdToHtml';
|
||||
|
||||
const stringUtils = require('../../stringUtils.js');
|
||||
const md5 = require('md5');
|
||||
@@ -67,4 +67,4 @@ function plugin(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
|
||||
export default {
|
||||
plugin,
|
||||
}
|
||||
};
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
import { RuleOptions } from 'lib/joplin-renderer/MdToHtml';
|
||||
|
||||
const htmlUtils = require('../../htmlUtils.js');
|
||||
const utils = require('../../utils');
|
||||
@@ -47,4 +47,4 @@ function plugin(markdownIt:any, ruleOptions:RuleOptions) {
|
||||
markdownIt.renderer.rules.html_inline = handleImageTags(htmlInlineDefaultRender);
|
||||
}
|
||||
|
||||
export default { plugin }
|
||||
export default { plugin };
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { RuleOptions } from "lib/joplin-renderer/MdToHtml";
|
||||
import { RuleOptions } from 'lib/joplin-renderer/MdToHtml';
|
||||
|
||||
let katex = require('katex');
|
||||
const md5 = require('md5');
|
||||
@@ -8,6 +8,54 @@ const mhchemModule = require('./katex_mhchem.js');
|
||||
// to serialize them with json-stringify-safe
|
||||
const stringifySafe = require('json-stringify-safe');
|
||||
|
||||
function stringifyKatexOptions(options:any) {
|
||||
if (!options) return '';
|
||||
|
||||
const newOptions = { ...options };
|
||||
|
||||
// The Katex macro structure is extremely verbose and slow to cache,
|
||||
// so we need bespoke code to serialize it.
|
||||
|
||||
// macros:
|
||||
// \hb: {tokens: Array(1), numArgs: 0}
|
||||
// \d: {tokens: Array(7), numArgs: 1}
|
||||
// \e:
|
||||
// tokens: Array(11)
|
||||
// 0: Token {text: "}", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 1: Token {text: "1", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 2: Token {text: "#", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 3: Token {text: "{", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 4: Token {text: "^", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 5: Token {text: "}", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 6: Token {text: "e", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 7: Token {text: " ", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 8: Token {text: "m", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 9: Token {text: " ", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// 10: Token {text: "{", loc: SourceLocation, noexpand: undefined, treatAsRelax: undefined}
|
||||
// numArgs: 1
|
||||
// \dv: {tokens: Array(15), numArgs: 2}
|
||||
// \ddv: {tokens: Array(19), numArgs: 2}
|
||||
// \pdv: {tokens: Array(15), numArgs: 2}
|
||||
// \pddv: {tokens: Array(15), numArgs: 2}
|
||||
// \abs: {tokens: Array(10), numArgs: 1}
|
||||
// \inflim: {tokens: Array(10), numArgs: 0}
|
||||
// \infint: {tokens: Array(2), numArgs: 0}
|
||||
// \prob: {tokens: Array(12), numArgs: 1}
|
||||
// \expval: {tokens: Array(14), numArgs: 2}
|
||||
// \wf: {tokens: Array(6), numArgs: 0}
|
||||
|
||||
if (options.macros) {
|
||||
const toSerialize:any = {};
|
||||
for (const k of Object.keys(options.macros)) {
|
||||
const macroText:string[] = options.macros[k].tokens.map((t:any) => t.text);
|
||||
toSerialize[k] = `${macroText.join('')}_${options.macros[k].numArgs}`;
|
||||
}
|
||||
newOptions.macros = toSerialize;
|
||||
}
|
||||
|
||||
return stringifySafe(newOptions);
|
||||
}
|
||||
|
||||
katex = mhchemModule(katex);
|
||||
|
||||
function katexStyle() {
|
||||
@@ -43,14 +91,13 @@ function katexStyle() {
|
||||
// Test if potential opening or closing delimieter
|
||||
// Assumes that there is a "$" at state.src[pos]
|
||||
function isValidDelim(state:any, pos:number) {
|
||||
let prevChar,
|
||||
nextChar,
|
||||
max = state.posMax,
|
||||
can_open = true,
|
||||
const max = state.posMax;
|
||||
|
||||
let can_open = true,
|
||||
can_close = true;
|
||||
|
||||
prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
|
||||
nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
|
||||
const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1;
|
||||
const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1;
|
||||
|
||||
// Check non-whitespace conditions for opening and closing, and
|
||||
// check that closing delimeter isn't followed by a number
|
||||
@@ -68,7 +115,7 @@ function isValidDelim(state:any, pos:number) {
|
||||
}
|
||||
|
||||
function math_inline(state:any, silent:boolean) {
|
||||
let start, match, token, res, pos;
|
||||
let match, token, res, pos;
|
||||
|
||||
if (state.src[state.pos] !== '$') {
|
||||
return false;
|
||||
@@ -87,7 +134,7 @@ function math_inline(state:any, silent:boolean) {
|
||||
// This loop will assume that the first leading backtick can not
|
||||
// be the first character in state.src, which is known since
|
||||
// we have found an opening delimieter already.
|
||||
start = state.pos + 1;
|
||||
const start = state.pos + 1;
|
||||
match = start;
|
||||
while ((match = state.src.indexOf('$', match)) !== -1) {
|
||||
// Found potential $, look for escapes, pos will point to
|
||||
@@ -148,7 +195,6 @@ function math_block(state:any, start:number, end:number, silent:boolean) {
|
||||
next,
|
||||
lastPos,
|
||||
found = false,
|
||||
token,
|
||||
pos = state.bMarks[start] + state.tShift[start],
|
||||
max = state.eMarks[start];
|
||||
|
||||
@@ -200,7 +246,7 @@ function math_block(state:any, start:number, end:number, silent:boolean) {
|
||||
|
||||
state.line = next + 1;
|
||||
|
||||
token = state.push('math_block', 'math', 0);
|
||||
const token = state.push('math_block', 'math', 0);
|
||||
token.block = true;
|
||||
token.content = (firstLine && firstLine.trim() ? `${firstLine}\n` : '') + state.getLines(start + 1, next, state.tShift[start], true) + (lastLine && lastLine.trim() ? lastLine : '');
|
||||
token.map = [start, state.line];
|
||||
@@ -211,13 +257,13 @@ function math_block(state:any, start:number, end:number, silent:boolean) {
|
||||
const cache_:any = {};
|
||||
|
||||
function renderToStringWithCache(latex:string, katexOptions:any) {
|
||||
const cacheKey = md5(escape(latex) + escape(stringifySafe(katexOptions)));
|
||||
const cacheKey = md5(escape(latex) + escape(stringifyKatexOptions(katexOptions)));
|
||||
if (cacheKey in cache_) {
|
||||
return cache_[cacheKey];
|
||||
} else {
|
||||
const beforeMacros = stringifySafe(katexOptions.macros);
|
||||
const beforeMacros = stringifyKatexOptions(katexOptions.macros);
|
||||
const output = katex.renderToString(latex, katexOptions);
|
||||
const afterMacros = stringifySafe(katexOptions.macros);
|
||||
const afterMacros = stringifyKatexOptions(katexOptions.macros);
|
||||
|
||||
// Don't cache the formulas that add macros, otherwise
|
||||
// they won't be added on second run.
|
||||
@@ -233,7 +279,7 @@ export default {
|
||||
// https://github.com/laurent22/joplin/issues/1105
|
||||
if (!options.context.userData.__katex) options.context.userData.__katex = { macros: {} };
|
||||
|
||||
const katexOptions:any = {}
|
||||
const katexOptions:any = {};
|
||||
katexOptions.macros = options.context.userData.__katex.macros;
|
||||
katexOptions.trust = true;
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@ function mermaidReady() {
|
||||
// <h1 id="mermaid">Mermaid</h1>
|
||||
//
|
||||
// And that's going to make the lib set the `mermaid` object to the H1 element.
|
||||
// So below, we double-check that what we have really is an instance of the library.
|
||||
// So below, we double-check that what we have really is an instance of the library.
|
||||
return typeof mermaid !== 'undefined' && mermaid !== null && typeof mermaid === 'object' && !!mermaid.init;
|
||||
}
|
||||
|
||||
|
@@ -87,11 +87,16 @@ class HtmlUtils {
|
||||
return tagStack[tagStack.length - 1];
|
||||
};
|
||||
|
||||
// The BASE tag allows changing the base URL from which files are loaded, and
|
||||
// that can break several plugins, such as Katex (which needs to load CSS
|
||||
// files using a relative URL). For that reason it is disabled.
|
||||
// More info: https://github.com/laurent22/joplin/issues/3021
|
||||
const disallowedTags = ['script', 'iframe', 'frameset', 'frame', 'object', 'base', 'embed'];
|
||||
// The BASE tag allows changing the base URL from which files are
|
||||
// loaded, and that can break several plugins, such as Katex (which
|
||||
// needs to load CSS files using a relative URL). For that reason
|
||||
// it is disabled. More info:
|
||||
// https://github.com/laurent22/joplin/issues/3021
|
||||
//
|
||||
// "link" can be used to escape the parser and inject JavaScript.
|
||||
// Adding "meta" too for the same reason as it shouldn't be used in
|
||||
// notes anyway.
|
||||
const disallowedTags = ['script', 'iframe', 'frameset', 'frame', 'object', 'base', 'embed', 'link', 'meta'];
|
||||
|
||||
const parser = new htmlparser2.Parser({
|
||||
|
||||
|
@@ -363,4 +363,4 @@ export default function(theme:any) {
|
||||
`;
|
||||
|
||||
return [css];
|
||||
};
|
||||
}
|
||||
|
@@ -616,9 +616,9 @@
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz",
|
||||
"integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg=="
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
|
||||
"integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g=="
|
||||
},
|
||||
"html-entities": {
|
||||
"version": "1.2.1",
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"base-64": "^0.1.0",
|
||||
"font-awesome-filetypes": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"highlight.js": "10.1.1",
|
||||
"highlight.js": "^10.2.1",
|
||||
"html-entities": "^1.2.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"katex": "^0.12.0",
|
||||
|
@@ -13,7 +13,7 @@ export function basename(path:string) {
|
||||
|
||||
export function filename(path:string, includeDir:boolean = false):string {
|
||||
if (!path) throw new Error('Path is empty');
|
||||
let output = includeDir ? path : basename(path);
|
||||
const output = includeDir ? path : basename(path);
|
||||
if (output.indexOf('.') < 0) return output;
|
||||
|
||||
const splitted = output.split('.');
|
||||
|
@@ -30,6 +30,13 @@ const markdownUtils = {
|
||||
return url;
|
||||
},
|
||||
|
||||
unescapeLinkUrl(url:string) {
|
||||
url = url.replace(/%28/g, '(');
|
||||
url = url.replace(/%29/g, ')');
|
||||
url = url.replace(/%20/g, ' ');
|
||||
return url;
|
||||
},
|
||||
|
||||
prependBaseUrl(md:string, baseUrl:string) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
return md.replace(/(\]\()([^\s\)]+)(.*?\))/g, (_match:any, before:string, url:string, after:string) => {
|
||||
@@ -37,6 +44,7 @@ const markdownUtils = {
|
||||
});
|
||||
},
|
||||
|
||||
// Returns the **encoded** URLs, so to be useful they should be decoded again before use.
|
||||
extractImageUrls(md:string) {
|
||||
const markdownIt = new MarkdownIt();
|
||||
setupLinkify(markdownIt); // Necessary to support file:/// links
|
||||
|
@@ -256,9 +256,11 @@ class BaseItem extends BaseModel {
|
||||
propValue = JSON.stringify(propValue);
|
||||
} else if (propValue === null || propValue === undefined) {
|
||||
propValue = '';
|
||||
} else {
|
||||
propValue = `${propValue}`;
|
||||
}
|
||||
|
||||
return propValue;
|
||||
return propValue.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
|
||||
}
|
||||
|
||||
static unserialize_format(type, propName, propValue) {
|
||||
@@ -279,7 +281,7 @@ class BaseItem extends BaseModel {
|
||||
propValue = Database.formatValue(ItemClass.fieldType(propName), propValue);
|
||||
}
|
||||
|
||||
return propValue;
|
||||
return typeof propValue === 'string' ? propValue.replace(/\\n/g, '\n').replace(/\\r/g, '\r') : propValue;
|
||||
}
|
||||
|
||||
static async serialize(item, shownKeys = null) {
|
||||
|
@@ -508,11 +508,16 @@ class Setting extends BaseModel {
|
||||
'folders.sortOrder.reverse': { value: false, type: SettingItemType.Bool, public: true, label: () => _('Reverse sort order'), appTypes: ['cli'] },
|
||||
trackLocation: { value: true, type: SettingItemType.Bool, section: 'note', public: true, label: () => _('Save geo-location with notes') },
|
||||
|
||||
// 2020-10-29: For now disable the beta editor due to
|
||||
// underlying bugs in the TextInput component which we cannot
|
||||
// fix. Also the editor crashes in Android and in some cases in
|
||||
// iOS.
|
||||
// https://discourse.joplinapp.org/t/anyone-using-the-beta-editor-on-ios/11658/9
|
||||
'editor.beta': {
|
||||
value: false,
|
||||
type: SettingItemType.Bool,
|
||||
section: 'note',
|
||||
public: mobilePlatform === 'ios',
|
||||
public: false, // mobilePlatform === 'ios',
|
||||
appTypes: ['mobile'],
|
||||
label: () => 'Opt-in to the editor beta',
|
||||
description: () => 'This beta adds list continuation, Markdown preview, and Markdown shortcuts. If you find bugs, please report them in the Discourse forum.',
|
||||
|
@@ -3,6 +3,7 @@ const { stringify } = require('query-string');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { _ } = require('lib/locale');
|
||||
const urlUtils = require('lib/urlUtils.js');
|
||||
|
||||
class OneDriveApi {
|
||||
// `isPublic` is to tell OneDrive whether the application is a "public" one (Mobile and desktop
|
||||
@@ -90,16 +91,16 @@ class OneDriveApi {
|
||||
}
|
||||
|
||||
async execTokenRequest(code, redirectUri) {
|
||||
const body = new shim.FormData();
|
||||
body.append('client_id', this.clientId());
|
||||
if (!this.isPublic()) body.append('client_secret', this.clientSecret());
|
||||
body.append('code', code);
|
||||
body.append('redirect_uri', redirectUri);
|
||||
body.append('grant_type', 'authorization_code');
|
||||
const body = {};
|
||||
body['client_id'] = this.clientId();
|
||||
if (!this.isPublic()) body['client_secret'] = this.clientSecret();
|
||||
body['code'] = code;
|
||||
body['redirect_uri'] = redirectUri;
|
||||
body['grant_type'] = 'authorization_code';
|
||||
|
||||
const r = await shim.fetch(this.tokenBaseUrl(), {
|
||||
method: 'POST',
|
||||
body: body,
|
||||
body: urlUtils.objectToQueryString(body),
|
||||
headers: {
|
||||
['Content-Type']: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
@@ -366,19 +367,21 @@ class OneDriveApi {
|
||||
throw new Error(_('Cannot refresh token: authentication data is missing. Starting the synchronisation again may fix the problem.'));
|
||||
}
|
||||
|
||||
const body = new shim.FormData();
|
||||
body.append('client_id', this.clientId());
|
||||
if (!this.isPublic()) body.append('client_secret', this.clientSecret());
|
||||
body.append('refresh_token', this.auth_.refresh_token);
|
||||
body.append('redirect_uri', 'http://localhost:1917');
|
||||
body.append('grant_type', 'refresh_token');
|
||||
const body = {};
|
||||
body['client_id'] = this.clientId();
|
||||
if (!this.isPublic()) body['client_secret'] = this.clientSecret();
|
||||
body['refresh_token'] = this.auth_.refresh_token;
|
||||
body['redirect_uri'] = 'http://localhost:1917';
|
||||
body['grant_type'] = 'refresh_token';
|
||||
|
||||
const options = {
|
||||
const response = await shim.fetch(this.tokenBaseUrl(), {
|
||||
method: 'POST',
|
||||
body: body,
|
||||
};
|
||||
body: urlUtils.objectToQueryString(body),
|
||||
headers: {
|
||||
['Content-Type']: 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
|
||||
const response = await shim.fetch(this.tokenBaseUrl(), options);
|
||||
if (!response.ok) {
|
||||
this.setAuth(null);
|
||||
const msg = await response.text();
|
||||
|
@@ -17,7 +17,7 @@ export function basename(path:string) {
|
||||
|
||||
export function filename(path:string, includeDir:boolean = false) {
|
||||
if (!path) throw new Error('Path is empty');
|
||||
let output = includeDir ? path : basename(path);
|
||||
const output = includeDir ? path : basename(path);
|
||||
if (output.indexOf('.') < 0) return output;
|
||||
|
||||
const splitted = output.split('.');
|
||||
|
@@ -47,7 +47,7 @@ const attributesToStr = (attributes) =>
|
||||
|
||||
const attachmentElement = ({ src, attributes, id }) =>
|
||||
[
|
||||
`<a href='joplin://${id}' ${attributesToStr(attributes)}>`,
|
||||
`<a href=':/${id}' ${attributesToStr(attributes)}>`,
|
||||
` ${attributes.alt || src}`,
|
||||
'</a>',
|
||||
].join('');
|
||||
|
@@ -11,6 +11,7 @@ type EnabledCondition = string;
|
||||
export interface CommandContext {
|
||||
// The state may also be of type "AppState" (used by the desktop app), which inherits from "State" (used by all apps)
|
||||
state: State,
|
||||
dispatch: Function,
|
||||
}
|
||||
|
||||
export interface CommandRuntime {
|
||||
@@ -143,8 +144,17 @@ export default class CommandService extends BaseService {
|
||||
return output;
|
||||
}
|
||||
|
||||
public commandNames() {
|
||||
return Object.keys(this.commands_);
|
||||
public commandNames(publicOnly:boolean = false) {
|
||||
if (publicOnly) {
|
||||
const output = [];
|
||||
for (const name in this.commands_) {
|
||||
if (!this.isPublic(name)) continue;
|
||||
output.push(name);
|
||||
}
|
||||
return output;
|
||||
} else {
|
||||
return Object.keys(this.commands_);
|
||||
}
|
||||
}
|
||||
|
||||
public commandByName(name:string, options:CommandByNameOptions = null):Command {
|
||||
@@ -203,10 +213,20 @@ export default class CommandService extends BaseService {
|
||||
delete command.runtime;
|
||||
}
|
||||
|
||||
private createContext():CommandContext {
|
||||
return {
|
||||
state: this.store_.getState(),
|
||||
dispatch: (action:any) => {
|
||||
this.store_.dispatch(action);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public async execute(commandName:string, ...args:any[]):Promise<any | void> {
|
||||
const command = this.commandByName(commandName);
|
||||
this.logger().info('CommandService::execute:', commandName, args);
|
||||
return command.runtime.execute({ state: this.store_.getState() }, ...args);
|
||||
if (!command.runtime) throw new Error(`Cannot execute a command without a runtime: ${commandName}`);
|
||||
return command.runtime.execute(this.createContext(), ...args);
|
||||
}
|
||||
|
||||
public scheduleExecute(commandName:string, args:any) {
|
||||
@@ -219,6 +239,10 @@ export default class CommandService extends BaseService {
|
||||
return stateToWhenClauseContext(this.store_.getState());
|
||||
}
|
||||
|
||||
public isPublic(commandName:string) {
|
||||
return !!this.label(commandName);
|
||||
}
|
||||
|
||||
// When looping on commands and checking their enabled state, the whenClauseContext
|
||||
// should be specified (created using currentWhenClauseContext) to avoid having
|
||||
// to re-create it on each call.
|
||||
|
@@ -106,8 +106,9 @@ class DecryptionWorker {
|
||||
if (!('errorHandler' in options)) options.errorHandler = 'log';
|
||||
|
||||
if (this.state_ !== 'idle') {
|
||||
this.logger().debug(`DecryptionWorker: cannot start because state is "${this.state_}"`);
|
||||
return;
|
||||
const msg = `DecryptionWorker: cannot start because state is "${this.state_}"`;
|
||||
this.logger().debug(msg);
|
||||
return { error: new Error(msg) };
|
||||
}
|
||||
|
||||
// Note: the logic below is an optimisation to avoid going through the loop if no master key exists
|
||||
@@ -115,7 +116,8 @@ class DecryptionWorker {
|
||||
// "throw" and "dispatch" logic.
|
||||
const loadedMasterKeyCount = await this.encryptionService().loadedMasterKeysCount();
|
||||
if (!loadedMasterKeyCount) {
|
||||
this.logger().info('DecryptionWorker: cannot start because no master key is currently loaded.');
|
||||
const msg = 'DecryptionWorker: cannot start because no master key is currently loaded.';
|
||||
this.logger().info(msg);
|
||||
const ids = await MasterKey.allIds();
|
||||
|
||||
if (ids.length) {
|
||||
@@ -130,7 +132,7 @@ class DecryptionWorker {
|
||||
});
|
||||
}
|
||||
}
|
||||
return;
|
||||
return { error: new Error(msg) };
|
||||
}
|
||||
|
||||
this.logger().info('DecryptionWorker: starting decryption...');
|
||||
|
@@ -16,7 +16,6 @@ const defaultKeymapItems = {
|
||||
{ accelerator: 'Cmd+N', command: 'newNote' },
|
||||
{ accelerator: 'Cmd+T', command: 'newTodo' },
|
||||
{ accelerator: 'Cmd+S', command: 'synchronize' },
|
||||
{ accelerator: '', command: 'print' },
|
||||
{ accelerator: 'Cmd+H', command: 'hideApp' },
|
||||
{ accelerator: 'Cmd+Q', command: 'quit' },
|
||||
{ accelerator: 'Cmd+,', command: 'config' },
|
||||
@@ -51,7 +50,6 @@ const defaultKeymapItems = {
|
||||
{ accelerator: 'Ctrl+N', command: 'newNote' },
|
||||
{ accelerator: 'Ctrl+T', command: 'newTodo' },
|
||||
{ accelerator: 'Ctrl+S', command: 'synchronize' },
|
||||
{ accelerator: '', command: 'print' },
|
||||
{ accelerator: 'Ctrl+Q', command: 'quit' },
|
||||
{ accelerator: 'Ctrl+Alt+I', command: 'insertTemplate' },
|
||||
{ accelerator: 'Ctrl+C', command: 'textCopy' },
|
||||
@@ -103,29 +101,42 @@ export default class KeymapService extends BaseService {
|
||||
super();
|
||||
|
||||
this.lastSaveTime_ = Date.now();
|
||||
|
||||
// By default, initialize for the current platform
|
||||
// Manual initialization allows testing for other platforms
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
public get lastSaveTime():number {
|
||||
return this.lastSaveTime_;
|
||||
}
|
||||
|
||||
public initialize(platform: string = shim.platformName()) {
|
||||
// `additionalDefaultCommandNames` will be added to the default keymap
|
||||
// **except** if they are already in it. Basically this is a mechanism
|
||||
// to add all the commands from the command service to the default
|
||||
// keymap.
|
||||
public initialize(additionalDefaultCommandNames:string[] = [], platform: string = shim.platformName()) {
|
||||
this.platform = platform;
|
||||
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
this.defaultKeymapItems = defaultKeymapItems.darwin;
|
||||
this.defaultKeymapItems = defaultKeymapItems.darwin.slice();
|
||||
this.modifiersRegExp = modifiersRegExp.darwin;
|
||||
break;
|
||||
default:
|
||||
this.defaultKeymapItems = defaultKeymapItems.default;
|
||||
this.defaultKeymapItems = defaultKeymapItems.default.slice();
|
||||
this.modifiersRegExp = modifiersRegExp.default;
|
||||
}
|
||||
|
||||
for (const name of additionalDefaultCommandNames) {
|
||||
if (this.defaultKeymapItems.find((item:KeymapItem) => item.command === name)) continue;
|
||||
this.defaultKeymapItems.push({
|
||||
command: name,
|
||||
accelerator: null,
|
||||
});
|
||||
}
|
||||
|
||||
this.resetKeymap();
|
||||
}
|
||||
|
||||
// Reset keymap back to its default values
|
||||
public resetKeymap() {
|
||||
this.keymap = {};
|
||||
for (let i = 0; i < this.defaultKeymapItems.length; i++) {
|
||||
// Keep the original defaultKeymapItems array untouched
|
||||
@@ -140,7 +151,9 @@ export default class KeymapService extends BaseService {
|
||||
if (await shim.fsDriver().exists(customKeymapPath)) {
|
||||
this.logger().info(`KeymapService: Loading keymap from file: ${customKeymapPath}`);
|
||||
|
||||
const customKeymapFile = await shim.fsDriver().readFile(customKeymapPath, 'utf-8');
|
||||
const customKeymapFile = (await shim.fsDriver().readFile(customKeymapPath, 'utf-8')).trim();
|
||||
if (!customKeymapFile) return;
|
||||
|
||||
// Custom keymaps are supposed to contain an array of keymap items
|
||||
this.overrideKeymap(JSON.parse(customKeymapFile));
|
||||
}
|
||||
@@ -183,8 +196,8 @@ export default class KeymapService extends BaseService {
|
||||
|
||||
if (!commandName) throw new Error('Cannot register an accelerator without a command name');
|
||||
|
||||
const validatedAccelerator = this.convertToPlatform(accelerator);
|
||||
this.validateAccelerator(validatedAccelerator);
|
||||
const validatedAccelerator = accelerator ? this.convertToPlatform(accelerator) : null;
|
||||
if (validatedAccelerator) this.validateAccelerator(validatedAccelerator);
|
||||
|
||||
this.keymap[commandName] = {
|
||||
command: commandName,
|
||||
@@ -264,7 +277,7 @@ export default class KeymapService extends BaseService {
|
||||
// Throws whenever there are duplicate Accelerators used in the keymap
|
||||
this.validateKeymap();
|
||||
} catch (err) {
|
||||
this.initialize(); // Discard all the changes if there are any issues
|
||||
this.resetKeymap(); // Discard all the changes if there are any issues
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
@@ -73,6 +73,20 @@ export default class ResourceEditWatcher {
|
||||
return this.eventEmitter_.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
externalApi() {
|
||||
return {
|
||||
openAndWatch: async ({ resourceId }:any) => {
|
||||
return this.openAndWatch(resourceId);
|
||||
},
|
||||
stopWatching: async ({ resourceId }:any) => {
|
||||
return this.stopWatching(resourceId);
|
||||
},
|
||||
isWatched: async ({ resourceId }:any) => {
|
||||
return !!this.watchedItemByResourceId(resourceId);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private watch(fileToWatch:string) {
|
||||
if (!this.chokidar_) return;
|
||||
|
||||
|
@@ -2,6 +2,7 @@ import Setting from 'lib/models/Setting';
|
||||
import Logger from 'lib/Logger';
|
||||
import shim from 'lib/shim';
|
||||
import uuid from 'lib/uuid';
|
||||
import markdownUtils from 'lib/markdownUtils';
|
||||
|
||||
const { ltrimSlashes } = require('lib/path-utils');
|
||||
const { Database } = require('lib/database.js');
|
||||
@@ -631,6 +632,9 @@ export default class Api {
|
||||
async downloadImage_(url:string /* , allowFileProtocolImages */) {
|
||||
const tempDir = Setting.value('tempDir');
|
||||
|
||||
// The URL we get to download have been extracted from the Markdown document
|
||||
url = markdownUtils.unescapeLinkUrl(url);
|
||||
|
||||
const isDataUrl = url && url.toLowerCase().indexOf('data:') === 0;
|
||||
|
||||
const name = isDataUrl ? md5(`${Math.random()}_${Date.now()}`) : filename(url);
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import ResourceEditWatcher from 'lib/services/ResourceEditWatcher/index';
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
|
||||
export default {
|
||||
|
||||
externalEditWatcher: () => ExternalEditWatcher.instance().externalApi(),
|
||||
resourceEditWatcher: () => ResourceEditWatcher.instance().externalApi(),
|
||||
|
||||
};
|
||||
|
@@ -13,6 +13,43 @@ const http = require('http');
|
||||
const https = require('https');
|
||||
const toRelative = require('relative');
|
||||
const timers = require('timers');
|
||||
const zlib = require('zlib');
|
||||
|
||||
function fileExists(filePath) {
|
||||
try {
|
||||
return fs.statSync(filePath).isFile();
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const gunzipFile = function(source, destination) {
|
||||
if (!fileExists(source)) {
|
||||
throw new Error(`No such file: ${source}`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// prepare streams
|
||||
const src = fs.createReadStream(source);
|
||||
const dest = fs.createWriteStream(destination);
|
||||
|
||||
// extract the archive
|
||||
src.pipe(zlib.createGunzip()).pipe(dest);
|
||||
|
||||
// callback on extract completion
|
||||
dest.on('close', function() {
|
||||
resolve();
|
||||
});
|
||||
|
||||
src.on('error', () => {
|
||||
reject();
|
||||
});
|
||||
|
||||
dest.on('error', () => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function shimInit() {
|
||||
shim.fsDriver = () => {
|
||||
@@ -365,9 +402,25 @@ function shimInit() {
|
||||
const request = http.request(requestOptions, function(response) {
|
||||
response.pipe(file);
|
||||
|
||||
const isGzipped = response.headers['content-encoding'] === 'gzip';
|
||||
|
||||
file.on('finish', function() {
|
||||
file.close(() => {
|
||||
resolve(makeResponse(response));
|
||||
file.close(async () => {
|
||||
if (isGzipped) {
|
||||
const gzipFilePath = `${filePath}.gzip`;
|
||||
await shim.fsDriver().move(filePath, gzipFilePath);
|
||||
|
||||
try {
|
||||
await gunzipFile(gzipFilePath, filePath);
|
||||
resolve(makeResponse(response));
|
||||
} catch (error) {
|
||||
cleanUpOnError(error);
|
||||
}
|
||||
|
||||
shim.fsDriver().remove(gzipFilePath);
|
||||
} else {
|
||||
resolve(makeResponse(response));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -36,21 +36,31 @@ function shimInit() {
|
||||
};
|
||||
|
||||
shim.fetch = async function(url, options = null) {
|
||||
// The native fetch() throws an uncatable error that crashes the app if calling it with an
|
||||
// invalid URL such as '//.resource' or "http://ocloud. de" so detect if the URL is valid beforehand
|
||||
// and throw a catchable error.
|
||||
// Bug: https://github.com/facebook/react-native/issues/7436
|
||||
// The native fetch() throws an uncatchable error that crashes the
|
||||
// app if calling it with an invalid URL such as '//.resource' or
|
||||
// "http://ocloud. de" so detect if the URL is valid beforehand and
|
||||
// throw a catchable error. Bug:
|
||||
// https://github.com/facebook/react-native/issues/7436
|
||||
const validatedUrl = urlValidator.isUri(url);
|
||||
if (!validatedUrl) throw new Error(`Not a valid URL: ${url}`);
|
||||
|
||||
return shim.fetchWithRetry(() => {
|
||||
// If the request has a body and it's not a GET call, and it doesn't have a Content-Type header
|
||||
// we display a warning, because it could trigger a "Network request failed" error.
|
||||
// If the request has a body and it's not a GET call, and it
|
||||
// doesn't have a Content-Type header we display a warning,
|
||||
// because it could trigger a "Network request failed" error.
|
||||
// https://github.com/facebook/react-native/issues/30176
|
||||
if (options?.body && options?.method && options.method !== 'GET' && !options?.headers?.['Content-Type']) {
|
||||
console.warn('Done a non-GET fetch call without a Content-Type header. It may make the request fail.', url, options);
|
||||
}
|
||||
|
||||
// Among React Native `fetch()` many bugs, one of them is that
|
||||
// it will truncate strings when they contain binary data.
|
||||
// Browser fetch() or Node fetch() work fine but as always RN's
|
||||
// one doesn't. There's no obvious way to fix this so we'll
|
||||
// have to wait if it's eventually fixed upstream. See here for
|
||||
// more info:
|
||||
// https://github.com/laurent22/joplin/issues/3986#issuecomment-718019688
|
||||
|
||||
return fetch(validatedUrl, options);
|
||||
}, options);
|
||||
};
|
||||
|
@@ -91,4 +91,18 @@ urlUtils.extractResourceUrls = function(text) {
|
||||
return output;
|
||||
};
|
||||
|
||||
urlUtils.objectToQueryString = function(query) {
|
||||
if (!query) return '';
|
||||
|
||||
let queryString = '';
|
||||
const s = [];
|
||||
for (const k in query) {
|
||||
if (!query.hasOwnProperty(k)) continue;
|
||||
s.push(`${encodeURIComponent(k)}=${encodeURIComponent(query[k])}`);
|
||||
}
|
||||
queryString = s.join('&');
|
||||
|
||||
return queryString;
|
||||
};
|
||||
|
||||
module.exports = urlUtils;
|
||||
|
6
ReactNativeClient/package-lock.json
generated
6
ReactNativeClient/package-lock.json
generated
@@ -4425,9 +4425,9 @@
|
||||
}
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz",
|
||||
"integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg=="
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
|
||||
"integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g=="
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.5.5",
|
||||
|
@@ -25,7 +25,7 @@
|
||||
"events": "^1.1.1",
|
||||
"font-awesome-filetypes": "^2.1.0",
|
||||
"form-data": "^2.1.4",
|
||||
"highlight.js": "10.1.1",
|
||||
"highlight.js": "^10.2.1",
|
||||
"html-entities": "^1.2.1",
|
||||
"htmlparser2": "^4.1.0",
|
||||
"immer": "^7.0.9",
|
||||
|
@@ -589,7 +589,7 @@ async function readmeFileTitle(sourcePath) {
|
||||
const r = md.match(/(^|\n)# (.*)/);
|
||||
|
||||
if (!r) {
|
||||
throw new Error('Could not determine title for Markdown file: ', sourcePath);
|
||||
throw new Error(`Could not determine title for Markdown file: ${sourcePath}`);
|
||||
} else {
|
||||
return r[2];
|
||||
}
|
||||
|
@@ -10,12 +10,13 @@ const { execCommand, githubUsername } = require('./tool-utils.js');
|
||||
|
||||
// From https://stackoverflow.com/a/6234804/561309
|
||||
function escapeHtml(unsafe) {
|
||||
// We only escape <> as this is enough for Markdown
|
||||
return unsafe
|
||||
.replace(/&/g, '&')
|
||||
// .replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
.replace(/>/g, '>');
|
||||
// .replace(/"/g, '"')
|
||||
// .replace(/'/g, ''');
|
||||
}
|
||||
|
||||
async function gitLog(sinceTag) {
|
||||
|
@@ -392,6 +392,22 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog_cli.md
|
||||
|
||||
<div class="main">
|
||||
<h1>Joplin terminal app changelog<a name="joplin-terminal-app-changelog" href="#joplin-terminal-app-changelog" class="heading-anchor">🔗</a></h1>
|
||||
<h2><a href="https://github.com/laurent22/joplin/releases/tag/cli-v1.3.3">cli-v1.3.3</a> - 2020-10-23T16:00:38Z<a name="cli-v1-3-3-https-github-com-laurent22-joplin-releases-tag-cli-v1-3-3-2020-10-23t16-00-38z" href="#cli-v1-3-3-https-github-com-laurent22-joplin-releases-tag-cli-v1-3-3-2020-10-23t16-00-38z" class="heading-anchor">🔗</a></h2>
|
||||
<ul>
|
||||
<li>Improved: Added support for a custom S3 URL (#3921) (#3691 by <a href="https://github.com/aaron">@aaron</a>)</li>
|
||||
<li>Improved: Allow setting note geolocation attributes via API (#3884)</li>
|
||||
<li>Improved: Import <strike>,<s> tags (strikethrough) from Evernote (#3936 by Ian Slinger)</li>
|
||||
<li>Improved: Removed OneDrive Dev sync target which was not really useful</li>
|
||||
<li>Improved: Sort search results by average of multiple criteria, including 'Sort notes by' field setting (#3777 by <a href="https://github.com/shawnaxsom">@shawnaxsom</a>)</li>
|
||||
<li>Improved: Sort tags in a case-insensitive way</li>
|
||||
<li>Improved: Updated installation script with BSD support (#3930 by Andros Fenollosa)</li>
|
||||
<li>Fixed: Crash when trying to change app locale (#3847)</li>
|
||||
<li>Fixed: Fix search filters when language is in Korean or with accents (#3947 by Naveen M V)</li>
|
||||
<li>Fixed: Fixed freeze when importing ENEX as HTML, and fixed potential error when importing resources (#3958)</li>
|
||||
<li>Fixed: Fixed setting issue that would cause a password to be saved in plain text in the database, even when the keychain is working</li>
|
||||
<li>Fixed: Importing ENEX as HTML was importing as Markdown (#3923)</li>
|
||||
<li>Fixed: Regression: Fix export of pluginAssets when exporting to html/pdf (#3927 by Caleb John)</li>
|
||||
</ul>
|
||||
<h2><a href="https://github.com/laurent22/joplin/releases/tag/cli-v1.2.3">cli-v1.2.3</a> - 2020-10-09T11:17:18Z<a name="cli-v1-2-3-https-github-com-laurent22-joplin-releases-tag-cli-v1-2-3-2020-10-09t11-17-18z" href="#cli-v1-2-3-https-github-com-laurent22-joplin-releases-tag-cli-v1-2-3-2020-10-09t11-17-18z" class="heading-anchor">🔗</a></h2>
|
||||
<ul>
|
||||
<li>Improved: Improved handling of database migration failures</li>
|
||||
|
@@ -440,7 +440,7 @@ https://github.com/laurent22/joplin/blob/dev/README.md
|
||||
<tr>
|
||||
<td>Android</td>
|
||||
<td><a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplinapp.org/images/BadgeAndroid.png'/></a></td>
|
||||
<td>or download the APK file: <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.2.6/joplin-v1.2.6.apk">64-bit</a> <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.2.6/joplin-v1.2.6-32bit.apk">32-bit</a></td>
|
||||
<td>or download the APK file: <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.3.10/joplin-v1.3.10.apk">64-bit</a> <a href="https://github.com/laurent22/joplin-android/releases/download/android-v1.3.10/joplin-v1.3.10-32bit.apk">32-bit</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>iOS</td>
|
||||
|
440
docs/privacy/index.html
Normal file
440
docs/privacy/index.html
Normal file
@@ -0,0 +1,440 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<!--
|
||||
|
||||
!!! WARNING !!!
|
||||
|
||||
This file was auto-generated from readme/privacy.md and any manual change
|
||||
made to it will be overwritten. To make a change to this file please modify
|
||||
the source Markdown file:
|
||||
|
||||
https://github.com/laurent22/joplin/blob/dev/readme/privacy.md
|
||||
|
||||
-->
|
||||
|
||||
<head>
|
||||
<title>Joplin Privacy Policy | Joplin</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://joplinapp.org/css/bootstrap.min.css">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="https://joplinapp.org/favicon.ico">
|
||||
<!-- <link rel="stylesheet" href="https://joplinapp.org/css/fontawesome-all.min.css"> -->
|
||||
<link rel="stylesheet" href="https://joplinapp.org/css/fork-awesome.min.css">
|
||||
<script src="https://joplinapp.org/js/jquery-3.2.1.slim.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #F1F1F1;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.root {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a[href^="mailto:"] {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
td, th {
|
||||
padding: .8em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.page-markdown table pre,
|
||||
.page-markdown table blockquote {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.page-markdown table pre,
|
||||
.page-markdown table blockquote {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.page-markdown table pre {
|
||||
background-color: rgba(0,0,0,0);
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding-bottom: 0.3em;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-weight: 600;
|
||||
font-size: 2em;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
code {
|
||||
color: black;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
font-size: .85em;
|
||||
word-break: break-all;
|
||||
}
|
||||
pre code {
|
||||
border: none;
|
||||
}
|
||||
pre {
|
||||
font-size: .85em;
|
||||
}
|
||||
blockquote {
|
||||
font-size: 1em;
|
||||
color: #555;
|
||||
};
|
||||
#toc ul {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#toc > ul > li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#toc {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.title-icon {
|
||||
display: flex;
|
||||
height: 1em;
|
||||
}
|
||||
.title-text {
|
||||
display: flex;
|
||||
font-weight: normal;
|
||||
margin-bottom: .2em;
|
||||
margin-left: .5em;
|
||||
}
|
||||
.sub-title {
|
||||
font-weight: normal;
|
||||
}
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 0;
|
||||
box-shadow: 0 10px 20px #888888;
|
||||
}
|
||||
table.screenshots {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
table.screenshots th {
|
||||
height: 3em;
|
||||
text-align: center;
|
||||
}
|
||||
table.screenshots th,
|
||||
table.screenshots td {
|
||||
border: 1px solid #C2C2C2;
|
||||
}
|
||||
img[align="left"] {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mobile-screenshot {
|
||||
height: 40em;
|
||||
padding: 1em;
|
||||
}
|
||||
.cli-screenshot-wrapper {
|
||||
background-color: black;
|
||||
vertical-align: top;
|
||||
padding: 1em 2em 1em 1em;
|
||||
}
|
||||
.cli-screenshot {
|
||||
font-family: "Monaco", "Inconsolata", "CONSOLAS", "Deja Vu Sans Mono", "Droid Sans Mono", "Andale Mono", monospace;
|
||||
background-color: black;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
.cli-screenshot .prompt {
|
||||
color: #48C2F0;
|
||||
}
|
||||
.top-screenshot {
|
||||
margin-top: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
.header {
|
||||
position: relative;
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
color: white;
|
||||
background-color: #2B2B3D;
|
||||
}
|
||||
.header a h1 {
|
||||
color: white;
|
||||
}
|
||||
.header a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.content {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
padding-bottom: 2em;
|
||||
padding-top: 2em;
|
||||
}
|
||||
.forkme {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top:0;
|
||||
}
|
||||
.nav-wrapper {
|
||||
position: relative;
|
||||
width: inherit;
|
||||
}
|
||||
.nav {
|
||||
background-color: black;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.nav.sticky {
|
||||
position:fixed;
|
||||
top: 0;
|
||||
width: inherit;
|
||||
box-shadow: 0 0 10px #000000;
|
||||
}
|
||||
.nav a {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
padding: .6em .9em .6em .9em;
|
||||
}
|
||||
.nav ul {
|
||||
padding-left: 2em;
|
||||
margin-bottom: 0;
|
||||
display: table-cell;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.nav ul li {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
.nav li.selected {
|
||||
background-color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
.nav-right {
|
||||
display: flex;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
line-height: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nav-right .share-btn {
|
||||
display: none;
|
||||
}
|
||||
.nav-right .small-share-btn {
|
||||
display: none;
|
||||
}
|
||||
.footer {
|
||||
padding: 2em;
|
||||
border-top: 1px solid #d4d4d4;
|
||||
margin-top: 2em;
|
||||
color: gray;
|
||||
font-size: .9em;
|
||||
}
|
||||
a.heading-anchor {
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
width: 1.3em;
|
||||
font-size: 0.7em;
|
||||
margin-left: 0.4em;
|
||||
line-height: 1em;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
a.heading-anchor:hover,
|
||||
h1:hover a.heading-anchor,
|
||||
h2:hover a.heading-anchor,
|
||||
h3:hover a.heading-anchor,
|
||||
h4:hover a.heading-anchor,
|
||||
h5:hover a.heading-anchor,
|
||||
h6:hover a.heading-anchor {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.content{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#toc{
|
||||
display: block!important;
|
||||
align-self: flex-start;
|
||||
width: 300px;
|
||||
position: sticky; top: 20px; left: 0;
|
||||
}
|
||||
|
||||
.main{
|
||||
width: calc(100% - 300px);
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border-top: 1px solid #d4d4d4;
|
||||
margin-top: 30px;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
@media all and (min-width: 400px) {
|
||||
.nav-right .share-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav-right .small-share-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container root page-privacy">
|
||||
|
||||
<div class="header">
|
||||
<a class="forkme" href="https://github.com/laurent22/joplin"><img src="https://joplinapp.org/images/ForkMe.png"/></a>
|
||||
<a href="https://joplinapp.org"><h1 class="title"><img class="title-icon" src="https://joplinapp.org/images/Icon512.png"><span class="title-text">Joplin</span></h1></a>
|
||||
<p class="sub-title">An open source note taking and to-do application with synchronisation capabilities</p>
|
||||
</div>
|
||||
|
||||
<div class="nav-wrapper">
|
||||
<div class="nav">
|
||||
<ul>
|
||||
<li class=""><a href="https://joplinapp.org/" title="Home"><i class="fa fa-home"></i></a></li>
|
||||
<li><a href="https://discourse.joplinapp.org" title="Forum">Forum</a></li>
|
||||
<li><a class="help" href="#" title="Menu">Menu</a></li>
|
||||
<!-- <li><a class="gsod" href="https://joplinapp.org/gsod2020/" title="Google Season of Docs 2020">GSoD 2020</a></li> -->
|
||||
</ul>
|
||||
<div class="nav-right">
|
||||
<!--
|
||||
<iframe class="share-btn" src="https://www.facebook.com/plugins/share_button.php?href=http%3A%2F%2Fjoplinapp.org&layout=button&size=small&mobile_iframe=true&width=60&height=20&appId" width="60" height="20" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
|
||||
<iframe class="share-btn" src="https://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fjoplinapp.org" width="62" height="20" title="Tweet" style="border: 0; overflow: hidden;"></iframe>
|
||||
-->
|
||||
<iframe class="share-btn share-btn-github" src="https://ghbtns.com/github-btn.html?user=laurent22&repo=joplin&type=star&count=true" frameborder="0" scrolling="0" width="100px" height="20px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div id="toc"><ul>
|
||||
<li>
|
||||
<p>Applications</p>
|
||||
<ul>
|
||||
<li><a href="https://joplinapp.org/desktop/">Desktop application</a></li>
|
||||
<li><a href="https://joplinapp.org/mobile/">Mobile applications</a></li>
|
||||
<li><a href="https://joplinapp.org/terminal/">Terminal application</a></li>
|
||||
<li><a href="https://joplinapp.org/clipper/">Web Clipper</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Support</p>
|
||||
<ul>
|
||||
<li><a href="https://discourse.joplinapp.org">Joplin Forum</a></li>
|
||||
<li><a href="https://joplinapp.org/markdown/">Markdown Guide</a></li>
|
||||
<li><a href="https://joplinapp.org/e2ee/">How to enable end-to-end encryption</a></li>
|
||||
<li><a href="https://joplinapp.org/conflict/">What is a conflict?</a></li>
|
||||
<li><a href="https://joplinapp.org/debugging/">How to enable debug mode</a></li>
|
||||
<li><a href="https://joplinapp.org/faq/">FAQ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Joplin API</p>
|
||||
<ul>
|
||||
<li><a href="https://joplinapp.org/api/overview/">Joplin API Overview</a></li>
|
||||
<li><a href="https://joplinapp.org/api/get_started/plugins/">Plugin development</a></li>
|
||||
<li><a href="https://joplinapp.org/api/tutorials/toc_plugin/">Plugin tutorial</a></li>
|
||||
<li><a href="https://joplinapp.org/api/references/plugin_api/classes/joplin.html">Plugin API</a></li>
|
||||
<li><a href="https://joplinapp.org/api/references/rest_api/">Data API documentation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Development</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/laurent22/joplin/blob/dev/BUILD.md">How to build the apps</a></li>
|
||||
<li><a href="https://joplinapp.org/spec/e2ee/">End-to-end encryption spec</a></li>
|
||||
<li><a href="https://joplinapp.org/spec/history/">Note History spec</a></li>
|
||||
<li><a href="https://joplinapp.org/spec/sync_lock/">Sync Lock spec</a></li>
|
||||
<li><a href="https://joplinapp.org/spec/plugins/">Plugin Architecture spec</a></li>
|
||||
<li><a href="https://joplinapp.org/spec/search_sorting/">Search Sorting spec</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Google Summer of Code 2020</p>
|
||||
<ul>
|
||||
<li><a href="https://joplinapp.org/gsoc2020/index/">Google Summer of Code 2020</a></li>
|
||||
<li><a href="https://joplinapp.org/gsoc2020/ideas/">Project Ideas</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>About</p>
|
||||
<ul>
|
||||
<li><a href="https://joplinapp.org/changelog/">Changelog (Desktop App)</a></li>
|
||||
<li><a href="https://joplinapp.org/changelog_cli/">Changelog (CLI App)</a></li>
|
||||
<li><a href="https://joplinapp.org/stats/">Stats</a></li>
|
||||
<li><a href="https://joplinapp.org/donate/">Donate</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<h1>Joplin Privacy Policy<a name="joplin-privacy-policy" href="#joplin-privacy-policy" class="heading-anchor">🔗</a></h1>
|
||||
<p>The Joplin applications, including the Android, iOS, Windows, macOS and Linux applications, do not send any data to any service without your authorisation. Any data that Joplin saves, such as notes or images, are saved to your own device and you are free to delete this data at any time.</p>
|
||||
<p>If you choose to synchronise with a third-party, such as OneDrive or Dropbox, the notes will be sent to that account, in which case the third-party privacy policy applies.</p>
|
||||
<p>For any question about Joplin privacy policy, please leave a message <a href="https://discourse.joplinapp.org/">on the forum</a>.</p>
|
||||
|
||||
<div class="bottom-links">
|
||||
<a href="https://github.com/laurent22/joplin/blob/dev/readme/privacy.md">
|
||||
<i class="fa fa-github"></i> Improve this doc
|
||||
</a>
|
||||
</div>
|
||||
<script>
|
||||
function stickyHeader() {
|
||||
return; // Disabled
|
||||
|
||||
if ($(window).scrollTop() > 179) {
|
||||
$('.nav').addClass('sticky');
|
||||
} else {
|
||||
$('.nav').removeClass('sticky');
|
||||
}
|
||||
}
|
||||
|
||||
$('#toc').hide();
|
||||
|
||||
$('.help').click(function(event) {
|
||||
event.preventDefault();
|
||||
$('#toc').show();
|
||||
});
|
||||
|
||||
$(window).scroll(function() {
|
||||
stickyHeader();
|
||||
});
|
||||
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-103586105-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
|
||||
</div></div>
|
||||
|
||||
<div class="footer">
|
||||
Copyright (C) 2016-2020 Laurent Cozic
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
84
package-lock.json
generated
84
package-lock.json
generated
@@ -319,14 +319,14 @@
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
|
||||
"integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg=="
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
|
||||
},
|
||||
"acorn-globals": {
|
||||
"version": "4.3.4",
|
||||
@@ -335,6 +335,13 @@
|
||||
"requires": {
|
||||
"acorn": "^6.0.1",
|
||||
"acorn-walk": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"acorn-jsx": {
|
||||
@@ -352,6 +359,7 @@
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -757,9 +765,9 @@
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
|
||||
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
||||
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
|
||||
},
|
||||
"bach": {
|
||||
"version": "1.2.0",
|
||||
@@ -2652,7 +2660,8 @@
|
||||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.0.4",
|
||||
@@ -3821,12 +3830,30 @@
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||
"requires": {
|
||||
"ajv": "^6.5.5",
|
||||
"ajv": "^6.12.3",
|
||||
"har-schema": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"has": {
|
||||
@@ -4404,9 +4431,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"joplin-turndown": {
|
||||
"version": "4.0.29",
|
||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.29.tgz",
|
||||
"integrity": "sha512-rVGu8u4TpSRETo59/jiVW9iaXnpdxxpBHjb7nyCflkDfWhg1Kska4uagBQGw7cD2yxw7mB2YUIB/fAgtlIzcDQ==",
|
||||
"version": "4.0.30",
|
||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.30.tgz",
|
||||
"integrity": "sha512-OrGdNTsjI6/cbx/es9Hl0YI3YTql4SopduFcYCnWTZgqT0SJqILnF2JQxSNnbPnkSDIIRdNOG4+iNzlY6bS1nw==",
|
||||
"requires": {
|
||||
"css": "^2.2.4",
|
||||
"html-entities": "^1.2.1",
|
||||
@@ -4470,13 +4497,6 @@
|
||||
"whatwg-url": "^7.0.0",
|
||||
"ws": "^7.0.0",
|
||||
"xml-name-validator": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
|
||||
"integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"json-parse-better-errors": {
|
||||
@@ -5453,16 +5473,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
|
||||
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
|
||||
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
|
||||
"requires": {
|
||||
"deep-is": "~0.1.3",
|
||||
"fast-levenshtein": "~2.0.4",
|
||||
"fast-levenshtein": "~2.0.6",
|
||||
"levn": "~0.3.0",
|
||||
"prelude-ls": "~1.1.2",
|
||||
"type-check": "~0.3.2",
|
||||
"wordwrap": "~1.0.0"
|
||||
"word-wrap": "~1.2.3"
|
||||
}
|
||||
},
|
||||
"ordered-read-streams": {
|
||||
@@ -7328,13 +7348,13 @@
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "3.0.1",
|
||||
|
@@ -52,7 +52,7 @@
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.11.0",
|
||||
"immer": "^7.0.5",
|
||||
"joplin-turndown": "^4.0.29",
|
||||
"joplin-turndown": "^4.0.30",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"relative": "^3.0.2"
|
||||
}
|
||||
|
@@ -1,5 +1,21 @@
|
||||
# Joplin terminal app changelog
|
||||
|
||||
## [cli-v1.3.3](https://github.com/laurent22/joplin/releases/tag/cli-v1.3.3) - 2020-10-23T16:00:38Z
|
||||
|
||||
- Improved: Added support for a custom S3 URL (#3921) (#3691 by [@aaron](https://github.com/aaron))
|
||||
- Improved: Allow setting note geolocation attributes via API (#3884)
|
||||
- Improved: Import <strike>,<s> tags (strikethrough) from Evernote (#3936 by Ian Slinger)
|
||||
- Improved: Removed OneDrive Dev sync target which was not really useful
|
||||
- Improved: Sort search results by average of multiple criteria, including 'Sort notes by' field setting (#3777 by [@shawnaxsom](https://github.com/shawnaxsom))
|
||||
- Improved: Sort tags in a case-insensitive way
|
||||
- Improved: Updated installation script with BSD support (#3930 by Andros Fenollosa)
|
||||
- Fixed: Crash when trying to change app locale (#3847)
|
||||
- Fixed: Fix search filters when language is in Korean or with accents (#3947 by Naveen M V)
|
||||
- Fixed: Fixed freeze when importing ENEX as HTML, and fixed potential error when importing resources (#3958)
|
||||
- Fixed: Fixed setting issue that would cause a password to be saved in plain text in the database, even when the keychain is working
|
||||
- Fixed: Importing ENEX as HTML was importing as Markdown (#3923)
|
||||
- Fixed: Regression: Fix export of pluginAssets when exporting to html/pdf (#3927 by Caleb John)
|
||||
|
||||
## [cli-v1.2.3](https://github.com/laurent22/joplin/releases/tag/cli-v1.2.3) - 2020-10-09T11:17:18Z
|
||||
|
||||
- Improved: Improved handling of database migration failures
|
||||
|
7
readme/privacy.md
Normal file
7
readme/privacy.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Joplin Privacy Policy
|
||||
|
||||
The Joplin applications, including the Android, iOS, Windows, macOS and Linux applications, do not send any data to any service without your authorisation. Any data that Joplin saves, such as notes or images, are saved to your own device and you are free to delete this data at any time.
|
||||
|
||||
If you choose to synchronise with a third-party, such as OneDrive or Dropbox, the notes will be sent to that account, in which case the third-party privacy policy applies.
|
||||
|
||||
For any question about Joplin privacy policy, please leave a message [on the forum](https://discourse.joplinapp.org/).
|
Reference in New Issue
Block a user