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

Compare commits

..

83 Commits

Author SHA1 Message Date
Laurent Cozic
9f5924984c Plugin Generator release v2.12.1 2023-06-28 15:13:26 +01:00
Laurent Cozic
6db5723a50 Setup new release 2.12 2023-06-28 15:12:32 +01:00
Laurent Cozic
1d0bcc435b Generator: Updated doc 2023-06-28 14:49:19 +01:00
Laurent Cozic
700158d83c Generator: Fixed incompatbility with latest Yeoman version 2023-06-28 14:48:52 +01:00
Laurent Cozic
df4e8c9f47 Generator: Add script updateVersion to update both manifest and package version numbers 2023-06-28 14:48:04 +01:00
renovate[bot]
ce6baccd82 Update dependency react-native-modal-datetime-picker to v15.0.1 (#8378)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-28 01:58:38 +00:00
renovate[bot]
0caf1fdd7b Update dependency react-native-modal-datetime-picker to v15 (#8377)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-27 22:03:22 +01:00
renovate[bot]
2a10cd3333 Update dependency react-native-get-random-values to v1.9.0 (#8372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-27 10:36:20 +01:00
Laurent Cozic
3ec257abc3 Plugin Generator release v2.11.1 2023-06-27 10:34:50 +01:00
Laurent Cozic
295756a528 Merge branch 'release-2.11' into dev 2023-06-27 10:33:22 +01:00
Laurent Cozic
5603900fc2 CLI v2.11.1 2023-06-27 10:29:24 +01:00
Laurent Cozic
e1195f7370 Lock file 2023-06-27 10:25:21 +01:00
Laurent Cozic
5625b15cb5 Releasing sub-packages 2023-06-27 10:24:24 +01:00
Arda Kılıçdağı
af4147ed58 All: Translation: Update tr_TR.po (#8368) 2023-06-27 02:53:18 -04:00
Harris Arvanitis
9e10261e0b All: Translation: Update el_GR.po (#8357) 2023-06-27 02:51:29 -04:00
Joplin Bot
487fecb16e Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-26 12:21:51 +00:00
Laurent Cozic
e0ce301c3f lock file 2023-06-26 13:00:45 +01:00
Laurent Cozic
bb474287e5 Merge branch 'release-2.11' into dev 2023-06-26 12:17:23 +01:00
pedr
583ae0385b Desktop: Fixes #8317: Removing Markdown when using 'Paste as Text' (#8351) 2023-06-26 12:00:47 +01:00
renovate[bot]
35a4eef87b Update dependency @electron/remote to v2.0.10 (#8365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-26 02:20:40 +00:00
Laurent Cozic
e46ecdcb2b Android 2.11.31 2023-06-25 15:28:47 +01:00
Laurent Cozic
6036420bc6 iOS 12.11.5 2023-06-25 15:06:29 +01:00
Laurent Cozic
cd73661200 lock file 2023-06-25 14:52:55 +01:00
Laurent Cozic
5dbadbbff6 iOS 12.11.4 2023-06-25 14:07:09 +01:00
Laurent Cozic
2dc02fe0f2 All: Resolves #7686: Upgrade E2EE encryption method to AES-256 2023-06-25 14:04:42 +01:00
renovate[bot]
a474b403f9 Update dependency tap to v16.3.6 (#8359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-25 00:50:38 +00:00
renovate[bot]
e9a8475ecf Update dependency pg to v8.11.0 (#8358)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-24 22:43:09 +01:00
github-actions[bot]
0f9bdf1ca0 @XarisA has signed the CLA in laurent22/joplin#8357 2023-06-23 20:43:15 +00:00
Joplin Bot
a7781f2a6b Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-23 18:17:44 +00:00
Laurent Cozic
71144bb675 Tools: Fixed buildPluginDoc command 2023-06-23 18:18:25 +01:00
Laurent Cozic
6886f6fd39 Desktop release v2.11.11 2023-06-23 15:10:56 +01:00
Laurent Cozic
83610c01c0 Tools: Switch to @electron/notarize 2023-06-23 15:10:05 +01:00
Laurent Cozic
5b18edc8be Desktop release v2.11.10 2023-06-23 14:24:54 +01:00
Laurent Cozic
671fa542b4 Tools: Switch to @electron/notarize 2023-06-23 14:20:47 +01:00
pedr
4e4685b039 Tools: Creating a vscode configuration for debugging the server project (#8322) 2023-06-23 12:49:54 +01:00
renovate[bot]
e4023a349f Update dependency dayjs to v1.11.8 (#8355)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-22 15:28:00 +00:00
renovate[bot]
e7c37d17c1 Update dependency jquery to v3.7.0 (#8348)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-21 12:59:31 +01:00
Joplin Bot
d6c2b64a43 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-21 00:43:17 +00:00
Joplin Bot
10e49d8cd3 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-20 18:12:08 +00:00
Laurent Cozic
95d83a8ced Android 2.11.30 2023-06-20 17:51:05 +01:00
renovate[bot]
6b00c4e6ce Update dependency @react-native-community/datetimepicker to v7.1.0 (#8347)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-20 17:35:14 +01:00
Laurent Cozic
dc2def403c Tools: Only build one Android version 2023-06-20 16:14:48 +01:00
Laurent Cozic
89e2603f02 Android 2.11.29 2023-06-20 16:13:02 +01:00
Joplin Bot
35e03e2ad9 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-20 15:41:48 +01:00
Laurent Cozic
12e853b871 Doc: Update French translation 2023-06-20 15:41:39 +01:00
Laurent Cozic
5841596acb Android: Add support for Voice Typing for most languages (#8309) 2023-06-20 15:41:22 +01:00
Laurent Cozic
377e35b1af Mobile: Improved Vosk error handling 2023-06-20 15:33:53 +01:00
Laurent Cozic
a3181d4458 v28 2023-06-20 15:33:15 +01:00
Laurent Cozic
e057c7beea Tools: Only build one Android version 2023-06-20 15:31:58 +01:00
Laurent Cozic
60c25f88cf Chore: Server: Refactor error codes 2023-06-20 10:43:51 +01:00
Laurent Cozic
d532b9dffc Server v2.11.2 2023-06-20 09:41:19 +01:00
renovate[bot]
5a2310ff4d Update dependency react-redux to v8.0.7 (#8346)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-20 05:20:21 +00:00
Joplin Bot
472120d5ec Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-20 00:40:08 +00:00
Laurent Cozic
9de83bf922 Revert formidable update 2023-06-19 19:40:08 +01:00
Joplin Bot
96bd76e45e Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-19 18:15:15 +00:00
pedr
5564e2178a CI: Add a step to check if the Docker image is accepting HTTP requests (#8336) 2023-06-19 17:06:22 +01:00
renovate[bot]
9eb643f8f7 Update dependency formidable to v3 (#8334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-19 17:02:25 +01:00
renovate[bot]
c2bdaddcec Update dependency nodemailer to v6.9.3 (#8338)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-18 12:53:27 +00:00
Laurent Cozic
17888a2da0 Server: Improve performance and reliability when adding an item 2023-06-18 10:38:54 +01:00
renovate[bot]
eda18c3860 Update dependency formidable to v2.1.2 (#8333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 15:36:32 +01:00
Laurent Cozic
878bf13a0e Chore: Server: Convert migrations to arrow functions 2023-06-16 10:14:01 +01:00
Laurent Cozic
a8c21b2477 lock file 2023-06-16 10:03:47 +01:00
Laurent Cozic
b85bd8d296 Renovate ignore 2023-06-16 10:03:31 +01:00
Laurent Cozic
eb6d5690aa Renovate ignore 2023-06-16 09:26:16 +01:00
renovate[bot]
ebe00ea58b Update dependency react-redux to v8 (#8328)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 09:23:23 +01:00
renovate[bot]
e14a0b54f5 Update dependency styled-components to v5.3.11 (#8324)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 04:20:55 +00:00
renovate[bot]
2f0f63e2b1 Update dependency @react-native-community/geolocation to v3.0.6 (#8325)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 00:31:15 +00:00
Laurent Cozic
048100ebb3 renovate 2023-06-15 23:16:57 +01:00
Laurent Cozic
7880d07868 Renovate ignore 2023-06-15 23:16:56 +01:00
renovate[bot]
9ba42b7919 Update dependency domhandler to v5 (#8321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 22:06:26 +01:00
renovate[bot]
fcc7ed3ed4 Update dependency @react-native-community/geolocation to v3 (#8320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 21:38:29 +01:00
renovate[bot]
c000d4f4b3 Update dependency @koa/cors to v4 (#8319)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-15 21:37:11 +01:00
Laurent Cozic
6e0c484441 Doc: Fixed layout 2023-06-15 19:10:33 +01:00
Laurent Cozic
753a25a5e1 Fix tests 2023-06-14 17:45:13 +01:00
Laurent Cozic
57b4198d2c All: Security: Prevent XSS when passing specially encoded string to a link 2023-06-14 16:55:54 +01:00
Laurent Cozic
2078fa4e40 Fix tests 2023-06-14 15:59:20 +01:00
Laurent Cozic
69826610a2 Desktop: Security: Prevent calling arbitrary commands via x-callback-url 2023-06-14 15:51:35 +01:00
Laurent Cozic
0f9727144f Desktop: Fixes #8316: Include more Rich Text editor commands in the menu 2023-06-14 14:57:12 +01:00
Joplin Bot
11355bfdc5 Doc: Auto-update documentation
Auto-updated using release-website.sh
2023-06-13 18:14:50 +00:00
Laurent Cozic
4d08d7385f Tools: Do not lock pull requests after merging 2023-06-13 18:08:32 +01:00
Laurent Cozic
ce7da882ee Android: Add support for Voice Typing for most languages (#8309) 2023-06-13 18:06:54 +01:00
Laurent Cozic
92c68882e2 Desktop: Resolves #8080: Add support for plugin user data (#8312) 2023-06-13 18:06:16 +01:00
renovate[bot]
6424832984 Update dependency ts-loader to v9.4.3 (#8311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-12 20:47:12 +00:00
134 changed files with 1359 additions and 1208 deletions

View File

@@ -225,6 +225,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.j
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
@@ -356,6 +357,7 @@ packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
packages/app-desktop/tools/notarizeMacApp.js
packages/app-desktop/utils/markupLanguageUtils.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/components/ActionButton.js

View File

@@ -33,5 +33,5 @@ jobs:
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
lock-pullrequest-aftermerge: false
#use-dco-flag: true - If you are using DCO instead of CLA

View File

@@ -176,3 +176,35 @@ jobs:
# Basic test to ensure that the created build is valid. It should exit with
# code 0 if it works.
docker run joplin/server:0.0.0-beta node dist/app.js migrate list
- name: Check HTTP request
run: |
# Need to pass environment variables:
docker run -p 22300:22300 joplin/server:0.0.0-beta node dist/app.js --env dev &
# Wait for server to start
sleep 10
# Check if status code is correct
# if the actual_status DOES NOT include the expected_status
# it exits the process with code 1
expected_status="HTTP/1.1 200 OK"
actual_status=$(curl -I -X GET http://localhost:22300/api/ping | head -n 1)
if [[ ! "$actual_status" =~ "$expected_status" ]]; then
echo 'Failed while checking the status code after request to /api/ping'
echo 'expected: ' $expected_status
echo 'actual: ' $actual_status
exit 1;
fi
# Check if the body response is correct
# if the actual_body is different of expected_body exit with code 1
expected_body='{"status":"ok","message":"Joplin Server is running"}'
actual_body=$(curl http://localhost:22300/api/ping)
if [[ "$actual_body" != "$expected_body" ]]; then
echo 'Failed while checking the body response after request to /api/ping'
exit 1;
fi

2
.gitignore vendored
View File

@@ -210,6 +210,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.j
packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
@@ -341,6 +342,7 @@ packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.js
packages/app-desktop/services/sortOrder/notesSortOrderUtils.test.js
packages/app-desktop/services/spellChecker/SpellCheckerServiceDriverNative.js
packages/app-desktop/tools/notarizeMacApp.js
packages/app-desktop/utils/markupLanguageUtils.js
packages/app-mobile/PluginAssetsLoader.js
packages/app-mobile/components/ActionButton.js

View File

@@ -36,7 +36,7 @@ index 6afcbbf0cc8ca2d69dd78077d61e59a90b2136bb..9f8d72b4ec5b2b3d290975d6a255917c
def kotlin_version = getExtOrDefault('kotlinVersion')
diff --git a/android/src/main/java/com/reactnativevosk/VoskModule.kt b/android/src/main/java/com/reactnativevosk/VoskModule.kt
index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..f3da440bc2863a59db6d2d1691c54d8d4870cb3f 100644
index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..5a8539b9cce8951967640dba755e29a4e3ff404a 100644
--- a/android/src/main/java/com/reactnativevosk/VoskModule.kt
+++ b/android/src/main/java/com/reactnativevosk/VoskModule.kt
@@ -19,13 +19,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@@ -66,7 +66,25 @@ index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..f3da440bc2863a59db6d2d1691c54d8d
sendEvent("onResult", text)
}
}
@@ -153,6 +165,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@@ -93,12 +105,11 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
@ReactMethod
fun loadModel(path: String, promise: Promise) {
cleanModel();
- StorageService.unpack(context, path, "models",
- { model: Model? ->
- this.model = model
- promise.resolve("Model successfully loaded")
- }
- ) { e: IOException ->
+
+ try {
+ this.model = Model(path);
+ promise.resolve("Model successfully loaded")
+ } catch (e: IOException) {
this.model = null
promise.reject(e)
}
@@ -153,6 +164,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
cleanRecognizer();
}

View File

@@ -22,11 +22,11 @@ Three types of applications are available: for **desktop** (Windows, macOS and L
Operating System | Download
---|---
Windows (32 and 64-bit) | <a href='https://objects.joplinusercontent.com/v2.10.19/Joplin-Setup-2.10.19.exe?source=JoplinWebsite&type=New'><img alt='Get it on Windows' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeWindows.png'/></a>
macOS | <a href='https://objects.joplinusercontent.com/v2.10.19/Joplin-2.10.19.dmg?source=JoplinWebsite&type=New'><img alt='Get it on macOS' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeMacOS.png'/></a>
Linux | <a href='https://objects.joplinusercontent.com/v2.10.19/Joplin-2.10.19.AppImage?source=JoplinWebsite&type=New'><img alt='Get it on Linux' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeLinux.png'/></a>
Windows (32 and 64-bit) | <a href='https://objects.joplinusercontent.com/v2.11.11/Joplin-Setup-2.11.11.exe?source=JoplinWebsite&type=New'><img alt='Get it on Windows' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeWindows.png'/></a>
macOS | <a href='https://objects.joplinusercontent.com/v2.11.11/Joplin-2.11.11.dmg?source=JoplinWebsite&type=New'><img alt='Get it on macOS' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeMacOS.png'/></a>
Linux | <a href='https://objects.joplinusercontent.com/v2.11.11/Joplin-2.11.11.AppImage?source=JoplinWebsite&type=New'><img alt='Get it on Linux' width="134px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeLinux.png'/></a>
**On Windows**, you may also use the <a href='https://objects.joplinusercontent.com/v2.10.19/JoplinPortable.exe?source=JoplinWebsite&type=New'>Portable version</a>. The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
**On Windows**, you may also use the <a href='https://objects.joplinusercontent.com/v2.11.11/JoplinPortable.exe?source=JoplinWebsite&type=New'>Portable version</a>. The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
**On Linux**, the recommended way is to use the following installation script as it will handle the desktop icon too:

View File

@@ -353,5 +353,43 @@
"web/bundles/": true,
"web/env.php": true,
}
},
"tasks": {
"version": "2.0.0",
"tasks": [
{
"label": "transpile-server",
"type": "shell",
"command": "cd ${workspaceFolder}/packages/server && yarn tsc",
"group": "build",
}
]
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"name": "Server",
"type": "node",
"request": "launch",
"preLaunchTask": "transpile-server",
"skipFiles": [
"<node_internals>/**"
],
"outFiles": [
"${workspaceFolder}/**/*.js",
"!**/node_modules/**"
],
"program": "${workspaceFolder}/packages/server/src/app.ts",
"args": [
"--env", "dev"
],
"env": {
"JOPLIN_IS_TESTING": "1",
"TERMS_URL": "https://example.com",
"PRIVACY_URL": "https://example.com",
}
}
]
}
}

View File

@@ -16,7 +16,7 @@
"buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
"buildCommandIndex": "node packages/tools/gulp/tasks/buildCommandIndexRun.js",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../joplin-website/docs/api/references/plugin_api packages/lib/services/plugins/api/",
"buildPluginDoc": "cd packages/generate-plugin-doc && yarn run buildPluginDoc_",
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc",
"updateNews": "node ./packages/tools/website/updateNews",
"buildSettingJsonSchema": "yarn workspace joplin start settingschema ../../../joplin-website/docs/schema/settings.json",
@@ -84,7 +84,6 @@
"lint-staged": "13.2.2",
"madge": "6.0.0",
"npm-package-json-lint": "6.4.0",
"typedoc": "0.17.8",
"typescript": "5.0.2"
},
"dependencies": {

View File

@@ -35,15 +35,15 @@
],
"owner": "Laurent Cozic"
},
"version": "2.11.0",
"version": "2.12.0",
"bin": "./main.js",
"engines": {
"node": ">=10.0.0"
},
"dependencies": {
"@joplin/lib": "~2.11",
"@joplin/renderer": "~2.11",
"@joplin/utils": "~2.11",
"@joplin/lib": "~2.12",
"@joplin/renderer": "~2.12",
"@joplin/utils": "~2.12",
"aws-sdk": "2.1340.0",
"chalk": "4.1.2",
"compare-version": "0.1.2",
@@ -70,7 +70,7 @@
"yargs-parser": "21.1.1"
},
"devDependencies": {
"@joplin/tools": "~2.11",
"@joplin/tools": "~2.12",
"@types/fs-extra": "11.0.1",
"@types/jest": "29.5.1",
"@types/node": "18.15.13",

View File

@@ -0,0 +1 @@
<a data-from-md title='&lt;style&gt;' href='&amp;#x22&amp;#x2c&amp;#x22&amp;#x61&amp;#x22&amp;#x29&amp;#x3b&amp;#x74&amp;#x6f&amp;#x70&amp;#x2e&amp;#x72&amp;#x65&amp;#x71&amp;#x75&amp;#x69&amp;#x72&amp;#x65&amp;#x28&amp;#x27&amp;#x63&amp;#x68&amp;#x69&amp;#x6c&amp;#x64&amp;#x5f&amp;#x70&amp;#x72&amp;#x6f&amp;#x63&amp;#x65&amp;#x73&amp;#x73&amp;#x27&amp;#x29&amp;#x2e&amp;#x65&amp;#x78&amp;#x65&amp;#x63&amp;#x28&amp;#x27&amp;#x6f&amp;#x70&amp;#x65&amp;#x6e&amp;#x20&amp;#x2f&amp;#x53&amp;#x79&amp;#x73&amp;#x74&amp;#x65&amp;#x6d&amp;#x2f&amp;#x41&amp;#x70&amp;#x70&amp;#x6c&amp;#x69&amp;#x63&amp;#x61&amp;#x74&amp;#x69&amp;#x6f&amp;#x6e&amp;#x73&amp;#x2f&amp;#x43&amp;#x61&amp;#x6c&amp;#x63&amp;#x75&amp;#x6c&amp;#x61&amp;#x74&amp;#x6f&amp;#x72&amp;#x2e&amp;#x61&amp;#x70&amp;#x70&amp;#x27&amp;#x29&amp;#x3b&amp;#x2f&amp;#x2f' onclick='postMessage(&quot;&amp;#x22&amp;#x2c&amp;#x22&amp;#x61&amp;#x22&amp;#x29&amp;#x3b&amp;#x74&amp;#x6f&amp;#x70&amp;#x2e&amp;#x72&amp;#x65&amp;#x71&amp;#x75&amp;#x69&amp;#x72&amp;#x65&amp;#x28&amp;#x27&amp;#x63&amp;#x68&amp;#x69&amp;#x6c&amp;#x64&amp;#x5f&amp;#x70&amp;#x72&amp;#x6f&amp;#x63&amp;#x65&amp;#x73&amp;#x73&amp;#x27&amp;#x29&amp;#x2e&amp;#x65&amp;#x78&amp;#x65&amp;#x63&amp;#x28&amp;#x27&amp;#x6f&amp;#x70&amp;#x65&amp;#x6e&amp;#x20&amp;#x2f&amp;#x53&amp;#x79&amp;#x73&amp;#x74&amp;#x65&amp;#x6d&amp;#x2f&amp;#x41&amp;#x70&amp;#x70&amp;#x6c&amp;#x69&amp;#x63&amp;#x61&amp;#x74&amp;#x69&amp;#x6f&amp;#x6e&amp;#x73&amp;#x2f&amp;#x43&amp;#x61&amp;#x6c&amp;#x63&amp;#x75&amp;#x6c&amp;#x61&amp;#x74&amp;#x6f&amp;#x72&amp;#x2e&amp;#x61&amp;#x70&amp;#x70&amp;#x27&amp;#x29&amp;#x3b&amp;#x2f&amp;#x2f&quot;, { resourceId: &quot;&quot; }); return false;'>xxxxx</a>

View File

@@ -0,0 +1 @@
[xxxxx](&#x22&#x2c&#x22&#x61&#x22&#x29&#x3b&#x74&#x6f&#x70&#x2e&#x72&#x65&#x71&#x75&#x69&#x72&#x65&#x28&#x27&#x63&#x68&#x69&#x6c&#x64&#x5f&#x70&#x72&#x6f&#x63&#x65&#x73&#x73&#x27&#x29&#x2e&#x65&#x78&#x65&#x63&#x28&#x27&#x6f&#x70&#x65&#x6e&#x20&#x2f&#x53&#x79&#x73&#x74&#x65&#x6d&#x2f&#x41&#x70&#x70&#x6c&#x69&#x63&#x61&#x74&#x69&#x6f&#x6e&#x73&#x2f&#x43&#x61&#x6c&#x63&#x75&#x6c&#x61&#x74&#x6f&#x72&#x2e&#x61&#x70&#x70&#x27&#x29&#x3b&#x2f&#x2f '<style>')

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "2.11.2",
"version": "2.12.0",
"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'",

View File

@@ -34,7 +34,7 @@ import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog';
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import { isCallbackUrl, parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import ElectronAppWrapper from '../../ElectronAppWrapper';
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
@@ -173,6 +173,7 @@ class MainScreenComponent extends React.Component<Props, State> {
}
private openCallbackUrl(url: string) {
if (!isCallbackUrl(url)) throw new Error(`Invalid callback URL: ${url}`);
const { command, params } = parseCallbackUrl(url);
void CommandService.instance().execute(command.toString(), params.id);
}

View File

@@ -26,6 +26,7 @@ import { loadScript } from '../../../utils/loadScript';
import bridge from '../../../../services/bridge';
import { TinyMceEditorEvents } from './utils/types';
import type { Editor } from 'tinymce';
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@@ -78,23 +79,6 @@ function stripMarkup(markupLanguage: number, markup: string, options: any = null
return markupToHtml_.stripMarkup(markupLanguage, markup, options);
}
interface TinyMceCommand {
name: string;
value?: any;
ui?: boolean;
}
interface JoplinCommandToTinyMceCommands {
[key: string]: TinyMceCommand;
}
const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
'textBold': { name: 'mceToggleFormat', value: 'bold' },
'textItalic': { name: 'mceToggleFormat', value: 'italic' },
'textLink': { name: 'mceLink' },
'search': { name: 'SearchReplace' },
};
interface LastOnChangeEventInfo {
content: string;
resourceInfos: ResourceInfos;
@@ -274,11 +258,17 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
return false;
}
const tinyMceCmd: TinyMceCommand = { ...joplinCommandToTinyMceCommands[cmd.name] };
if (!('ui' in tinyMceCmd)) tinyMceCmd.ui = false;
if (!('value' in tinyMceCmd)) tinyMceCmd.value = null;
if (joplinCommandToTinyMceCommands[cmd.name] === true) {
// Already handled in useWindowCommandHandlers.ts
} else if (joplinCommandToTinyMceCommands[cmd.name] === false) {
// Explicitely not supported
} else {
const tinyMceCmd: TinyMceCommand = { ...(joplinCommandToTinyMceCommands[cmd.name] as TinyMceCommand) };
if (!('ui' in tinyMceCmd)) tinyMceCmd.ui = false;
if (!('value' in tinyMceCmd)) tinyMceCmd.value = null;
editor.execCommand(tinyMceCmd.name, tinyMceCmd.ui, tinyMceCmd.value);
editor.execCommand(tinyMceCmd.name, tinyMceCmd.ui, tinyMceCmd.value);
}
return true;
},
@@ -603,11 +593,15 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
joplinSup: { inline: 'sup', remove: 'all' },
},
setup: (editor: Editor) => {
editor.addCommand('joplinAttach', () => {
insertResourcesIntoContentRef.current();
});
editor.ui.registry.addButton('joplinAttach', {
tooltip: _('Attach file'),
icon: 'paperclip',
onAction: async function() {
insertResourcesIntoContentRef.current();
editor.execCommand('joplinAttach');
},
});
@@ -1112,7 +1106,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
function onPasteAsText() {
pasteAsPlainText(null);
// clipboard.readText returns Markdown instead of text when copying content from
// the Rich Text Editor. When the user "Paste as text" he does not expect to see
// anything besides text, that is why we are stripping here before pasting
// https://github.com/laurent22/joplin/pull/8351
const clipboardWithoutMarkdown = stripMarkup(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, clipboard.readText());
pasteAsPlainText(clipboardWithoutMarkdown);
}
editor.on(TinyMceEditorEvents.KeyUp, onKeyUp);

View File

@@ -0,0 +1,22 @@
export interface TinyMceCommand {
name: string;
value?: any;
ui?: boolean;
}
interface JoplinCommandToTinyMceCommands {
[key: string]: TinyMceCommand | boolean;
}
// If the mapping is simply `true` it means that the command is supported via
// useWindowCommandHandlers.ts. We still add it here to have the complete list
// of supported commands.
export const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
'textBold': { name: 'mceToggleFormat', value: 'bold' },
'textItalic': { name: 'mceToggleFormat', value: 'italic' },
'textCode': { name: 'mceToggleFormat', value: 'code' },
'textLink': { name: 'mceLink' },
'search': { name: 'SearchReplace' },
'attachFile': { name: 'joplinAttach' },
'insertDateTime': true,
};

View File

@@ -4,6 +4,7 @@ import editorCommandDeclarations from '../editorCommandDeclarations';
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from '@joplin/lib/services/CommandService';
import time from '@joplin/lib/time';
import { reg } from '@joplin/lib/registry';
import { joplinCommandToTinyMceCommands } from '../NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands';
const commandsWithDependencies = [
require('../commands/showLocalSearch'),
@@ -24,6 +25,8 @@ interface HookDependencies {
}
function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, setFormNote: Function): CommandRuntime {
const markdownEditorOnly = !Object.keys(joplinCommandToTinyMceCommands).includes(declaration.name);
return {
execute: async (_context: CommandContext, ...args: any[]) => {
if (!editorRef.current) {
@@ -65,7 +68,7 @@ function editorCommandRuntime(declaration: CommandDeclaration, editorRef: any, s
// currently selected text.
//
// https://github.com/laurent22/joplin/issues/5707
enabledCondition: '(!modalDialogVisible || gotoAnythingVisible) && markdownEditorPaneVisible && oneNoteSelected && noteIsMarkdown',
enabledCondition: `(!modalDialogVisible || gotoAnythingVisible) ${markdownEditorOnly ? '&& markdownEditorPaneVisible' : ''} && oneNoteSelected && noteIsMarkdown`,
};
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "2.11.9",
"version": "2.12.0",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,
@@ -108,7 +108,7 @@
},
"homepage": "https://github.com/laurent22/joplin#readme",
"devDependencies": {
"@joplin/tools": "~2.11",
"@joplin/tools": "~2.12",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.5.1",
"@types/node": "18.15.13",
@@ -117,7 +117,6 @@
"@types/styled-components": "5.1.26",
"electron": "19.1.4",
"electron-builder": "22.11.7",
"electron-notarize": "1.2.2",
"electron-rebuild": "3.2.9",
"glob": "8.1.0",
"gulp": "4.0.2",
@@ -134,11 +133,12 @@
"7zip-bin-win": "^2.1.1"
},
"dependencies": {
"@electron/remote": "2.0.9",
"@electron/notarize": "1.2.4",
"@electron/remote": "2.0.10",
"@fortawesome/fontawesome-free": "5.15.4",
"@joeattardi/emoji-button": "4.6.4",
"@joplin/lib": "~2.11",
"@joplin/renderer": "~2.11",
"@joplin/lib": "~2.12",
"@joplin/renderer": "~2.12",
"async-mutex": "0.4.0",
"codemirror": "5.65.9",
"color": "3.2.1",
@@ -162,7 +162,7 @@
"react": "18.2.0",
"react-datetime": "3.2.0",
"react-dom": "18.2.0",
"react-redux": "8.0.5",
"react-redux": "8.0.7",
"react-select": "5.7.3",
"react-toggle-button": "2.2.0",
"react-tooltip": "4.5.1",
@@ -171,7 +171,7 @@
"roboto-fontface": "0.10.0",
"smalltalk": "2.5.1",
"sqlite3": "5.1.6",
"styled-components": "5.3.10",
"styled-components": "5.3.11",
"styled-system": "5.1.5",
"taboverride": "4.0.3",
"tinymce": "5.10.6"

View File

@@ -1,14 +1,23 @@
const fs = require('fs');
const path = require('path');
const electron_notarize = require('electron-notarize');
import { existsSync } from 'fs';
import { join } from 'path';
import { notarize } from '@electron/notarize';
const execCommand = require('./execCommand');
function isDesktopAppTag(tagName) {
function isDesktopAppTag(tagName: string) {
if (!tagName) return false;
return tagName[0] === 'v';
}
module.exports = async function(params) {
interface Params {
appOutDir: string;
packager: {
appInfo: {
productFilename: string;
};
};
}
export default async (params: Params) => {
if (process.platform !== 'darwin') return;
console.info('Checking if notarization should be done...');
@@ -26,8 +35,8 @@ module.exports = async function(params) {
// Same appId in electron-builder.
const appId = 'net.cozic.joplin-desktop';
const appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
if (!fs.existsSync(appPath)) {
const appPath = join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
if (!existsSync(appPath)) {
throw new Error(`Cannot find application at: ${appPath}`);
}
@@ -40,7 +49,7 @@ module.exports = async function(params) {
}, 60000);
try {
await electron_notarize.notarize({
await notarize({
appBundleId: appId,
appPath: appPath,
@@ -58,7 +67,12 @@ module.exports = async function(params) {
// Use this to get it:
//
// xcrun altool --list-providers -u APPLE_ID -p APPLE_ID_PASSWORD
ascProvider: process.env.APPLE_ASC_PROVIDER,
// ascProvider: process.env.APPLE_ASC_PROVIDER,
// In our case, the team ID is the same as the legacy ASC_PROVIDER
teamId: process.env.APPLE_ASC_PROVIDER,
tool: 'notarytool',
});
} catch (error) {
console.error(error);

View File

@@ -110,8 +110,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097712
versionName "2.11.27"
versionCode 2097716
versionName "2.12.0"
// ndk {
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
// }

View File

@@ -1,7 +0,0 @@
French small model for Vosk
WER
%WER 23.95 [ 37203 / 155330, 5373 ins, 4427 del, 27403 sub ] exp/chain_a/tdnn/decode_test_cv/wer_12_0.0
%WER 19.30 [ 2975 / 15412, 683 ins, 672 del, 1620 sub ] exp/chain_a/tdnn/decode_test_mtedx/wer_10_0.0
%WER 27.25 [ 20208 / 74145, 2647 ins, 5852 del, 11709 sub ] exp/chain_a/tdnn/decode_test_podcast_reseg/wer_10_0.0

View File

@@ -1,8 +0,0 @@
--use-energy=false
--sample-frequency=16000
--num-mel-bins=40
--num-ceps=40
--low-freq=40
--high-freq=-200
--allow-upsample=true
--allow-downsample=true

View File

@@ -1,10 +0,0 @@
--min-active=200
--max-active=7000
--beam=13.0
--lattice-beam=4.0
--acoustic-scale=1.0
--frame-subsampling-factor=3
--endpoint.silence-phones=1:2:3:4:5:6:7:8:9:10
--endpoint.rule2.min-trailing-silence=0.5
--endpoint.rule3.min-trailing-silence=1.0
--endpoint.rule4.min-trailing-silence=2.0

View File

@@ -1,76 +0,0 @@
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440

View File

@@ -1,154 +0,0 @@
1 nonword
2 begin
3 end
4 internal
5 singleton
6 nonword
7 begin
8 end
9 internal
10 singleton
11 begin
12 end
13 internal
14 singleton
15 begin
16 end
17 internal
18 singleton
19 begin
20 end
21 internal
22 singleton
23 begin
24 end
25 internal
26 singleton
27 begin
28 end
29 internal
30 singleton
31 begin
32 end
33 internal
34 singleton
35 begin
36 end
37 internal
38 singleton
39 begin
40 end
41 internal
42 singleton
43 begin
44 end
45 internal
46 singleton
47 begin
48 end
49 internal
50 singleton
51 begin
52 end
53 internal
54 singleton
55 begin
56 end
57 internal
58 singleton
59 begin
60 end
61 internal
62 singleton
63 begin
64 end
65 internal
66 singleton
67 begin
68 end
69 internal
70 singleton
71 begin
72 end
73 internal
74 singleton
75 begin
76 end
77 internal
78 singleton
79 begin
80 end
81 internal
82 singleton
83 begin
84 end
85 internal
86 singleton
87 begin
88 end
89 internal
90 singleton
91 begin
92 end
93 internal
94 singleton
95 begin
96 end
97 internal
98 singleton
99 begin
100 end
101 internal
102 singleton
103 begin
104 end
105 internal
106 singleton
107 begin
108 end
109 internal
110 singleton
111 begin
112 end
113 internal
114 singleton
115 begin
116 end
117 internal
118 singleton
119 begin
120 end
121 internal
122 singleton
123 begin
124 end
125 internal
126 singleton
127 begin
128 end
129 internal
130 singleton
131 begin
132 end
133 internal
134 singleton
135 begin
136 end
137 internal
138 singleton
139 begin
140 end
141 internal
142 singleton
143 begin
144 end
145 internal
146 singleton
147 begin
148 end
149 internal
150 singleton
151 begin
152 end
153 internal
154 singleton

View File

@@ -1,3 +0,0 @@
[
1.022245e+11 -6.33291e+09 -2.480997e+09 8.290258e+09 -9.084483e+09 -8.092173e+09 -1.4735e+10 -7.041795e+09 -1.171205e+10 -2.976464e+08 -1.009425e+10 -6765179 -7.821326e+09 1.449499e+09 -6.413975e+09 -5.303802e+08 -4.998635e+09 9.521598e+07 -3.073041e+09 1.56756e+08 -1.287956e+09 1.738752e+08 -2.382392e+08 -2.716675e+07 4.404485e+08 -1.913359e+08 7.780919e+08 -4.006922e+08 7.895809e+08 -5.401082e+08 5.17605e+08 -6.227134e+08 6.58271e+08 -6.204593e+07 5.187754e+08 -4.497048e+08 4.219366e+07 -2.78742e+08 -1.797385e+07 -3.604475e+07 1.053647e+09
1.040194e+13 6.245521e+11 4.223293e+11 6.831219e+11 6.078478e+11 6.3425e+11 7.943839e+11 6.013323e+11 6.781652e+11 5.272091e+11 5.810814e+11 4.353831e+11 4.473305e+11 3.42063e+11 3.083377e+11 2.14257e+11 1.892057e+11 1.163827e+11 8.367058e+10 4.203224e+10 2.297476e+10 7.596307e+09 1.099877e+09 2.886651e+08 3.797438e+09 9.372847e+09 1.629059e+10 2.196351e+10 2.747149e+10 3.072878e+10 3.238528e+10 3.330232e+10 3.407238e+10 3.230687e+10 2.676914e+10 2.252055e+10 1.914305e+10 1.565974e+10 1.224627e+10 8.415393e+09 0 ]

View File

@@ -1,2 +0,0 @@
--left-context=3
--right-context=3

View File

@@ -1 +0,0 @@
1b7180e6-e500-4818-adc8-a41fe97a84ce

View File

@@ -46,6 +46,7 @@ import { NoteEntity } from '@joplin/lib/services/database/types';
import Logger from '@joplin/lib/Logger';
import VoiceTypingDialog from '../voiceTyping/VoiceTypingDialog';
import { voskEnabled } from '../../services/voiceTyping/vosk';
import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android';
const urlUtils = require('@joplin/lib/urlUtils');
// import Vosk from 'react-native-vosk';
@@ -1000,7 +1001,7 @@ class NoteScreenComponent extends BaseScreenComponent {
});
// Voice typing is enabled only for French language and on Android for now
if (voskEnabled && shim.mobilePlatform() === 'android' && currentLocale() === 'fr_FR') {
if (voskEnabled && shim.mobilePlatform() === 'android' && isSupportedLanguage(currentLocale())) {
output.push({
title: _('Voice typing...'),
onPress: () => {
@@ -1307,7 +1308,7 @@ class NoteScreenComponent extends BaseScreenComponent {
const renderVoiceTypingDialog = () => {
if (!this.state.voiceTypingDialogShown) return null;
return <VoiceTypingDialog onText={this.voiceTypingDialog_onText} onDismiss={this.voiceTypingDialog_onDismiss}/>;
return <VoiceTypingDialog locale={currentLocale()} onText={this.voiceTypingDialog_onText} onDismiss={this.voiceTypingDialog_onDismiss}/>;
};
return (

View File

@@ -1,12 +1,14 @@
import * as React from 'react';
import { useState, useEffect, useCallback } from 'react';
import { Banner, ActivityIndicator } from 'react-native-paper';
import { _ } from '@joplin/lib/locale';
import { Banner, ActivityIndicator, Modal } from 'react-native-paper';
import { _, languageName } from '@joplin/lib/locale';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import { getVosk, Recorder, startRecording, Vosk } from '../../services/voiceTyping/vosk';
import { IconSource } from 'react-native-paper/lib/typescript/src/components/Icon';
import { modelIsDownloaded } from '../../services/voiceTyping/vosk.android';
interface Props {
locale: string;
onDismiss: ()=> void;
onText: (text: string)=> void;
}
@@ -16,30 +18,39 @@ enum RecorderState {
Recording = 2,
Processing = 3,
Error = 4,
Downloading = 5,
}
const useVosk = (): [Error | null, Vosk|null] => {
const useVosk = (locale: string): [Error | null, boolean, Vosk|null] => {
const [vosk, setVosk] = useState<Vosk>(null);
const [error, setError] = useState<Error>(null);
const [mustDownloadModel, setMustDownloadModel] = useState<boolean | null>(null);
useAsyncEffect(async (event: AsyncEffectEvent) => {
if (mustDownloadModel === null) return;
try {
const v = await getVosk();
const v = await getVosk(locale);
if (event.cancelled) return;
setVosk(v);
} catch (error) {
setError(error);
} finally {
setMustDownloadModel(false);
}
}, []);
}, [locale, mustDownloadModel]);
return [error, vosk];
useAsyncEffect(async (_event: AsyncEffectEvent) => {
setMustDownloadModel(!(await modelIsDownloaded(locale)));
}, [locale]);
return [error, mustDownloadModel, vosk];
};
export default (props: Props) => {
const [recorder, setRecorder] = useState<Recorder>(null);
const [recorderState, setRecorderState] = useState<RecorderState>(RecorderState.Loading);
const [voskError, vosk] = useVosk();
const [voskError, mustDownloadModel, vosk] = useVosk(props.locale);
useEffect(() => {
if (voskError) {
@@ -49,6 +60,12 @@ export default (props: Props) => {
}
}, [vosk, voskError]);
useEffect(() => {
if (mustDownloadModel) {
setRecorderState(RecorderState.Downloading);
}
}, [mustDownloadModel]);
useEffect(() => {
if (recorderState === RecorderState.Recording) {
setRecorder(startRecording(vosk, {
@@ -60,7 +77,7 @@ export default (props: Props) => {
}, [recorderState, vosk, props.onText]);
const onDismiss = useCallback(() => {
recorder.cleanup();
if (recorder) recorder.cleanup();
props.onDismiss();
}, [recorder, props.onDismiss]);
@@ -69,6 +86,7 @@ export default (props: Props) => {
[RecorderState.Loading]: () => _('Loading...'),
[RecorderState.Recording]: () => _('Please record your voice...'),
[RecorderState.Processing]: () => _('Converting speech to text...'),
[RecorderState.Downloading]: () => _('Downloading %s language files...', languageName(props.locale)),
[RecorderState.Error]: () => _('Error: %s', voskError.message),
};
@@ -80,6 +98,7 @@ export default (props: Props) => {
[RecorderState.Loading]: ({ size }: { size: number }) => <ActivityIndicator animating={true} style={{ width: size, height: size }} />,
[RecorderState.Recording]: 'microphone',
[RecorderState.Processing]: 'microphone',
[RecorderState.Downloading]: ({ size }: { size: number }) => <ActivityIndicator animating={true} style={{ width: size, height: size }} />,
[RecorderState.Error]: 'alert-circle-outline',
};
@@ -87,16 +106,18 @@ export default (props: Props) => {
};
return (
<Banner
visible={true}
icon={renderIcon()}
actions={[
{
label: _('Done'),
onPress: onDismiss,
},
]}>
{`${_('Voice typing...')}\n${renderContent()}`}
</Banner>
<Modal visible={true} style={{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }}>
<Banner
visible={true}
icon={renderIcon()}
actions={[
{
label: _('Done'),
onPress: onDismiss,
},
]}>
{`${_('Voice typing...')}\n${renderContent()}`}
</Banner>
</Modal>
);
};

View File

@@ -517,13 +517,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 94;
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 12.11.3;
MARKETING_VERSION = 12.12.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -546,12 +546,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 94;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 12.11.3;
MARKETING_VERSION = 12.12.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -698,14 +698,14 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 94;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 12.11.3;
MARKETING_VERSION = 12.12.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
@@ -729,14 +729,14 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 94;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = A9BXAFS6CT;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = ShareExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 12.11.3;
MARKETING_VERSION = 12.12.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -333,7 +333,7 @@ PODS:
- React-jsinspector (0.71.10)
- React-logger (0.71.10):
- glog
- react-native-alarm-notification (2.11.0):
- react-native-alarm-notification (2.12.0):
- React
- react-native-camera (4.2.1):
- React-Core
@@ -347,9 +347,9 @@ PODS:
- React-Core
- react-native-fingerprint-scanner (6.0.0):
- React
- react-native-geolocation (2.1.0):
- react-native-geolocation (3.0.6):
- React-Core
- react-native-get-random-values (1.8.0):
- react-native-get-random-values (1.9.0):
- React-Core
- react-native-image-picker (5.3.1):
- React-Core
@@ -359,7 +359,7 @@ PODS:
- React-Core
- react-native-rsa-native (2.0.5):
- React
- react-native-saf-x (2.11.0):
- react-native-saf-x (2.12.0):
- React-Core
- react-native-safe-area-context (4.5.3):
- RCT-Folly
@@ -465,7 +465,7 @@ PODS:
- React-Core
- RNCPushNotificationIOS (1.11.0):
- React-Core
- RNDateTimePicker (7.0.1):
- RNDateTimePicker (7.1.0):
- React-Core
- RNExitApp (1.1.0):
- React
@@ -483,7 +483,15 @@ PODS:
- React-Core
- RNVectorIcons (9.2.0):
- React-Core
- RNZipArchive (6.0.9):
- React-Core
- RNZipArchive/Core (= 6.0.9)
- SSZipArchive (~> 2.2)
- RNZipArchive/Core (6.0.9):
- React-Core
- SSZipArchive (~> 2.2)
- SocketRocket (0.6.0)
- SSZipArchive (2.4.3)
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
@@ -577,6 +585,7 @@ DEPENDENCIES:
- RNSecureRandom (from `../node_modules/react-native-securerandom`)
- RNShare (from `../node_modules/react-native-share`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- RNZipArchive (from `../node_modules/react-native-zip-archive`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@@ -595,6 +604,7 @@ SPEC REPOS:
- libevent
- OpenSSL-Universal
- SocketRocket
- SSZipArchive
- YogaKit
EXTERNAL SOURCES:
@@ -724,6 +734,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-share"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
RNZipArchive:
:path: "../node_modules/react-native-zip-archive"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
@@ -763,17 +775,17 @@ SPEC CHECKSUMS:
React-jsiexecutor: 4bb480a183a354e4dbfb1012936b1a2bb9357de7
React-jsinspector: cdc854f8b13abd202afa54bc12578e5afb9cfae1
React-logger: ef2269b3afa6ba868da90496c3e17a4ec4f4cee0
react-native-alarm-notification: 26527410a6162d07a9dc57f4bbc62e94ff48e65d
react-native-alarm-notification: 0732f97be04975a23ba60e675bdb961a0aaf6aa6
react-native-camera: 3eae183c1d111103963f3dd913b65d01aef8110f
react-native-document-picker: 69ca2094d8780cfc1e7e613894d15290fdc54bba
react-native-fingerprint-scanner: ac6656f18c8e45a7459302b84da41a44ad96dbbe
react-native-geolocation: 69f4fd37650b8e7fee91816d395e62dd16f5ab8d
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
react-native-geolocation: 0f7fe8a4c2de477e278b0365cce27d089a8c5903
react-native-get-random-values: dee677497c6a740b71e5612e8dbd83e7539ed5bb
react-native-image-picker: ec9b713e248760bfa0f879f0715391de4651a7cb
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
react-native-netinfo: ccbe1085dffd16592791d550189772e13bf479e2
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
react-native-saf-x: 9bd5238d3b43d76bbec64aa82c173ac20a4bce9f
react-native-saf-x: 129cd2ddf120a1f6164c724b2846d172666b33de
react-native-safe-area-context: b8979f5eda6ed5903d4dbc885be3846ea3daa753
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
@@ -795,7 +807,7 @@ SPEC CHECKSUMS:
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNCPushNotificationIOS: 64218f3c776c03d7408284a819b2abfda1834bc8
RNDateTimePicker: e073697ac3e8a378968d68ab0581fef542b8af8a
RNDateTimePicker: 7ecd54a97fc3749f38c3c89a171f6cbd52f3c142
RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
@@ -804,7 +816,9 @@ SPEC CHECKSUMS:
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
RNZipArchive: 68a0c6db4b1c103f846f1559622050df254a3ade
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
Yoga: e7ea9e590e27460d28911403b894722354d73479
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

View File

@@ -2,7 +2,7 @@
"name": "@joplin/app-mobile",
"description": "Joplin for Mobile",
"license": "AGPL-3.0-or-later",
"version": "2.11.0",
"version": "2.12.0",
"private": true,
"scripts": {
"start": "BROWSERSLIST_IGNORE_OLD_DATA=true react-native start --reset-cache",
@@ -18,13 +18,13 @@
"postinstall": "jetify && yarn run build"
},
"dependencies": {
"@joplin/lib": "~2.11",
"@joplin/react-native-alarm-notification": "~2.11",
"@joplin/react-native-saf-x": "~2.11",
"@joplin/renderer": "~2.11",
"@joplin/lib": "~2.12",
"@joplin/react-native-alarm-notification": "~2.12",
"@joplin/react-native-saf-x": "~2.12",
"@joplin/renderer": "~2.12",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/datetimepicker": "7.0.1",
"@react-native-community/geolocation": "2.1.0",
"@react-native-community/datetimepicker": "7.1.0",
"@react-native-community/geolocation": "3.0.6",
"@react-native-community/netinfo": "9.3.10",
"@react-native-community/push-notification-ios": "1.11.0",
"@react-native-community/slider": "4.4.2",
@@ -50,11 +50,11 @@
"react-native-file-viewer": "2.1.5",
"react-native-fingerprint-scanner": "6.0.0",
"react-native-fs": "2.20.0",
"react-native-get-random-values": "1.8.0",
"react-native-get-random-values": "1.9.0",
"react-native-image-picker": "5.3.1",
"react-native-image-resizer": "1.4.5",
"react-native-localize": "3.0.0",
"react-native-modal-datetime-picker": "14.0.1",
"react-native-modal-datetime-picker": "15.0.1",
"react-native-paper": "5.8.0",
"react-native-popup-menu": "0.16.1",
"react-native-quick-actions": "0.3.13",
@@ -69,7 +69,8 @@
"react-native-version-info": "1.1.1",
"react-native-vosk": "0.1.12",
"react-native-webview": "12.4.0",
"react-redux": "7.2.9",
"react-native-zip-archive": "6.0.9",
"react-redux": "8.0.7",
"redux": "4.2.1",
"rn-fetch-blob": "0.12.0",
"stream": "0.0.2",
@@ -95,7 +96,7 @@
"@codemirror/search": "6.3.0",
"@codemirror/state": "6.2.0",
"@codemirror/view": "6.9.3",
"@joplin/tools": "~2.11",
"@joplin/tools": "~2.12",
"@lezer/highlight": "1.1.4",
"@tsconfig/react-native": "2.0.2",
"@types/fs-extra": "11.0.1",
@@ -116,7 +117,7 @@
"metro-react-native-babel-preset": "0.73.9",
"nodemon": "2.0.22",
"ts-jest": "29.1.0",
"ts-loader": "9.4.2",
"ts-loader": "9.4.3",
"ts-node": "10.9.1",
"typescript": "5.0.2",
"uglify-js": "3.17.4",

View File

@@ -1,17 +1,25 @@
import { languageCodeOnly } from '@joplin/lib/locale';
import Logger from '@joplin/lib/Logger';
import shim from '@joplin/lib/shim';
import Vosk from 'react-native-vosk';
import { unzip } from 'react-native-zip-archive';
import RNFetchBlob from 'rn-fetch-blob';
const md5 = require('md5');
const logger = Logger.create('voiceTyping/vosk');
enum State {
Idle = 0,
Recording,
Completing,
}
interface StartOptions {
onResult: (text: string)=> void;
}
let vosk_: Vosk|null = null;
let vosk_: Record<string, Vosk> = {};
let state_: State = State.Idle;
export const voskEnabled = true;
@@ -23,12 +31,113 @@ export interface Recorder {
cleanup: ()=> void;
}
export const getVosk = async () => {
if (vosk_) return vosk_;
vosk_ = new Vosk();
const result = await vosk_.loadModel('model-fr-fr');
const supportedLanguages = {
'en': 'https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip',
'cn': 'https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip',
'ru': 'https://alphacephei.com/vosk/models/vosk-model-small-ru-0.22.zip',
'fr': 'https://alphacephei.com/vosk/models/vosk-model-small-fr-0.22.zip',
'de': 'https://alphacephei.com/vosk/models/vosk-model-small-de-0.15.zip',
'es': 'https://alphacephei.com/vosk/models/vosk-model-small-es-0.42.zip',
'pt': 'https://alphacephei.com/vosk/models/vosk-model-small-pt-0.3.zip',
'tr': 'https://alphacephei.com/vosk/models/vosk-model-small-tr-0.3.zip',
'vn': 'https://alphacephei.com/vosk/models/vosk-model-small-vn-0.4.zip',
'it': 'https://alphacephei.com/vosk/models/vosk-model-small-it-0.22.zip',
'nl': 'https://alphacephei.com/vosk/models/vosk-model-small-nl-0.22.zip',
'uk': 'https://alphacephei.com/vosk/models/vosk-model-small-uk-v3-small.zip',
'ja': 'https://alphacephei.com/vosk/models/vosk-model-small-ja-0.22.zip',
'hi': 'https://alphacephei.com/vosk/models/vosk-model-small-hi-0.22.zip',
'cs': 'https://alphacephei.com/vosk/models/vosk-model-small-cs-0.4-rhasspy.zip',
'pl': 'https://alphacephei.com/vosk/models/vosk-model-small-pl-0.22.zip',
'uz': 'https://alphacephei.com/vosk/models/vosk-model-small-uz-0.22.zip',
'ko': 'https://alphacephei.com/vosk/models/vosk-model-small-ko-0.22.zip',
};
export const isSupportedLanguage = (locale: string) => {
const l = languageCodeOnly(locale).toLowerCase();
return Object.keys(supportedLanguages).includes(l);
};
// Where all the models files for all the languages are
const getModelRootDir = () => {
return `${RNFetchBlob.fs.dirs.DocumentDir}/vosk-models`;
};
// Where we unzip a model after downloading it
const getUnzipDir = (locale: string) => {
return `${getModelRootDir()}/${locale}`;
};
// Where the model for a particular language is
const getModelDir = (locale: string) => {
return `${getUnzipDir(locale)}/model`;
};
const languageModelUrl = (locale: string) => {
const l = languageCodeOnly(locale).toLowerCase();
if (!(l in supportedLanguages)) throw new Error(`No language file for: ${locale}`);
return (supportedLanguages as any)[l];
};
export const modelIsDownloaded = async (locale: string) => {
const uuidFile = `${getModelDir(locale)}/uuid`;
return shim.fsDriver().exists(uuidFile);
};
export const getVosk = async (locale: string) => {
if (vosk_[locale]) return vosk_[locale];
const vosk = new Vosk();
const modelDir = await downloadModel(locale);
logger.info(`Loading model from ${modelDir}`);
await shim.fsDriver().readDirStats(modelDir);
const result = await vosk.loadModel(modelDir);
logger.info('getVosk:', result);
return vosk_;
vosk_ = { [locale]: vosk };
return vosk;
};
const downloadModel = async (locale: string) => {
const modelUrl = languageModelUrl(locale);
const unzipDir = getUnzipDir(locale);
const zipFilePath = `${unzipDir}.zip`;
const modelDir = getModelDir(locale);
const uuidFile = `${modelDir}/uuid`;
if (await modelIsDownloaded(locale)) {
logger.info(`Model for ${locale} already exists at ${modelDir}`);
return modelDir;
}
await shim.fsDriver().remove(unzipDir);
logger.info(`Downloading model from: ${modelUrl}`);
await shim.fetchBlob(languageModelUrl(locale), {
path: zipFilePath,
});
logger.info(`Unzipping ${zipFilePath} => ${unzipDir}`);
await unzip(zipFilePath, unzipDir);
const dirs = await shim.fsDriver().readDirStats(unzipDir);
if (dirs.length !== 1) {
logger.error('Expected 1 directory but got', dirs);
throw new Error(`Expected 1 directory, but got ${dirs.length}`);
}
const fullUnzipPath = `${unzipDir}/${dirs[0].path}`;
logger.info(`Moving ${fullUnzipPath} => ${modelDir}`);
await shim.fsDriver().rename(fullUnzipPath, modelDir);
await shim.fsDriver().writeFile(uuidFile, md5(modelUrl));
await shim.fsDriver().remove(zipFilePath);
return modelDir;
};
export const startRecording = (vosk: Vosk, options: StartOptions): Recorder => {
@@ -110,8 +219,9 @@ export const startRecording = (vosk: Vosk, options: StartOptions): Recorder => {
});
},
cleanup: () => {
if (state_ !== State.Idle) {
if (state_ === State.Recording) {
logger.info('Cancelling...');
state_ = State.Completing;
vosk.stopOnly();
completeRecording('', null);
}

View File

@@ -13,7 +13,15 @@ export interface Recorder {
cleanup: ()=> void;
}
export const getVosk = async () => {
export const isSupportedLanguage = (_locale: string) => {
return false;
};
export const modelIsDownloaded = async (_locale: string) => {
return false;
};
export const getVosk = async (_locale: string) => {
return {} as any;
};

View File

@@ -116,6 +116,13 @@ export default class FsDriverRN extends FsDriverBase {
return RNFS.moveFile(source, dest);
}
public async rename(source: string, dest: string) {
if (isScopedUri(source) || isScopedUri(dest)) {
await RNSAF.rename(source, dest);
}
return RNFS.moveFile(source, dest);
}
public async exists(path: string) {
if (isScopedUri(path)) {
return RNSAF.exists(path);

View File

@@ -1,7 +1,7 @@
{
"name": "@joplin/fork-htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser",
"version": "4.1.43",
"version": "4.1.44",
"author": "Felix Boehm <me@feedic.com>",
"publishConfig": {
"access": "public"
@@ -40,7 +40,7 @@
},
"dependencies": {
"domelementtype": "2.3.0",
"domhandler": "3.3.0",
"domhandler": "5.0.3",
"domutils": "3.1.0",
"entities": "2.2.0"
},

View File

@@ -2,7 +2,7 @@
"name": "@joplin/fork-sax",
"description": "An evented streaming XML parser in JavaScript",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"version": "1.2.47",
"version": "1.2.48",
"main": "lib/sax.js",
"publishConfig": {
"access": "public"
@@ -16,7 +16,7 @@
],
"devDependencies": {
"standard": "17.0.0",
"tap": "16.3.4"
"tap": "16.3.6"
},
"gitHead": "eb4b0e64eab40a51b0895d3a40a9d8c3cb7b1b14"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-uslug",
"version": "1.0.8",
"version": "1.0.9",
"description": "A permissive slug generator that works with unicode.",
"author": "Jeremy Selier <jerem.selier@gmail.com>",
"publishConfig": {

View File

@@ -0,0 +1,3 @@
# generate-plugin-doc
This package is only to run the buildPluginDoc command. The version of typedoc we use needs an old version of typescript, so we cannot put the whole thing at the root, where it used to be.

View File

@@ -0,0 +1,11 @@
{
"name": "generate-plugin-doc",
"packageManager": "yarn@3.3.1",
"scripts": {
"buildPluginDoc_": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme '../../Assets/PluginDocTheme/' --readme '../../Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../../../joplin-website/docs/api/references/plugin_api ../lib/services/plugins/api/"
},
"dependencies": {
"typedoc": "0.17.8",
"typescript": "4.7.4"
}
}

View File

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

View File

@@ -12,7 +12,12 @@ module.exports = class extends Generator {
this.option('silent');
this.option('update');
this.option('nodePackageManager', 'npm');
// This appears to be deprecated and undocumented
// Maybe need this instead?
// https://github.com/yeoman/generator/issues/1294#issuecomment-841668595
// this.option('nodePackageManager', 'npm');
if (this.options.update) {
// When updating, overwrite files without prompting

View File

@@ -14,7 +14,7 @@ npm install -g generator-joplin
Then generate your new project:
```bash
yo joplin
yo --node-package-manager npm joplin
```
## Development
@@ -39,6 +39,10 @@ To build the plugin, simply run `npm run dist`.
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
## Updating the manifest version number
You can run `npm run updateVersion` to bump the patch part of the version number, so for example 1.0.3 will become 1.0.4. This script will update both the package.json and manifest.json version numbers so as to keep them in sync.
## Publishing the plugin
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
@@ -67,6 +71,13 @@ By default, the compiler (webpack) is going to compile `src/index.ts` only (as w
To get such an external script file to compile, you need to add it to the `extraScripts` array in `plugin.config.json`. The path you add should be relative to /src. For example, if you have a file in "/src/webviews/index.ts", the path should be set to "webviews/index.ts". Once compiled, the file will always be named with a .js extension. So you will get "webviews/index.js" in the plugin package, and that's the path you should use to reference the file.
## More information
- [Joplin Plugin API](https://joplinapp.org/api/references/plugin_api/classes/joplin.html)
- [Joplin Data API](https://joplinapp.org/api/references/rest_api/)
- [Joplin Plugin Manifest](https://joplinapp.org/api/references/plugin_manifest/)
- Ask for help on the [forum](https://discourse.joplinapp.org/) or our [Discord channel](https://discord.gg/VSj7AFHvpq)
## License
MIT © Laurent Cozic

View File

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

View File

@@ -4,7 +4,8 @@
"scripts": {
"dist": "webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && webpack --env joplin-plugin-config=createArchive",
"prepare": "npm run dist",
"update": "npm install -g generator-joplin && yo joplin --update"
"updateVersion": "webpack --env joplin-plugin-config=updateVersion",
"update": "npm install -g generator-joplin && yo joplin --node-package-manager npm --update"
},
"license": "MIT",
"keywords": [

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"id": "<%= pluginId %>",
"app_min_version": "2.11",
"app_min_version": "2.12",
"version": "1.0.0",
"name": "<%= pluginName %>",
"description": "<%= pluginDescription %>",

View File

@@ -44,8 +44,12 @@ for (const moduleName of builtinModules) {
moduleFallback[moduleName] = false;
}
const getPackageJson = () => {
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
};
function validatePackageJson() {
const content = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const content = getPackageJson();
if (!content.name || content.name.indexOf('joplin-plugin-') !== 0) {
console.warn(chalk.yellow(`WARNING: To publish the plugin, the package name should start with "joplin-plugin-" (found "${content.name}") in ${packageJsonPath}`));
}
@@ -102,7 +106,6 @@ function validateScreenshots(screenshots) {
});
}
function readManifest(manifestPath) {
const content = fs.readFileSync(manifestPath, 'utf8');
const output = JSON.parse(content);
@@ -133,12 +136,16 @@ function createPluginArchive(sourceDir, destPath) {
console.info(chalk.cyan(`Plugin archive has been created in ${destPath}`));
}
const writeManifest = (manifestPath, content) => {
fs.writeFileSync(manifestPath, JSON.stringify(content, null, '\t'), 'utf8');
};
function createPluginInfo(manifestPath, destPath, jplFilePath) {
const contentText = fs.readFileSync(manifestPath, 'utf8');
const content = JSON.parse(contentText);
content._publish_hash = `sha256:${fileSha256(jplFilePath)}`;
content._publish_commit = currentGitInfo();
fs.writeFileSync(destPath, JSON.stringify(content, null, '\t'), 'utf8');
writeManifest(destPath, content);
}
function onBuildCompleted() {
@@ -262,6 +269,32 @@ function buildExtraScriptConfigs(userConfig) {
return output;
}
const increaseVersion = version => {
try {
const s = version.split('.');
const d = Number(s[s.length - 1]) + 1;
s[s.length - 1] = `${d}`;
return s.join('.');
} catch (error) {
error.message = `Could not parse version number: ${version}: ${error.message}`;
throw error;
}
};
const updateVersion = () => {
const packageJson = getPackageJson();
packageJson.version = increaseVersion(packageJson.version);
fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
const manifest = readManifest(manifestPath);
manifest.version = increaseVersion(manifest.version);
writeManifest(manifestPath, manifest);
if (packageJson.version !== manifest.version) {
console.warn(chalk.yellow(`Version numbers have been updated but they do not match: package.json (${packageJson.version}), manifest.json (${manifest.version}). Set them to the required values to get them in sync.`));
}
};
function main(environ) {
const configName = environ['joplin-plugin-config'];
if (!configName) throw new Error('A config file must be specified via the --joplin-plugin-config flag');
@@ -298,6 +331,11 @@ function main(environ) {
fs.mkdirpSync(publishDir);
}
if (configName === 'updateVersion') {
updateVersion();
return [];
}
return configs[configName];
}

View File

@@ -1,6 +1,6 @@
{
"name": "generator-joplin",
"version": "2.11.0",
"version": "2.12.1",
"description": "Scaffolds out a new Joplin plugin",
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
"author": {
@@ -34,4 +34,4 @@
"repository": "https://github.com/laurent22/generator-joplin",
"license": "AGPL-3.0-or-later",
"private": true
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/htmlpack",
"version": "2.11.0",
"version": "2.12.0",
"description": "Pack an HTML file and all its linked resources into a single HTML file",
"main": "dist/index.js",
"types": "src/index.ts",
@@ -14,7 +14,7 @@
"author": "Laurent Cozic",
"license": "MIT",
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.43",
"@joplin/fork-htmlparser2": "^4.1.44",
"css": "3.0.0",
"datauri": "4.1.0",
"fs-extra": "11.1.1",

View File

@@ -3,13 +3,14 @@ import * as callbackUrlUtils from './callbackUrlUtils';
describe('callbackUrlUtils', () => {
it('should identify valid callback urls', () => {
const url = 'joplin://x-callback-url/123?a=b';
const url = 'joplin://x-callback-url/openFolder?a=b';
expect(callbackUrlUtils.isCallbackUrl(url)).toBe(true);
});
it('should identify invalid callback urls', () => {
expect(callbackUrlUtils.isCallbackUrl('not-joplin://x-callback-url/123?a=b')).toBe(false);
expect(callbackUrlUtils.isCallbackUrl('joplin://xcallbackurl/123?a=b')).toBe(false);
expect(callbackUrlUtils.isCallbackUrl('joplin://x-callback-url/invalidCommand?a=b')).toBe(false);
});
it('should build valid note callback urls', () => {

View File

@@ -1,7 +1,9 @@
const URL = require('url-parse');
export function isCallbackUrl(s: string) {
return s.startsWith('joplin://x-callback-url/');
return s.startsWith('joplin://x-callback-url/openNote?') ||
s.startsWith('joplin://x-callback-url/openFolder?') ||
s.startsWith('joplin://x-callback-url/openTag?');
}
export function getNoteCallbackUrl(noteId: string) {

View File

@@ -508,7 +508,8 @@ function languageNameInEnglish(languageCode: string) {
return codeToLanguageE_[languageCode] ? codeToLanguageE_[languageCode] : '';
}
function languageName(languageCode: string, defaultToEnglish: boolean = true) {
function languageName(canonicalName: string, defaultToEnglish: boolean = true) {
const languageCode = languageCodeOnly(canonicalName);
if (codeToLanguage_[languageCode]) return codeToLanguage_[languageCode];
if (defaultToEnglish) return languageNameInEnglish(languageCode);
return '';
@@ -603,4 +604,4 @@ const stringByLocale = (locale: string, s: string, ...args: any[]): string => {
}
};
export { _, _n, supportedLocales, currentLocale, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
export { _, _n, supportedLocales, languageName, currentLocale, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "2.11.0",
"version": "2.12.0",
"description": "Joplin Core library",
"author": "Laurent Cozic",
"homepage": "",
@@ -31,14 +31,14 @@
"dependencies": {
"@aws-sdk/client-s3": "3.296.0",
"@aws-sdk/s3-request-presigner": "3.296.0",
"@joplin/fork-htmlparser2": "^4.1.43",
"@joplin/fork-sax": "^1.2.47",
"@joplin/fork-uslug": "^1.0.8",
"@joplin/htmlpack": "~2.11",
"@joplin/renderer": "~2.11",
"@joplin/turndown": "^4.0.65",
"@joplin/turndown-plugin-gfm": "^1.0.47",
"@joplin/utils": "~2.11",
"@joplin/fork-htmlparser2": "^4.1.44",
"@joplin/fork-sax": "^1.2.48",
"@joplin/fork-uslug": "^1.0.9",
"@joplin/htmlpack": "~2.12",
"@joplin/renderer": "~2.12",
"@joplin/turndown": "^4.0.66",
"@joplin/turndown-plugin-gfm": "^1.0.48",
"@joplin/utils": "~2.12",
"@types/nanoid": "3.0.0",
"async-mutex": "0.4.0",
"base-64": "1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/pdf-viewer",
"version": "2.11.0",
"version": "2.12.0",
"description": "Provides embedded PDF viewers for Joplin",
"main": "dist/main.js",
"types": "src/main.ts",
@@ -30,7 +30,7 @@
"jest-environment-jsdom": "29.5.0",
"style-loader": "3.3.3",
"ts-jest": "29.1.0",
"ts-loader": "9.4.2",
"ts-loader": "9.4.3",
"typescript": "5.0.2",
"webpack": "5.74.0",
"webpack-cli": "4.10.0"
@@ -39,11 +39,11 @@
"@fortawesome/fontawesome-svg-core": "6.1.2",
"@fortawesome/free-solid-svg-icons": "6.1.2",
"@fortawesome/react-fontawesome": "0.2.0",
"@joplin/lib": "~2.11",
"@joplin/lib": "~2.12",
"async-mutex": "0.4.0",
"pdfjs-dist": "2.16.105",
"react": "18.2.0",
"react-dom": "18.2.0",
"styled-components": "5.3.10"
"styled-components": "5.3.11"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/plugin-repo-cli",
"version": "2.11.0",
"version": "2.12.0",
"description": "",
"main": "index.js",
"bin": "./dist/index.js",
@@ -18,9 +18,9 @@
"author": "",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@joplin/lib": "~2.11",
"@joplin/tools": "~2.11",
"@joplin/utils": "~2.11",
"@joplin/lib": "~2.12",
"@joplin/tools": "~2.12",
"@joplin/utils": "~2.12",
"fs-extra": "11.1.1",
"gh-release-assets": "2.0.1",
"node-fetch": "2.6.7",

View File

@@ -1,7 +1,7 @@
{
"name": "@joplin/react-native-alarm-notification",
"title": "React Native Alarm Notification for Joplin. Forked from https://github.com/emekalites/react-native-alarm-notification",
"version": "2.11.0",
"version": "2.12.0",
"description": "schedule alarm with notification in react-native",
"main": "index.js",
"private": true,

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/react-native-saf-x",
"version": "2.11.0",
"version": "2.12.0",
"description": "a module to help work with scoped storages on android easily",
"main": "src/index",
"react-native": "src/index",

View File

@@ -5,7 +5,7 @@ describe('linkReplacement', () => {
test('should handle non-resource links', () => {
const r = linkReplacement('https://example.com/test').html;
expect(r).toBe('<a data-from-md href=\'https://example.com/test\' onclick=\'postMessage("https://example.com/test", { resourceId: "" }); return false;\'>');
expect(r).toBe('<a data-from-md href=\'https://example.com/test\' onclick=\'postMessage(&quot;https://example.com/test&quot;, { resourceId: &quot;&quot; }); return false;\'>');
});
test('should handle non-resource links - simple rendering', () => {
@@ -17,7 +17,7 @@ describe('linkReplacement', () => {
// Handles a link such as:
// [Google](https://www.goo'onclick=javascript:alert(/1/);f=')
const r = linkReplacement('https://www.goo\'onclick=javascript:alert(/1/);f=\'', { linkRenderingType: 1 }).html;
expect(r).toBe('<a data-from-md href=\'https://www.goo&apos;onclick=javascript:alert(/1/);f=&apos;\' onclick=\'postMessage("https://www.goo%27onclick=javascript:alert(/1/);f=%27", { resourceId: "" }); return false;\'>');
expect(r).toBe('<a data-from-md href=\'https://www.goo&apos;onclick=javascript:alert(/1/);f=&apos;\' onclick=\'postMessage(&quot;https://www.goo%27onclick=javascript:alert(/1/);f=%27&quot;, { resourceId: &quot;&quot; }); return false;\'>');
});
test('should handle resource links - downloaded status', () => {
@@ -35,7 +35,7 @@ describe('linkReplacement', () => {
},
}).html;
expect(r).toBe(`<a data-from-md data-resource-id='${resourceId}' href='#' onclick='postMessage("joplin://${resourceId}", { resourceId: "${resourceId}" }); return false;'><span class="resource-icon fa-joplin"></span>`);
expect(r).toBe(`<a data-from-md data-resource-id='${resourceId}' href='#' onclick='postMessage(&quot;joplin://${resourceId}&quot;, { resourceId: &quot;${resourceId}&quot; }); return false;'><span class="resource-icon fa-joplin"></span>`);
});
test('should handle resource links - idle status', () => {

View File

@@ -99,7 +99,7 @@ export default function(href: string, options: Options = null): LinkReplacementR
postMessageSyntax: options.postMessageSyntax ?? 'void',
}, onClick);
} else {
js = `onclick='${js}'`;
js = `onclick='${htmlentities(js)}'`;
}
if (hrefAttr.indexOf('#') === 0 && href.indexOf('#') === 0) js = ''; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "2.11.0",
"version": "2.12.0",
"description": "The Joplin note renderer, used the mobile and desktop application",
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
"main": "index.js",
@@ -26,8 +26,8 @@
"typescript": "5.0.2"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.43",
"@joplin/fork-uslug": "^1.0.8",
"@joplin/fork-htmlparser2": "^4.1.44",
"@joplin/fork-uslug": "^1.0.9",
"font-awesome-filetypes": "2.1.0",
"fs-extra": "11.1.1",
"highlight.js": "11.8.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/server",
"version": "2.11.1",
"version": "2.12.0",
"private": true,
"scripts": {
"start-dev": "yarn run build && JOPLIN_IS_TESTING=1 nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",
@@ -23,19 +23,19 @@
"dependencies": {
"@aws-sdk/client-s3": "3.296.0",
"@fortawesome/fontawesome-free": "5.15.4",
"@joplin/lib": "~2.11",
"@joplin/renderer": "~2.11",
"@koa/cors": "3.1.0",
"@joplin/lib": "~2.12",
"@joplin/renderer": "~2.12",
"@koa/cors": "3.4.3",
"@types/uuid": "9.0.1",
"bcryptjs": "2.4.3",
"bulma": "0.9.4",
"bulma-prefers-dark": "0.1.0-beta.1",
"compare-versions": "3.6.0",
"dayjs": "1.11.7",
"formidable": "2.1.1",
"dayjs": "1.11.8",
"formidable": "2.1.2",
"fs-extra": "11.1.1",
"html-entities": "1.4.0",
"jquery": "3.6.4",
"jquery": "3.7.0",
"knex": "2.4.2",
"koa": "2.14.2",
"markdown-it": "13.0.1",
@@ -43,9 +43,9 @@
"nanoid": "2.1.11",
"node-cron": "3.0.2",
"node-env-file": "0.1.8",
"nodemailer": "6.9.2",
"nodemailer": "6.9.3",
"nodemon": "2.0.22",
"pg": "8.10.0",
"pg": "8.11.0",
"pretty-bytes": "5.6.0",
"prettycron": "0.10.0",
"query-string": "7.1.3",
@@ -58,7 +58,7 @@
"zxcvbn": "4.4.2"
},
"devDependencies": {
"@joplin/tools": "~2.11",
"@joplin/tools": "~2.12",
"@rmp135/sql-ts": "1.16.0",
"@types/formidable": "2.0.6",
"@types/fs-extra": "11.0.1",

View File

@@ -3,7 +3,7 @@ import { DbConnection, defaultAdminEmail, defaultAdminPassword } from '../db';
import { hashPassword } from '../utils/auth';
import uuidgen from '../utils/uuidgen';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('users', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.text('email', 'mediumtext').unique().notNullable();
@@ -123,13 +123,13 @@ export async function up(db: DbConnection): Promise<any> {
updated_time: now,
created_time: now,
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('users');
await db.schema.dropTable('sessions');
await db.schema.dropTable('permissions');
await db.schema.dropTable('files');
await db.schema.dropTable('api_clients');
await db.schema.dropTable('changes');
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('notifications', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('owner_id', 32).notNullable();
@@ -17,8 +17,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('notifications', (table: Knex.CreateTableBuilder) => {
table.unique(['owner_id', 'key']);
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('notifications');
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('shares', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('owner_id', 32).notNullable();
@@ -10,8 +10,8 @@ export async function up(db: DbConnection): Promise<any> {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('shares');
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('share_users', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('share_id', 32).notNullable();
@@ -38,8 +38,8 @@ export async function up(db: DbConnection): Promise<any> {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('share_users');
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('joplin_file_contents', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.string('owner_id', 32).notNullable();
@@ -18,8 +18,8 @@ export async function up(db: DbConnection): Promise<any> {
table.integer('content_type', 2).defaultTo(1).notNullable();
table.string('content_id', 32).defaultTo('').notNullable();
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('joplin_file_contents');
}
};

View File

@@ -1,13 +1,13 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.string('folder_id', 32).defaultTo('').notNullable();
table.integer('is_auto', 1).defaultTo(0).notNullable();
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('joplin_items');
}
};

View File

@@ -25,7 +25,7 @@ import { DbConnection } from '../db';
// is_shared: 1
// type_: 1`;
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('items', (table: Knex.CreateTableBuilder) => {
table.string('id', 32).unique().primary().notNullable();
table.text('name').notNullable();
@@ -109,8 +109,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.dropTable('permissions');
await db.schema.dropTable('joplin_file_contents');
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('items');
}
};

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('max_item_size').defaultTo(0).notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('can_share').defaultTo(1).notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('email_confirmed').defaultTo(0).notNullable();
table.integer('must_set_password').defaultTo(0).notNullable();
@@ -40,8 +40,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('tokens', (table: Knex.CreateTableBuilder) => {
table.index(['value', 'user_id']);
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('account_type').defaultTo(0).notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('can_upload').defaultTo(1).notNullable();
});
await db('users').update({ can_upload: 1 });
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('subscriptions', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.string('user_id', 32).notNullable();
@@ -12,8 +12,8 @@ export async function up(db: DbConnection): Promise<any> {
table.bigInteger('updated_time').notNullable();
table.bigInteger('created_time').notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.bigInteger('jop_updated_time').defaultTo(0).notNullable();
});
@@ -22,8 +22,8 @@ export async function up(db: DbConnection): Promise<any> {
}
});
}
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,13 +1,13 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.renameColumn('can_share', 'can_share_folder');
table.integer('can_share_note').defaultTo(1).notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -5,14 +5,14 @@ import { DbConnection } from '../db';
// recreate it in the same migration. So this is split into two migrations.
// https://github.com/knex/knex/issues/2581
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('max_item_size');
table.dropColumn('can_share_folder');
table.dropColumn('can_share_note');
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('max_item_size').defaultTo(null).nullable();
table.specificType('can_share_folder', 'smallint').defaultTo(null).nullable();
@@ -12,8 +12,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.integer('total_item_size').defaultTo(0).notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,9 +1,9 @@
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db('users').update({ email: db.raw('LOWER(email)') });
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('total_item_size');
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,12 +1,12 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.bigInteger('total_item_size').defaultTo(0).notNullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('users', (table: Knex.CreateTableBuilder) => {
table.specificType('enabled', 'smallint').defaultTo(1).nullable();
});
@@ -9,8 +9,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('subscriptions', (table: Knex.CreateTableBuilder) => {
table.specificType('is_deleted', 'smallint').defaultTo(0).nullable();
});
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
try {
await db.schema.alterTable('emails', (table: Knex.CreateTableBuilder) => {
table.text('key', 'mediumtext').defaultTo('').notNullable();
@@ -13,8 +13,8 @@ export async function up(db: DbConnection): Promise<any> {
// await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
// table.unique(['recipient_email', 'key']);
// });
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
// import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(_db: DbConnection): Promise<any> {
export const up = async (_db: DbConnection) => {
// try {
// await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
// table.dropUnique(['recipient_email', 'key']);
@@ -9,8 +9,8 @@ export async function up(_db: DbConnection): Promise<any> {
// } catch (error) {
// // console.warn('Could not drop unique constraint - this is not an error.', error);
// }
}
};
export async function down(_db: DbConnection): Promise<any> {
export const down = async (_db: DbConnection) => {
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('user_flags', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.string('user_id', 32).notNullable();
@@ -13,8 +13,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('user_flags', (table: Knex.CreateTableBuilder) => {
table.unique(['user_id', 'type']);
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('user_flags');
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('share_users', (table: Knex.CreateTableBuilder) => {
table.text('master_key', 'mediumtext').defaultTo('').notNullable();
});
@@ -9,9 +9,9 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.string('master_key_id', 32).defaultTo('').notNullable();
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.alterTable('share_users', (table: Knex.CreateTableBuilder) => {
table.dropColumn('master_key');
});
@@ -19,4 +19,4 @@ export async function down(db: DbConnection): Promise<any> {
await db.schema.alterTable('shares', (table: Knex.CreateTableBuilder) => {
table.dropColumn('master_key_id');
});
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('events', (table: Knex.CreateTableBuilder) => {
table.uuid('id').unique().notNullable();
table.increments('counter').unique().primary().notNullable();
@@ -14,8 +14,8 @@ export async function up(db: DbConnection): Promise<any> {
table.index('type');
table.index('name');
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('events');
}
};

View File

@@ -2,7 +2,7 @@ import { Knex } from 'knex';
import { DbConnection } from '../db';
import { msleep } from '../utils/time';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
if (!(await db.schema.hasColumn('items', 'owner_id'))) {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.string('owner_id', 32).defaultTo('').notNullable();
@@ -58,10 +58,10 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.string('owner_id', 32).notNullable().alter();
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropColumn('owner_id');
});
}
};

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.unique(['name', 'owner_id']);
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropUnique(['name', 'owner_id']);
});
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('storages', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.text('connection_string').notNullable();
@@ -31,12 +31,12 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('storages', (table: Knex.CreateTableBuilder) => {
table.unique(['connection_string']);
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('storages');
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropColumn('content_storage_id');
});
}
};

View File

@@ -1,14 +1,14 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.index('content_storage_id');
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.alterTable('items', (table: Knex.CreateTableBuilder) => {
table.dropIndex('content_storage_id');
});
}
};

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { DbConnection } from '../db';
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.createTable('user_deletions', (table: Knex.CreateTableBuilder) => {
table.increments('id').unique().primary().notNullable();
table.string('user_id', 32).notNullable();
@@ -19,8 +19,8 @@ export async function up(db: DbConnection): Promise<any> {
await db.schema.alterTable('user_deletions', (table: Knex.CreateTableBuilder) => {
table.unique(['user_id']);
});
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.dropTable('user_deletions');
}
};

View File

@@ -5,16 +5,16 @@ import { DbConnection } from '../db';
// it to an empty string by default, and update all rows that have "0" as
// recipient_id.
export async function up(db: DbConnection): Promise<any> {
export const up = async (db: DbConnection) => {
await db.schema.alterTable('emails', (table: Knex.CreateTableBuilder) => {
table.string('recipient_id', 32).defaultTo('').notNullable().alter();
});
await db('emails').update({ recipient_id: '' }).where('recipient_id', '=', '0');
}
};
export async function down(db: DbConnection): Promise<any> {
export const down = async (db: DbConnection) => {
await db.schema.alterTable('emails', (table: Knex.CreateTableBuilder) => {
table.string('recipient_id', 32).defaultTo(0).notNullable().alter();
});
}
};

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