Compare commits

...
57 Commits
Author SHA1 Message Date
Laurent Cozic 84a793270d move back to test 2021-05-21 15:15:48 +02:00
Laurent Cozic 5f8b73350d other tdsts 2021-05-21 15:09:08 +02:00
Laurent Cozic a733cb0394 services 2021-05-21 13:44:57 +02:00
Laurent Cozic 7da4cb0d80 sync tests 2021-05-21 13:22:50 +02:00
Laurent Cozic 3233082b4c move more 2021-05-21 13:14:45 +02:00
Laurent Cozic c782f5e981 move 2021-05-21 13:01:43 +02:00
Laurent Cozic db51e2dd4b cli setup 2021-05-21 12:42:19 +02:00
Laurent Cozic f00f3d7f04 test move 2021-05-21 12:33:33 +02:00
Laurent Cozic 6ff560f22f Desktop: Fixes #4591: Prevent cursor from jumping to top of page when pasting image 2021-05-20 19:13:35 +02:00
Laurent Cozic 2226b79c46 Desktop: Fixed pasting HTML in Rich Text editor, and improved pasting plain text 2021-05-20 18:08:59 +02:00
Laurent Cozic 9e9bf63d70 Desktop: Regression: Pasting plain text in Rich Text editor was broken 2021-05-20 17:42:04 +02:00
Breno Baptista 5d9419be5d Doc: Improved Linux notifications instruction (#4985) 2021-05-19 22:27:43 +01:00
Roman Musin 9f37aa96c6 Android: Fixes #4912, Fixes #4086, Fixes #4908: Fixed and improved alarm notifications (#4984) 2021-05-19 22:26:42 +01:00
JackGruber eceb14ff9e Desktop: Resolves #4433: Import linked local files when importing Markdown files (#4966) 2021-05-19 22:22:03 +01:00
Laurent Cozic 85211e8d5c Mobile: Fixes #4494: Fixed opening URLs that contain non-alphabetical characters 2021-05-19 16:48:55 +02:00
Laurent Cozic bd08041f53 Desktop: Pass custom CSS property to all export handlers and renderers 2021-05-19 15:00:16 +02:00
Laurent Cozic 77b284f01f Server: Added API end points to manage users 2021-05-18 17:11:50 +02:00
Laurent Cozic daaaa133ab Server: Allow enabling or disabling the sharing feature per user 2021-05-18 15:53:56 +02:00
Laurent Cozic e6c4eb7cdf Server: Renamed users.item_max_size => users.max_item_size 2021-05-18 15:11:03 +02:00
tatsu dc2cdb7d3a Doc: Fixed json of s3 bucket policy (#4981) 2021-05-17 15:15:23 -04:00
Andre Jilderda f3e03d48bb Desktop: Add classnames to DOM elements for theming purposes (#4933) 2021-05-17 19:33:44 +01:00
Laurent Cozic 6577f4f35d Desktop: Regression: It was no longer possible to add list items in an empty note 2021-05-17 20:30:48 +02:00
Laurent Cozic d29624c816 Desktop: Handle too large items for Joplin Server 2021-05-17 18:55:39 +02:00
Laurent Cozic 6afde54bda Server: Add support for item size limit 2021-05-17 18:35:01 +02:00
Laurent Cozic ec7f0f479a Server: Improved log table too and made it sortable 2021-05-17 17:29:21 +02:00
Laurent Cozic 7f05420fda Server: Improved Items table and added item size to it 2021-05-17 17:02:15 +02:00
Laurent Cozic a3f8cd4850 Merge branch 'release-2.0' into dev 2021-05-16 19:40:08 +02:00
Laurent Cozic 01ccf5170a Tools: Changelog for Android version 2021-05-16 19:38:36 +02:00
Laurent Cozic 6ddb69e1ea Server: Fixed bug when unsharing a notebook that has no recipients 2021-05-16 18:55:07 +02:00
Laurent Cozic b01f82bb33 Android release v2.0.1 2021-05-16 17:54:13 +02:00
Laurent Cozic b6c9edba21 Tools: Make sure branch has been pushed before releasing Android version 2021-05-16 17:50:50 +02:00
Laurent Cozic f7d164be6e Desktop: Allow unsharing a note 2021-05-16 17:28:49 +02:00
Laurent Cozic 6f2f24171d Desktop: Add Share Notebook menu item 2021-05-16 15:21:55 +02:00
Laurent Cozic 12cc64008b typo 2021-05-16 12:49:05 +02:00
Laurent Cozic b9955f58d3 Server: Refactor ShareType 2021-05-16 12:46:58 +02:00
Laurent Cozic 489995daef Server: Fixed deleting a note that has been shared 2021-05-16 12:42:58 +02:00
Laurent Cozic e156ee1b58 Server: Generate only one share link per note 2021-05-16 12:33:36 +02:00
Laurent Cozic a24b0091ad Server: Go back to home page when there is an error and user is logged in 2021-05-16 12:19:18 +02:00
Laurent Cozic 2655b6deee Merge branch 'dev' of github.com:laurent22/joplin into dev 2021-05-16 11:48:15 +02:00
Laurent Cozic 45c40f7395 Server: Fixed log page 2021-05-16 11:46:16 +02:00
Woosuk Park ecb0eee355 All: Translation: Update ko.po (#4976)
* Some Translated. and some modified

* ko.po Update
2021-05-16 02:39:43 -04:00
suixinio 4916f4cc92 All: Translation: Update zh_CN.po (#4969)
This PR updates zh_CN.po, with just a minor translation fix.
2021-05-15 15:58:48 -04:00
Laurent Cozic 15fe119256 Desktop: Made sync more reliable by making it skip items that time out, and improved sync status screen 2021-05-15 20:56:49 +02:00
Laurent Cozic 0b46880a00 Desktop: Fixes #4926: Fixed issue with empty panels being created by plugins 2021-05-15 17:30:56 +02:00
Laurent Cozic deaa731983 Update translations 2021-05-15 16:15:55 +02:00
Laurent Cozic d061bb1a4f typo 2021-05-15 16:06:24 +02:00
Laurent Cozic 03db0c5486 Desktop: Resolves #4462: Improved usability when plugin repository cannot be connected to 2021-05-15 16:04:10 +02:00
Laurent Cozic bb275e671d Tools: Allow running the test units with Postgres 2021-05-15 15:13:08 +02:00
Laurent Cozic 2d0580ff71 Server: Fixed /items page when using Postgres 2021-05-15 15:10:40 +02:00
Laurent Cozic 2331d3487b Desktop, Cli: Resolves #4968: Import SVG as images when importing ENEX files 2021-05-15 13:42:57 +02:00
Laurent Cozic f1380fd51d Server: Fixes #4540: Make sure temp files are deleted after upload is done 2021-05-15 12:13:46 +02:00
Laurent Cozic d462dab8eb Update French translation 2021-05-15 11:36:52 +02:00
Laurent Cozic cf37b74d9a Merge branch 'dev' of github.com:laurent22/joplin into dev 2021-05-15 11:14:40 +02:00
Laurent Cozic aec3ea9c0c Desktop, Cli: Fixes #4965: Improved importing Evernote notes that contain codeblocks 2021-05-15 11:12:11 +02:00
Helmut K. C. Tessarek 1f5aa70acd Update translations (for new server/client code) 2021-05-14 13:27:17 -04:00
Harris Arvanitis 416637ce83 All: Translation: Update el_GR.po (#4961) 2021-05-14 13:23:13 -04:00
Gen Neko 99c4b0bc01 All: Translation: Update ja_JP.po (#4960) 2021-05-14 13:22:35 -04:00
295 changed files with 22136 additions and 15090 deletions

No files matched your search

+123 -96
View File
@@ -74,69 +74,18 @@ packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/services/plugins/PluginRunner.d.ts
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/services/plugins/PluginRunner.js.map
packages/app-cli/tests/EnexToMd.d.ts
packages/app-cli/tests/EnexToMd.js
packages/app-cli/tests/EnexToMd.js.map
packages/app-cli/tests/HtmlToMd.d.ts
packages/app-cli/tests/HtmlToMd.js
packages/app-cli/tests/HtmlToMd.js.map
packages/app-cli/tests/InMemoryCache.d.ts
packages/app-cli/tests/InMemoryCache.js
packages/app-cli/tests/InMemoryCache.js.map
packages/app-cli/tests/MdToHtml.d.ts
packages/app-cli/tests/MdToHtml.js
packages/app-cli/tests/MdToHtml.js.map
packages/app-cli/tests/Synchronizer.basics.d.ts
packages/app-cli/tests/Synchronizer.basics.js
packages/app-cli/tests/Synchronizer.basics.js.map
packages/app-cli/tests/Synchronizer.conflicts.d.ts
packages/app-cli/tests/Synchronizer.conflicts.js
packages/app-cli/tests/Synchronizer.conflicts.js.map
packages/app-cli/tests/Synchronizer.e2ee.d.ts
packages/app-cli/tests/Synchronizer.e2ee.js
packages/app-cli/tests/Synchronizer.e2ee.js.map
packages/app-cli/tests/Synchronizer.resources.d.ts
packages/app-cli/tests/Synchronizer.resources.js
packages/app-cli/tests/Synchronizer.resources.js.map
packages/app-cli/tests/Synchronizer.revisions.d.ts
packages/app-cli/tests/Synchronizer.revisions.js
packages/app-cli/tests/Synchronizer.revisions.js.map
packages/app-cli/tests/Synchronizer.sharing.d.ts
packages/app-cli/tests/Synchronizer.sharing.js
packages/app-cli/tests/Synchronizer.sharing.js.map
packages/app-cli/tests/Synchronizer.tags.d.ts
packages/app-cli/tests/Synchronizer.tags.js
packages/app-cli/tests/Synchronizer.tags.js.map
packages/app-cli/tests/Synchronizer.tools.d.ts
packages/app-cli/tests/Synchronizer.tools.js
packages/app-cli/tests/Synchronizer.tools.js.map
packages/app-cli/tests/dateTimeFormats.d.ts
packages/app-cli/tests/dateTimeFormats.js
packages/app-cli/tests/dateTimeFormats.js.map
packages/app-cli/tests/file-api-driver.d.ts
packages/app-cli/tests/file-api-driver.js
packages/app-cli/tests/file-api-driver.js.map
packages/app-cli/tests/fsDriver.d.ts
packages/app-cli/tests/fsDriver.js
packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/htmlUtils.d.ts
packages/app-cli/tests/htmlUtils.js
packages/app-cli/tests/htmlUtils.js.map
packages/app-cli/tests/models_Folder.d.ts
packages/app-cli/tests/models_Folder.js
packages/app-cli/tests/models_Folder.js.map
packages/app-cli/tests/models_Folder.sharing.d.ts
packages/app-cli/tests/models_Folder.sharing.js
packages/app-cli/tests/models_Folder.sharing.js.map
packages/app-cli/tests/models_Note.d.ts
packages/app-cli/tests/models_Note.js
packages/app-cli/tests/models_Note.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/registry.d.ts
packages/app-cli/tests/registry.js
packages/app-cli/tests/registry.js.map
packages/app-cli/tests/MdToMd.d.ts
packages/app-cli/tests/MdToMd.js
packages/app-cli/tests/MdToMd.js.map
packages/app-cli/tests/services/plugins/PluginService.d.ts
packages/app-cli/tests/services/plugins/PluginService.js
packages/app-cli/tests/services/plugins/PluginService.js.map
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
packages/app-cli/tests/services/plugins/RepositoryApi.js
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
@@ -152,42 +101,6 @@ packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
packages/app-cli/tests/services/plugins/sandboxProxy.js
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
packages/app-cli/tests/services_CommandService.d.ts
packages/app-cli/tests/services_CommandService.js
packages/app-cli/tests/services_CommandService.js.map
packages/app-cli/tests/services_InteropService.d.ts
packages/app-cli/tests/services_InteropService.js
packages/app-cli/tests/services_InteropService.js.map
packages/app-cli/tests/services_InteropService_Exporter_Html.d.ts
packages/app-cli/tests/services_InteropService_Exporter_Html.js
packages/app-cli/tests/services_InteropService_Exporter_Html.js.map
packages/app-cli/tests/services_PluginService.d.ts
packages/app-cli/tests/services_PluginService.js
packages/app-cli/tests/services_PluginService.js.map
packages/app-cli/tests/services_ResourceService.d.ts
packages/app-cli/tests/services_ResourceService.js
packages/app-cli/tests/services_ResourceService.js.map
packages/app-cli/tests/services_SearchEngineUtils.d.ts
packages/app-cli/tests/services_SearchEngineUtils.js
packages/app-cli/tests/services_SearchEngineUtils.js.map
packages/app-cli/tests/services_keychainService.d.ts
packages/app-cli/tests/services_keychainService.js
packages/app-cli/tests/services_keychainService.js.map
packages/app-cli/tests/services_rest_Api.d.ts
packages/app-cli/tests/services_rest_Api.js
packages/app-cli/tests/services_rest_Api.js.map
packages/app-cli/tests/synchronizer_LockHandler.d.ts
packages/app-cli/tests/synchronizer_LockHandler.js
packages/app-cli/tests/synchronizer_LockHandler.js.map
packages/app-cli/tests/synchronizer_MigrationHandler.d.ts
packages/app-cli/tests/synchronizer_MigrationHandler.js
packages/app-cli/tests/synchronizer_MigrationHandler.js.map
packages/app-cli/tests/test-utils-synchronizer.d.ts
packages/app-cli/tests/test-utils-synchronizer.js
packages/app-cli/tests/test-utils-synchronizer.js.map
packages/app-cli/tests/test-utils.d.ts
packages/app-cli/tests/test-utils.js
packages/app-cli/tests/test-utils.js.map
packages/app-desktop/ElectronAppWrapper.d.ts
packages/app-desktop/ElectronAppWrapper.js
packages/app-desktop/ElectronAppWrapper.js.map
@@ -578,6 +491,9 @@ packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.d.ts
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js.map
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
@@ -677,6 +593,9 @@ packages/app-desktop/gui/style/StyledFormLabel.js.map
packages/app-desktop/gui/style/StyledInput.d.ts
packages/app-desktop/gui/style/StyledInput.js
packages/app-desktop/gui/style/StyledInput.js.map
packages/app-desktop/gui/style/StyledLink.d.ts
packages/app-desktop/gui/style/StyledLink.js
packages/app-desktop/gui/style/StyledLink.js.map
packages/app-desktop/gui/style/StyledMessage.d.ts
packages/app-desktop/gui/style/StyledMessage.js
packages/app-desktop/gui/style/StyledMessage.js.map
@@ -782,9 +701,9 @@ packages/app-mobile/services/AlarmServiceDriver.android.js.map
packages/app-mobile/services/AlarmServiceDriver.ios.d.ts
packages/app-mobile/services/AlarmServiceDriver.ios.js
packages/app-mobile/services/AlarmServiceDriver.ios.js.map
packages/app-mobile/setUpQuickActions.d.ts
packages/app-mobile/setUpQuickActions.js
packages/app-mobile/setUpQuickActions.js.map
packages/app-mobile/setupQuickActions.d.ts
packages/app-mobile/setupQuickActions.js
packages/app-mobile/setupQuickActions.js.map
packages/app-mobile/utils/ShareExtension.d.ts
packages/app-mobile/utils/ShareExtension.js
packages/app-mobile/utils/ShareExtension.js.map
@@ -800,6 +719,9 @@ packages/app-mobile/utils/checkPermissions.js.map
packages/app-mobile/utils/fs-driver-rn.d.ts
packages/app-mobile/utils/fs-driver-rn.js
packages/app-mobile/utils/fs-driver-rn.js.map
packages/app-mobile/utils/setupNotifications.d.ts
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/setupNotifications.js.map
packages/app-mobile/utils/shareHandler.d.ts
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/shareHandler.js.map
@@ -875,6 +797,9 @@ packages/lib/HtmlToMd.js.map
packages/lib/InMemoryCache.d.ts
packages/lib/InMemoryCache.js
packages/lib/InMemoryCache.js.map
packages/lib/InMemoryCache.test.d.ts
packages/lib/InMemoryCache.test.js
packages/lib/InMemoryCache.test.js.map
packages/lib/JoplinDatabase.d.ts
packages/lib/JoplinDatabase.js
packages/lib/JoplinDatabase.js.map
@@ -926,6 +851,9 @@ packages/lib/eventManager.js.map
packages/lib/file-api-driver-joplinServer.d.ts
packages/lib/file-api-driver-joplinServer.js
packages/lib/file-api-driver-joplinServer.js.map
packages/lib/file-api-driver.test.d.ts
packages/lib/file-api-driver.test.js
packages/lib/file-api-driver.test.js.map
packages/lib/file-api.d.ts
packages/lib/file-api.js
packages/lib/file-api.js.map
@@ -935,12 +863,24 @@ packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map
packages/lib/fsDriver.test.d.ts
packages/lib/fsDriver.test.js
packages/lib/fsDriver.test.js.map
packages/lib/htmlUtils.d.ts
packages/lib/htmlUtils.js
packages/lib/htmlUtils.js.map
packages/lib/htmlUtils.test.d.ts
packages/lib/htmlUtils.test.js
packages/lib/htmlUtils.test.js.map
packages/lib/htmlUtils2.test.d.ts
packages/lib/htmlUtils2.test.js
packages/lib/htmlUtils2.test.js.map
packages/lib/import-enex-md-gen.d.ts
packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.js.map
packages/lib/import-enex-md-gen.test.d.ts
packages/lib/import-enex-md-gen.test.js
packages/lib/import-enex-md-gen.test.js.map
packages/lib/locale.d.ts
packages/lib/locale.js
packages/lib/locale.js.map
@@ -950,6 +890,9 @@ packages/lib/markdownUtils.js.map
packages/lib/markdownUtils.test.d.ts
packages/lib/markdownUtils.test.js
packages/lib/markdownUtils.test.js.map
packages/lib/markdownUtils2.test.d.ts
packages/lib/markdownUtils2.test.js
packages/lib/markdownUtils2.test.js.map
packages/lib/markupLanguageUtils.d.ts
packages/lib/markupLanguageUtils.js
packages/lib/markupLanguageUtils.js.map
@@ -962,6 +905,12 @@ packages/lib/models/BaseItem.js.map
packages/lib/models/Folder.d.ts
packages/lib/models/Folder.js
packages/lib/models/Folder.js.map
packages/lib/models/Folder.sharing.test.d.ts
packages/lib/models/Folder.sharing.test.js
packages/lib/models/Folder.sharing.test.js.map
packages/lib/models/Folder.test.d.ts
packages/lib/models/Folder.test.js
packages/lib/models/Folder.test.js.map
packages/lib/models/ItemChange.d.ts
packages/lib/models/ItemChange.js
packages/lib/models/ItemChange.js.map
@@ -974,6 +923,9 @@ packages/lib/models/Migration.js.map
packages/lib/models/Note.d.ts
packages/lib/models/Note.js
packages/lib/models/Note.js.map
packages/lib/models/Note.test.d.ts
packages/lib/models/Note.test.js
packages/lib/models/Note.test.js.map
packages/lib/models/NoteResource.d.ts
packages/lib/models/NoteResource.js
packages/lib/models/NoteResource.js.map
@@ -995,12 +947,18 @@ packages/lib/models/Search.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/Setting.test.d.ts
packages/lib/models/Setting.test.js
packages/lib/models/Setting.test.js.map
packages/lib/models/SmartFilter.d.ts
packages/lib/models/SmartFilter.js
packages/lib/models/SmartFilter.js.map
packages/lib/models/Tag.d.ts
packages/lib/models/Tag.js
packages/lib/models/Tag.js.map
packages/lib/models/dateTimeFormats.test.d.ts
packages/lib/models/dateTimeFormats.test.js
packages/lib/models/dateTimeFormats.test.js.map
packages/lib/models/settings/FileHandler.d.ts
packages/lib/models/settings/FileHandler.js
packages/lib/models/settings/FileHandler.js.map
@@ -1028,6 +986,9 @@ packages/lib/reducer.js.map
packages/lib/registry.d.ts
packages/lib/registry.js
packages/lib/registry.js.map
packages/lib/registry.test.d.ts
packages/lib/registry.test.js
packages/lib/registry.test.js.map
packages/lib/services/AlarmService.d.ts
packages/lib/services/AlarmService.js
packages/lib/services/AlarmService.js.map
@@ -1040,6 +1001,9 @@ packages/lib/services/BaseService.js.map
packages/lib/services/CommandService.d.ts
packages/lib/services/CommandService.js
packages/lib/services/CommandService.js.map
packages/lib/services/CommandService.test.d.ts
packages/lib/services/CommandService.test.js
packages/lib/services/CommandService.test.js.map
packages/lib/services/DecryptionWorker.d.ts
packages/lib/services/DecryptionWorker.js
packages/lib/services/DecryptionWorker.js.map
@@ -1088,6 +1052,9 @@ packages/lib/services/ResourceFetcher.js.map
packages/lib/services/ResourceService.d.ts
packages/lib/services/ResourceService.js
packages/lib/services/ResourceService.js.map
packages/lib/services/ResourceService.test.d.ts
packages/lib/services/ResourceService.test.js
packages/lib/services/ResourceService.test.js.map
packages/lib/services/RevisionService.d.ts
packages/lib/services/RevisionService.js
packages/lib/services/RevisionService.js.map
@@ -1100,6 +1067,9 @@ packages/lib/services/UndoRedoService.js.map
packages/lib/services/WhenClause.d.ts
packages/lib/services/WhenClause.js
packages/lib/services/WhenClause.js.map
packages/lib/services/WhenClause.test.d.ts
packages/lib/services/WhenClause.test.js
packages/lib/services/WhenClause.test.js.map
packages/lib/services/commands/MenuUtils.d.ts
packages/lib/services/commands/MenuUtils.js
packages/lib/services/commands/MenuUtils.js.map
@@ -1133,6 +1103,9 @@ packages/lib/services/debug/populateDatabase.js.map
packages/lib/services/interop/InteropService.d.ts
packages/lib/services/interop/InteropService.js
packages/lib/services/interop/InteropService.js.map
packages/lib/services/interop/InteropService.test.d.ts
packages/lib/services/interop/InteropService.test.js
packages/lib/services/interop/InteropService.test.js.map
packages/lib/services/interop/InteropService_Exporter_Base.d.ts
packages/lib/services/interop/InteropService_Exporter_Base.js
packages/lib/services/interop/InteropService_Exporter_Base.js.map
@@ -1142,6 +1115,9 @@ packages/lib/services/interop/InteropService_Exporter_Custom.js.map
packages/lib/services/interop/InteropService_Exporter_Html.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.js
packages/lib/services/interop/InteropService_Exporter_Html.js.map
packages/lib/services/interop/InteropService_Exporter_Html.test.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.test.js
packages/lib/services/interop/InteropService_Exporter_Html.test.js.map
packages/lib/services/interop/InteropService_Exporter_Jex.d.ts
packages/lib/services/interop/InteropService_Exporter_Jex.js
packages/lib/services/interop/InteropService_Exporter_Jex.js.map
@@ -1190,6 +1166,9 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js.map
packages/lib/services/keychain/KeychainServiceDriverBase.d.ts
packages/lib/services/keychain/KeychainServiceDriverBase.js
packages/lib/services/keychain/KeychainServiceDriverBase.js.map
packages/lib/services/keychain/keychainService.test.d.ts
packages/lib/services/keychain/keychainService.test.js
packages/lib/services/keychain/keychainService.test.js.map
packages/lib/services/plugins/BasePluginRunner.d.ts
packages/lib/services/plugins/BasePluginRunner.js
packages/lib/services/plugins/BasePluginRunner.js.map
@@ -1304,6 +1283,9 @@ packages/lib/services/plugins/utils/validatePluginId.test.js.map
packages/lib/services/rest/Api.d.ts
packages/lib/services/rest/Api.js
packages/lib/services/rest/Api.js.map
packages/lib/services/rest/Api.test.d.ts
packages/lib/services/rest/Api.test.js
packages/lib/services/rest/Api.test.js.map
packages/lib/services/rest/ApiResponse.d.ts
packages/lib/services/rest/ApiResponse.js
packages/lib/services/rest/ApiResponse.js.map
@@ -1364,6 +1346,9 @@ packages/lib/services/searchengine/SearchEngine.js.map
packages/lib/services/searchengine/SearchEngineUtils.d.ts
packages/lib/services/searchengine/SearchEngineUtils.js
packages/lib/services/searchengine/SearchEngineUtils.js.map
packages/lib/services/searchengine/SearchEngineUtils.test.d.ts
packages/lib/services/searchengine/SearchEngineUtils.test.js
packages/lib/services/searchengine/SearchEngineUtils.test.js.map
packages/lib/services/searchengine/filterParser.d.ts
packages/lib/services/searchengine/filterParser.js
packages/lib/services/searchengine/filterParser.js.map
@@ -1388,6 +1373,30 @@ packages/lib/services/synchronizer/LockHandler.js.map
packages/lib/services/synchronizer/MigrationHandler.d.ts
packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map
packages/lib/services/synchronizer/Synchronizer.conflicts.test.d.ts
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js.map
packages/lib/services/synchronizer/Synchronizer.e2ee.test.d.ts
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js.map
packages/lib/services/synchronizer/Synchronizer.resources.test.d.ts
packages/lib/services/synchronizer/Synchronizer.resources.test.js
packages/lib/services/synchronizer/Synchronizer.resources.test.js.map
packages/lib/services/synchronizer/Synchronizer.revisions.test.d.ts
packages/lib/services/synchronizer/Synchronizer.revisions.test.js
packages/lib/services/synchronizer/Synchronizer.revisions.test.js.map
packages/lib/services/synchronizer/Synchronizer.sharing.test.d.ts
packages/lib/services/synchronizer/Synchronizer.sharing.test.js
packages/lib/services/synchronizer/Synchronizer.sharing.test.js.map
packages/lib/services/synchronizer/Synchronizer.tags.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tags.test.js
packages/lib/services/synchronizer/Synchronizer.tags.test.js.map
packages/lib/services/synchronizer/Synchronizer.tools.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tools.test.js
packages/lib/services/synchronizer/Synchronizer.tools.test.js.map
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.d.ts
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js.map
@@ -1397,6 +1406,12 @@ packages/lib/services/synchronizer/migrations/1.js.map
packages/lib/services/synchronizer/migrations/2.d.ts
packages/lib/services/synchronizer/migrations/2.js
packages/lib/services/synchronizer/migrations/2.js.map
packages/lib/services/synchronizer/synchronizer_LockHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js.map
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js.map
packages/lib/services/synchronizer/tools.d.ts
packages/lib/services/synchronizer/tools.js
packages/lib/services/synchronizer/tools.js.map
@@ -1409,6 +1424,12 @@ packages/lib/services/synchronizer/utils/types.js.map
packages/lib/shim.d.ts
packages/lib/shim.js
packages/lib/shim.js.map
packages/lib/testing/test-utils-synchronizer.d.ts
packages/lib/testing/test-utils-synchronizer.js
packages/lib/testing/test-utils-synchronizer.js.map
packages/lib/testing/test-utils.d.ts
packages/lib/testing/test-utils.js
packages/lib/testing/test-utils.js.map
packages/lib/theme.d.ts
packages/lib/theme.js
packages/lib/theme.js.map
@@ -1442,6 +1463,9 @@ packages/lib/themes/type.js.map
packages/lib/time.d.ts
packages/lib/time.js
packages/lib/time.js.map
packages/lib/utils/credentialFiles.d.ts
packages/lib/utils/credentialFiles.js
packages/lib/utils/credentialFiles.js.map
packages/lib/uuid.d.ts
packages/lib/uuid.js
packages/lib/uuid.js.map
@@ -1568,6 +1592,9 @@ packages/tools/generate-database-types.js.map
packages/tools/lerna-add.d.ts
packages/tools/lerna-add.js
packages/tools/lerna-add.js.map
packages/tools/release-android.d.ts
packages/tools/release-android.js
packages/tools/release-android.js.map
packages/tools/release-cli.d.ts
packages/tools/release-cli.js
packages/tools/release-cli.js.map
+1 -1
View File
@@ -76,7 +76,7 @@ module.exports = {
// Warn only for now because fixing everything would take too much
// refactoring, but new code should try to stick to it.
'complexity': ['warn', { max: 10 }],
// 'complexity': ['warn', { max: 10 }],
// Checks rules of Hooks
'react-hooks/rules-of-hooks': 'error',
+123 -96
View File
@@ -60,69 +60,18 @@ packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/services/plugins/PluginRunner.d.ts
packages/app-cli/app/services/plugins/PluginRunner.js
packages/app-cli/app/services/plugins/PluginRunner.js.map
packages/app-cli/tests/EnexToMd.d.ts
packages/app-cli/tests/EnexToMd.js
packages/app-cli/tests/EnexToMd.js.map
packages/app-cli/tests/HtmlToMd.d.ts
packages/app-cli/tests/HtmlToMd.js
packages/app-cli/tests/HtmlToMd.js.map
packages/app-cli/tests/InMemoryCache.d.ts
packages/app-cli/tests/InMemoryCache.js
packages/app-cli/tests/InMemoryCache.js.map
packages/app-cli/tests/MdToHtml.d.ts
packages/app-cli/tests/MdToHtml.js
packages/app-cli/tests/MdToHtml.js.map
packages/app-cli/tests/Synchronizer.basics.d.ts
packages/app-cli/tests/Synchronizer.basics.js
packages/app-cli/tests/Synchronizer.basics.js.map
packages/app-cli/tests/Synchronizer.conflicts.d.ts
packages/app-cli/tests/Synchronizer.conflicts.js
packages/app-cli/tests/Synchronizer.conflicts.js.map
packages/app-cli/tests/Synchronizer.e2ee.d.ts
packages/app-cli/tests/Synchronizer.e2ee.js
packages/app-cli/tests/Synchronizer.e2ee.js.map
packages/app-cli/tests/Synchronizer.resources.d.ts
packages/app-cli/tests/Synchronizer.resources.js
packages/app-cli/tests/Synchronizer.resources.js.map
packages/app-cli/tests/Synchronizer.revisions.d.ts
packages/app-cli/tests/Synchronizer.revisions.js
packages/app-cli/tests/Synchronizer.revisions.js.map
packages/app-cli/tests/Synchronizer.sharing.d.ts
packages/app-cli/tests/Synchronizer.sharing.js
packages/app-cli/tests/Synchronizer.sharing.js.map
packages/app-cli/tests/Synchronizer.tags.d.ts
packages/app-cli/tests/Synchronizer.tags.js
packages/app-cli/tests/Synchronizer.tags.js.map
packages/app-cli/tests/Synchronizer.tools.d.ts
packages/app-cli/tests/Synchronizer.tools.js
packages/app-cli/tests/Synchronizer.tools.js.map
packages/app-cli/tests/dateTimeFormats.d.ts
packages/app-cli/tests/dateTimeFormats.js
packages/app-cli/tests/dateTimeFormats.js.map
packages/app-cli/tests/file-api-driver.d.ts
packages/app-cli/tests/file-api-driver.js
packages/app-cli/tests/file-api-driver.js.map
packages/app-cli/tests/fsDriver.d.ts
packages/app-cli/tests/fsDriver.js
packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/htmlUtils.d.ts
packages/app-cli/tests/htmlUtils.js
packages/app-cli/tests/htmlUtils.js.map
packages/app-cli/tests/models_Folder.d.ts
packages/app-cli/tests/models_Folder.js
packages/app-cli/tests/models_Folder.js.map
packages/app-cli/tests/models_Folder.sharing.d.ts
packages/app-cli/tests/models_Folder.sharing.js
packages/app-cli/tests/models_Folder.sharing.js.map
packages/app-cli/tests/models_Note.d.ts
packages/app-cli/tests/models_Note.js
packages/app-cli/tests/models_Note.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/registry.d.ts
packages/app-cli/tests/registry.js
packages/app-cli/tests/registry.js.map
packages/app-cli/tests/MdToMd.d.ts
packages/app-cli/tests/MdToMd.js
packages/app-cli/tests/MdToMd.js.map
packages/app-cli/tests/services/plugins/PluginService.d.ts
packages/app-cli/tests/services/plugins/PluginService.js
packages/app-cli/tests/services/plugins/PluginService.js.map
packages/app-cli/tests/services/plugins/RepositoryApi.d.ts
packages/app-cli/tests/services/plugins/RepositoryApi.js
packages/app-cli/tests/services/plugins/RepositoryApi.js.map
@@ -138,42 +87,6 @@ packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
packages/app-cli/tests/services/plugins/sandboxProxy.js
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
packages/app-cli/tests/services_CommandService.d.ts
packages/app-cli/tests/services_CommandService.js
packages/app-cli/tests/services_CommandService.js.map
packages/app-cli/tests/services_InteropService.d.ts
packages/app-cli/tests/services_InteropService.js
packages/app-cli/tests/services_InteropService.js.map
packages/app-cli/tests/services_InteropService_Exporter_Html.d.ts
packages/app-cli/tests/services_InteropService_Exporter_Html.js
packages/app-cli/tests/services_InteropService_Exporter_Html.js.map
packages/app-cli/tests/services_PluginService.d.ts
packages/app-cli/tests/services_PluginService.js
packages/app-cli/tests/services_PluginService.js.map
packages/app-cli/tests/services_ResourceService.d.ts
packages/app-cli/tests/services_ResourceService.js
packages/app-cli/tests/services_ResourceService.js.map
packages/app-cli/tests/services_SearchEngineUtils.d.ts
packages/app-cli/tests/services_SearchEngineUtils.js
packages/app-cli/tests/services_SearchEngineUtils.js.map
packages/app-cli/tests/services_keychainService.d.ts
packages/app-cli/tests/services_keychainService.js
packages/app-cli/tests/services_keychainService.js.map
packages/app-cli/tests/services_rest_Api.d.ts
packages/app-cli/tests/services_rest_Api.js
packages/app-cli/tests/services_rest_Api.js.map
packages/app-cli/tests/synchronizer_LockHandler.d.ts
packages/app-cli/tests/synchronizer_LockHandler.js
packages/app-cli/tests/synchronizer_LockHandler.js.map
packages/app-cli/tests/synchronizer_MigrationHandler.d.ts
packages/app-cli/tests/synchronizer_MigrationHandler.js
packages/app-cli/tests/synchronizer_MigrationHandler.js.map
packages/app-cli/tests/test-utils-synchronizer.d.ts
packages/app-cli/tests/test-utils-synchronizer.js
packages/app-cli/tests/test-utils-synchronizer.js.map
packages/app-cli/tests/test-utils.d.ts
packages/app-cli/tests/test-utils.js
packages/app-cli/tests/test-utils.js.map
packages/app-desktop/ElectronAppWrapper.d.ts
packages/app-desktop/ElectronAppWrapper.js
packages/app-desktop/ElectronAppWrapper.js.map
@@ -564,6 +477,9 @@ packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.d.ts
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js
packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js.map
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
@@ -663,6 +579,9 @@ packages/app-desktop/gui/style/StyledFormLabel.js.map
packages/app-desktop/gui/style/StyledInput.d.ts
packages/app-desktop/gui/style/StyledInput.js
packages/app-desktop/gui/style/StyledInput.js.map
packages/app-desktop/gui/style/StyledLink.d.ts
packages/app-desktop/gui/style/StyledLink.js
packages/app-desktop/gui/style/StyledLink.js.map
packages/app-desktop/gui/style/StyledMessage.d.ts
packages/app-desktop/gui/style/StyledMessage.js
packages/app-desktop/gui/style/StyledMessage.js.map
@@ -768,9 +687,9 @@ packages/app-mobile/services/AlarmServiceDriver.android.js.map
packages/app-mobile/services/AlarmServiceDriver.ios.d.ts
packages/app-mobile/services/AlarmServiceDriver.ios.js
packages/app-mobile/services/AlarmServiceDriver.ios.js.map
packages/app-mobile/setUpQuickActions.d.ts
packages/app-mobile/setUpQuickActions.js
packages/app-mobile/setUpQuickActions.js.map
packages/app-mobile/setupQuickActions.d.ts
packages/app-mobile/setupQuickActions.js
packages/app-mobile/setupQuickActions.js.map
packages/app-mobile/utils/ShareExtension.d.ts
packages/app-mobile/utils/ShareExtension.js
packages/app-mobile/utils/ShareExtension.js.map
@@ -786,6 +705,9 @@ packages/app-mobile/utils/checkPermissions.js.map
packages/app-mobile/utils/fs-driver-rn.d.ts
packages/app-mobile/utils/fs-driver-rn.js
packages/app-mobile/utils/fs-driver-rn.js.map
packages/app-mobile/utils/setupNotifications.d.ts
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/setupNotifications.js.map
packages/app-mobile/utils/shareHandler.d.ts
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/shareHandler.js.map
@@ -861,6 +783,9 @@ packages/lib/HtmlToMd.js.map
packages/lib/InMemoryCache.d.ts
packages/lib/InMemoryCache.js
packages/lib/InMemoryCache.js.map
packages/lib/InMemoryCache.test.d.ts
packages/lib/InMemoryCache.test.js
packages/lib/InMemoryCache.test.js.map
packages/lib/JoplinDatabase.d.ts
packages/lib/JoplinDatabase.js
packages/lib/JoplinDatabase.js.map
@@ -912,6 +837,9 @@ packages/lib/eventManager.js.map
packages/lib/file-api-driver-joplinServer.d.ts
packages/lib/file-api-driver-joplinServer.js
packages/lib/file-api-driver-joplinServer.js.map
packages/lib/file-api-driver.test.d.ts
packages/lib/file-api-driver.test.js
packages/lib/file-api-driver.test.js.map
packages/lib/file-api.d.ts
packages/lib/file-api.js
packages/lib/file-api.js.map
@@ -921,12 +849,24 @@ packages/lib/fs-driver-base.js.map
packages/lib/fs-driver-node.d.ts
packages/lib/fs-driver-node.js
packages/lib/fs-driver-node.js.map
packages/lib/fsDriver.test.d.ts
packages/lib/fsDriver.test.js
packages/lib/fsDriver.test.js.map
packages/lib/htmlUtils.d.ts
packages/lib/htmlUtils.js
packages/lib/htmlUtils.js.map
packages/lib/htmlUtils.test.d.ts
packages/lib/htmlUtils.test.js
packages/lib/htmlUtils.test.js.map
packages/lib/htmlUtils2.test.d.ts
packages/lib/htmlUtils2.test.js
packages/lib/htmlUtils2.test.js.map
packages/lib/import-enex-md-gen.d.ts
packages/lib/import-enex-md-gen.js
packages/lib/import-enex-md-gen.js.map
packages/lib/import-enex-md-gen.test.d.ts
packages/lib/import-enex-md-gen.test.js
packages/lib/import-enex-md-gen.test.js.map
packages/lib/locale.d.ts
packages/lib/locale.js
packages/lib/locale.js.map
@@ -936,6 +876,9 @@ packages/lib/markdownUtils.js.map
packages/lib/markdownUtils.test.d.ts
packages/lib/markdownUtils.test.js
packages/lib/markdownUtils.test.js.map
packages/lib/markdownUtils2.test.d.ts
packages/lib/markdownUtils2.test.js
packages/lib/markdownUtils2.test.js.map
packages/lib/markupLanguageUtils.d.ts
packages/lib/markupLanguageUtils.js
packages/lib/markupLanguageUtils.js.map
@@ -948,6 +891,12 @@ packages/lib/models/BaseItem.js.map
packages/lib/models/Folder.d.ts
packages/lib/models/Folder.js
packages/lib/models/Folder.js.map
packages/lib/models/Folder.sharing.test.d.ts
packages/lib/models/Folder.sharing.test.js
packages/lib/models/Folder.sharing.test.js.map
packages/lib/models/Folder.test.d.ts
packages/lib/models/Folder.test.js
packages/lib/models/Folder.test.js.map
packages/lib/models/ItemChange.d.ts
packages/lib/models/ItemChange.js
packages/lib/models/ItemChange.js.map
@@ -960,6 +909,9 @@ packages/lib/models/Migration.js.map
packages/lib/models/Note.d.ts
packages/lib/models/Note.js
packages/lib/models/Note.js.map
packages/lib/models/Note.test.d.ts
packages/lib/models/Note.test.js
packages/lib/models/Note.test.js.map
packages/lib/models/NoteResource.d.ts
packages/lib/models/NoteResource.js
packages/lib/models/NoteResource.js.map
@@ -981,12 +933,18 @@ packages/lib/models/Search.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/Setting.test.d.ts
packages/lib/models/Setting.test.js
packages/lib/models/Setting.test.js.map
packages/lib/models/SmartFilter.d.ts
packages/lib/models/SmartFilter.js
packages/lib/models/SmartFilter.js.map
packages/lib/models/Tag.d.ts
packages/lib/models/Tag.js
packages/lib/models/Tag.js.map
packages/lib/models/dateTimeFormats.test.d.ts
packages/lib/models/dateTimeFormats.test.js
packages/lib/models/dateTimeFormats.test.js.map
packages/lib/models/settings/FileHandler.d.ts
packages/lib/models/settings/FileHandler.js
packages/lib/models/settings/FileHandler.js.map
@@ -1014,6 +972,9 @@ packages/lib/reducer.js.map
packages/lib/registry.d.ts
packages/lib/registry.js
packages/lib/registry.js.map
packages/lib/registry.test.d.ts
packages/lib/registry.test.js
packages/lib/registry.test.js.map
packages/lib/services/AlarmService.d.ts
packages/lib/services/AlarmService.js
packages/lib/services/AlarmService.js.map
@@ -1026,6 +987,9 @@ packages/lib/services/BaseService.js.map
packages/lib/services/CommandService.d.ts
packages/lib/services/CommandService.js
packages/lib/services/CommandService.js.map
packages/lib/services/CommandService.test.d.ts
packages/lib/services/CommandService.test.js
packages/lib/services/CommandService.test.js.map
packages/lib/services/DecryptionWorker.d.ts
packages/lib/services/DecryptionWorker.js
packages/lib/services/DecryptionWorker.js.map
@@ -1074,6 +1038,9 @@ packages/lib/services/ResourceFetcher.js.map
packages/lib/services/ResourceService.d.ts
packages/lib/services/ResourceService.js
packages/lib/services/ResourceService.js.map
packages/lib/services/ResourceService.test.d.ts
packages/lib/services/ResourceService.test.js
packages/lib/services/ResourceService.test.js.map
packages/lib/services/RevisionService.d.ts
packages/lib/services/RevisionService.js
packages/lib/services/RevisionService.js.map
@@ -1086,6 +1053,9 @@ packages/lib/services/UndoRedoService.js.map
packages/lib/services/WhenClause.d.ts
packages/lib/services/WhenClause.js
packages/lib/services/WhenClause.js.map
packages/lib/services/WhenClause.test.d.ts
packages/lib/services/WhenClause.test.js
packages/lib/services/WhenClause.test.js.map
packages/lib/services/commands/MenuUtils.d.ts
packages/lib/services/commands/MenuUtils.js
packages/lib/services/commands/MenuUtils.js.map
@@ -1119,6 +1089,9 @@ packages/lib/services/debug/populateDatabase.js.map
packages/lib/services/interop/InteropService.d.ts
packages/lib/services/interop/InteropService.js
packages/lib/services/interop/InteropService.js.map
packages/lib/services/interop/InteropService.test.d.ts
packages/lib/services/interop/InteropService.test.js
packages/lib/services/interop/InteropService.test.js.map
packages/lib/services/interop/InteropService_Exporter_Base.d.ts
packages/lib/services/interop/InteropService_Exporter_Base.js
packages/lib/services/interop/InteropService_Exporter_Base.js.map
@@ -1128,6 +1101,9 @@ packages/lib/services/interop/InteropService_Exporter_Custom.js.map
packages/lib/services/interop/InteropService_Exporter_Html.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.js
packages/lib/services/interop/InteropService_Exporter_Html.js.map
packages/lib/services/interop/InteropService_Exporter_Html.test.d.ts
packages/lib/services/interop/InteropService_Exporter_Html.test.js
packages/lib/services/interop/InteropService_Exporter_Html.test.js.map
packages/lib/services/interop/InteropService_Exporter_Jex.d.ts
packages/lib/services/interop/InteropService_Exporter_Jex.js
packages/lib/services/interop/InteropService_Exporter_Jex.js.map
@@ -1176,6 +1152,9 @@ packages/lib/services/keychain/KeychainServiceDriver.node.js.map
packages/lib/services/keychain/KeychainServiceDriverBase.d.ts
packages/lib/services/keychain/KeychainServiceDriverBase.js
packages/lib/services/keychain/KeychainServiceDriverBase.js.map
packages/lib/services/keychain/keychainService.test.d.ts
packages/lib/services/keychain/keychainService.test.js
packages/lib/services/keychain/keychainService.test.js.map
packages/lib/services/plugins/BasePluginRunner.d.ts
packages/lib/services/plugins/BasePluginRunner.js
packages/lib/services/plugins/BasePluginRunner.js.map
@@ -1290,6 +1269,9 @@ packages/lib/services/plugins/utils/validatePluginId.test.js.map
packages/lib/services/rest/Api.d.ts
packages/lib/services/rest/Api.js
packages/lib/services/rest/Api.js.map
packages/lib/services/rest/Api.test.d.ts
packages/lib/services/rest/Api.test.js
packages/lib/services/rest/Api.test.js.map
packages/lib/services/rest/ApiResponse.d.ts
packages/lib/services/rest/ApiResponse.js
packages/lib/services/rest/ApiResponse.js.map
@@ -1350,6 +1332,9 @@ packages/lib/services/searchengine/SearchEngine.js.map
packages/lib/services/searchengine/SearchEngineUtils.d.ts
packages/lib/services/searchengine/SearchEngineUtils.js
packages/lib/services/searchengine/SearchEngineUtils.js.map
packages/lib/services/searchengine/SearchEngineUtils.test.d.ts
packages/lib/services/searchengine/SearchEngineUtils.test.js
packages/lib/services/searchengine/SearchEngineUtils.test.js.map
packages/lib/services/searchengine/filterParser.d.ts
packages/lib/services/searchengine/filterParser.js
packages/lib/services/searchengine/filterParser.js.map
@@ -1374,6 +1359,30 @@ packages/lib/services/synchronizer/LockHandler.js.map
packages/lib/services/synchronizer/MigrationHandler.d.ts
packages/lib/services/synchronizer/MigrationHandler.js
packages/lib/services/synchronizer/MigrationHandler.js.map
packages/lib/services/synchronizer/Synchronizer.basics.test.d.ts
packages/lib/services/synchronizer/Synchronizer.basics.test.js
packages/lib/services/synchronizer/Synchronizer.basics.test.js.map
packages/lib/services/synchronizer/Synchronizer.conflicts.test.d.ts
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js
packages/lib/services/synchronizer/Synchronizer.conflicts.test.js.map
packages/lib/services/synchronizer/Synchronizer.e2ee.test.d.ts
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js
packages/lib/services/synchronizer/Synchronizer.e2ee.test.js.map
packages/lib/services/synchronizer/Synchronizer.resources.test.d.ts
packages/lib/services/synchronizer/Synchronizer.resources.test.js
packages/lib/services/synchronizer/Synchronizer.resources.test.js.map
packages/lib/services/synchronizer/Synchronizer.revisions.test.d.ts
packages/lib/services/synchronizer/Synchronizer.revisions.test.js
packages/lib/services/synchronizer/Synchronizer.revisions.test.js.map
packages/lib/services/synchronizer/Synchronizer.sharing.test.d.ts
packages/lib/services/synchronizer/Synchronizer.sharing.test.js
packages/lib/services/synchronizer/Synchronizer.sharing.test.js.map
packages/lib/services/synchronizer/Synchronizer.tags.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tags.test.js
packages/lib/services/synchronizer/Synchronizer.tags.test.js.map
packages/lib/services/synchronizer/Synchronizer.tools.test.d.ts
packages/lib/services/synchronizer/Synchronizer.tools.test.js
packages/lib/services/synchronizer/Synchronizer.tools.test.js.map
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.d.ts
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
packages/lib/services/synchronizer/gui/useSyncTargetUpgrade.js.map
@@ -1383,6 +1392,12 @@ packages/lib/services/synchronizer/migrations/1.js.map
packages/lib/services/synchronizer/migrations/2.d.ts
packages/lib/services/synchronizer/migrations/2.js
packages/lib/services/synchronizer/migrations/2.js.map
packages/lib/services/synchronizer/synchronizer_LockHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js
packages/lib/services/synchronizer/synchronizer_LockHandler.test.js.map
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.d.ts
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js
packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.js.map
packages/lib/services/synchronizer/tools.d.ts
packages/lib/services/synchronizer/tools.js
packages/lib/services/synchronizer/tools.js.map
@@ -1395,6 +1410,12 @@ packages/lib/services/synchronizer/utils/types.js.map
packages/lib/shim.d.ts
packages/lib/shim.js
packages/lib/shim.js.map
packages/lib/testing/test-utils-synchronizer.d.ts
packages/lib/testing/test-utils-synchronizer.js
packages/lib/testing/test-utils-synchronizer.js.map
packages/lib/testing/test-utils.d.ts
packages/lib/testing/test-utils.js
packages/lib/testing/test-utils.js.map
packages/lib/theme.d.ts
packages/lib/theme.js
packages/lib/theme.js.map
@@ -1428,6 +1449,9 @@ packages/lib/themes/type.js.map
packages/lib/time.d.ts
packages/lib/time.js
packages/lib/time.js.map
packages/lib/utils/credentialFiles.d.ts
packages/lib/utils/credentialFiles.js
packages/lib/utils/credentialFiles.js.map
packages/lib/uuid.d.ts
packages/lib/uuid.js
packages/lib/uuid.js.map
@@ -1554,6 +1578,9 @@ packages/tools/generate-database-types.js.map
packages/tools/lerna-add.d.ts
packages/tools/lerna-add.js
packages/tools/lerna-add.js.map
packages/tools/release-android.d.ts
packages/tools/release-android.js
packages/tools/release-android.js.map
packages/tools/release-cli.d.ts
packages/tools/release-cli.js
packages/tools/release-cli.js.map
+40 -40
View File
@@ -292,7 +292,7 @@ To add a **Bucket Policy** from the AWS S3 Web Console, navigate to the **Permis
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObject"
]
],
"Resource": [
"arn:aws:s3:::joplin-bucket",
"arn:aws:s3:::joplin-bucket/*"
@@ -342,7 +342,7 @@ In the desktop and mobile apps, an alarm can be associated with any to-do. It wi
- **Windows**: >= 8. Make sure the Action Center is enabled on Windows. Task bar balloon for Windows < 8. Growl as fallback. Growl takes precedence over Windows balloons.
- **macOS**: >= 10.8 or Growl if earlier.
- **Linux**: `notify-osd` or `libnotify-bin` installed (Ubuntu should have this by default). Growl otherwise
- **Linux**: `notify-send` tool, delivered through packages `notify-osd`, `libnotify-bin` or `libnotify-tools`. GNOME should have this by default, but install `libnotify-tools` if using KDE Plasma.
See [documentation and flow chart for reporter choice](https://github.com/mikaelbr/node-notifier/blob/master/DECISION_FLOW.md)
@@ -511,47 +511,47 @@ Current translations:
<!-- LOCALE-TABLE-AUTO-GENERATED -->
&nbsp; | Language | Po File | Last translator | Percent done
---|---|---|---|---
![](https://joplinapp.org/images/flags/country-4x3/arableague.png) | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 98%
![](https://joplinapp.org/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 31%
![](https://joplinapp.org/images/flags/country-4x3/ba.png) | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 76%
![](https://joplinapp.org/images/flags/country-4x3/bg.png) | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 59%
![](https://joplinapp.org/images/flags/es/catalonia.png) | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | jmontane, 2019 | 84%
![](https://joplinapp.org/images/flags/country-4x3/hr.png) | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 99%
![](https://joplinapp.org/images/flags/country-4x3/cz.png) | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Lukas Helebrandt](mailto:lukas@aiya.cz) | 88%
![](https://joplinapp.org/images/flags/country-4x3/dk.png) | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | Mustafa Al-Dailemi (dailemi@hotmail.com)Language-Team: | 98%
![](https://joplinapp.org/images/flags/country-4x3/de.png) | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Atalanttore](mailto:atalanttore@googlemail.com) | 98%
![](https://joplinapp.org/images/flags/country-4x3/ee.png) | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 58%
![](https://joplinapp.org/images/flags/country-4x3/arableague.png) | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 96%
![](https://joplinapp.org/images/flags/es/basque_country.png) | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 30%
![](https://joplinapp.org/images/flags/country-4x3/ba.png) | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 75%
![](https://joplinapp.org/images/flags/country-4x3/bg.png) | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 58%
![](https://joplinapp.org/images/flags/es/catalonia.png) | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | jmontane, 2019 | 83%
![](https://joplinapp.org/images/flags/country-4x3/hr.png) | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 96%
![](https://joplinapp.org/images/flags/country-4x3/cz.png) | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Lukas Helebrandt](mailto:lukas@aiya.cz) | 86%
![](https://joplinapp.org/images/flags/country-4x3/dk.png) | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | Mustafa Al-Dailemi (dailemi@hotmail.com)Language-Team: | 96%
![](https://joplinapp.org/images/flags/country-4x3/de.png) | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [Atalanttore](mailto:atalanttore@googlemail.com) | 95%
![](https://joplinapp.org/images/flags/country-4x3/ee.png) | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 57%
![](https://joplinapp.org/images/flags/country-4x3/gb.png) | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
![](https://joplinapp.org/images/flags/country-4x3/us.png) | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
![](https://joplinapp.org/images/flags/country-4x3/es.png) | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Mario Campo](mailto:mario.campo@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/es.png) | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Mario Campo](mailto:mario.campo@gmail.com) | 94%
![](https://joplinapp.org/images/flags/esperanto.png) | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 33%
![](https://joplinapp.org/images/flags/country-4x3/fi.png) | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 97%
![](https://joplinapp.org/images/flags/country-4x3/fr.png) | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 94%
![](https://joplinapp.org/images/flags/es/galicia.png) | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 39%
![](https://joplinapp.org/images/flags/country-4x3/id.png) | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [eresytter](mailto:42007357+eresytter@users.noreply.github.com) | 95%
![](https://joplinapp.org/images/flags/country-4x3/it.png) | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Alessandro Bernardello](mailto:mailfilledwithspam@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/hu.png) | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Szőke Sándor](mailto:mail@szokesandor.hu) | 90%
![](https://joplinapp.org/images/flags/country-4x3/be.png) | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 94%
![](https://joplinapp.org/images/flags/country-4x3/nl.png) | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/no.png) | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 78%
![](https://joplinapp.org/images/flags/country-4x3/ir.png) | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 73%
![](https://joplinapp.org/images/flags/country-4x3/pl.png) | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Nicolas Suzuki](mailto:nicolas.suzuki@pm.me) | 97%
![](https://joplinapp.org/images/flags/country-4x3/pt.png) | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/ro.png) | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 68%
![](https://joplinapp.org/images/flags/country-4x3/si.png) | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 98%
![](https://joplinapp.org/images/flags/country-4x3/se.png) | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 63%
![](https://joplinapp.org/images/flags/country-4x3/th.png) | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 46%
![](https://joplinapp.org/images/flags/country-4x3/vi.png) | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 75%
![](https://joplinapp.org/images/flags/country-4x3/tr.png) | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/ua.png) | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/gr.png) | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 84%
![](https://joplinapp.org/images/flags/country-4x3/ru.png) | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/rs.png) | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 73%
![](https://joplinapp.org/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [Yang Zhang](mailto:zyangmath@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/tw.png) | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Yaoze Ye](mailto:yaozeye@yahoo.co.jp) | 95%
![](https://joplinapp.org/images/flags/country-4x3/jp.png) | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 98%
![](https://joplinapp.org/images/flags/country-4x3/kr.png) | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 99%
![](https://joplinapp.org/images/flags/country-4x3/fi.png) | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato | 94%
![](https://joplinapp.org/images/flags/country-4x3/fr.png) | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 99%
![](https://joplinapp.org/images/flags/es/galicia.png) | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 38%
![](https://joplinapp.org/images/flags/country-4x3/id.png) | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [eresytter](mailto:42007357+eresytter@users.noreply.github.com) | 93%
![](https://joplinapp.org/images/flags/country-4x3/it.png) | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Alessandro Bernardello](mailto:mailfilledwithspam@gmail.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/hu.png) | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Szőke Sándor](mailto:mail@szokesandor.hu) | 88%
![](https://joplinapp.org/images/flags/country-4x3/be.png) | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 92%
![](https://joplinapp.org/images/flags/country-4x3/nl.png) | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MetBril](mailto:metbril@users.noreply.github.com) | 95%
![](https://joplinapp.org/images/flags/country-4x3/no.png) | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 76%
![](https://joplinapp.org/images/flags/country-4x3/ir.png) | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 71%
![](https://joplinapp.org/images/flags/country-4x3/pl.png) | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [konhi](mailto:hello.konhi@gmail.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/br.png) | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Nicolas Suzuki](mailto:nicolas.suzuki@pm.me) | 94%
![](https://joplinapp.org/images/flags/country-4x3/pt.png) | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/ro.png) | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 66%
![](https://joplinapp.org/images/flags/country-4x3/si.png) | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 96%
![](https://joplinapp.org/images/flags/country-4x3/se.png) | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 61%
![](https://joplinapp.org/images/flags/country-4x3/th.png) | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 45%
![](https://joplinapp.org/images/flags/country-4x3/vi.png) | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 73%
![](https://joplinapp.org/images/flags/country-4x3/tr.png) | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/ua.png) | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/gr.png) | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 97%
![](https://joplinapp.org/images/flags/country-4x3/ru.png) | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/rs.png) | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 71%
![](https://joplinapp.org/images/flags/country-4x3/cn.png) | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [Yang Zhang](mailto:zyangmath@gmail.com) | 94%
![](https://joplinapp.org/images/flags/country-4x3/tw.png) | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Yaoze Ye](mailto:yaozeye@yahoo.co.jp) | 92%
![](https://joplinapp.org/images/flags/country-4x3/jp.png) | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 97%
![](https://joplinapp.org/images/flags/country-4x3/kr.png) | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 96%
<!-- LOCALE-TABLE-AUTO-GENERATED -->
# Contributors
+14 -1
View File
@@ -1,4 +1,17 @@
const { afterEachCleanUp } = require('./tests/test-utils.js');
const { afterEachCleanUp } = require('@joplin/lib/testing/test-utils.js');
const { shimInit } = require('@joplin/lib/shim-init-node.js');
const shim = require('@joplin/lib/shim').default;
const sharp = require('sharp');
let keytar;
try {
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
} catch (error) {
console.error('Cannot load keytar - keychain support will be disabled', error);
keytar = null;
}
shimInit(sharp, keytar);
global.afterEach(async () => {
await afterEachCleanUp();
+1 -2
View File
@@ -5,7 +5,7 @@
"author": "Laurent Cozic",
"private": true,
"scripts": {
"test": "jest --config=jest.config.js --bail --forceExit",
"test": "jest --verbose=false --config=jest.config.js --bail --forceExit",
"test-one": "jest --verbose=false --config=jest.config.js --bail --forceExit",
"test-ci": "jest --config=jest.config.js --forceExit",
"build": "gulp build",
@@ -43,7 +43,6 @@
"@joplin/renderer": "1.8",
"aws-sdk": "^2.588.0",
"chalk": "^4.1.0",
"clean-html": "^1.5.0",
"compare-version": "^0.1.2",
"fs-extra": "^5.0.0",
"html-entities": "^1.2.1",
-116
View File
@@ -1,116 +0,0 @@
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const shim = require('@joplin/lib/shim').default;
const { enexXmlToHtml } = require('@joplin/lib/import-enex-html-gen.js');
const cleanHtml = require('clean-html');
const fileWithPath = (filename) =>
`${__dirname}/enex_to_html/${filename}`;
const audioResource = {
filename: 'audio test',
id: '9168ee833d03c5ea7c730ac6673978c1',
mime: 'audio/x-m4a',
size: 82011,
title: 'audio test',
};
// All the test HTML files are beautified ones, so we need to run
// this before the comparison. Before, beautifying was done by `enexXmlToHtml`
// but that was removed due to problems with the clean-html package.
const beautifyHtml = (html) => {
return new Promise((resolve) => {
try {
cleanHtml.clean(html, { wrap: 0 }, (...cleanedHtml) => resolve(cleanedHtml.join('')));
} catch (error) {
console.warn(`Could not clean HTML - the "unclean" version will be used: ${error.message}: ${html.trim().substr(0, 512).replace(/[\n\r]/g, ' ')}...`);
resolve([html].join(''));
}
});
};
/**
* Tests the importer for a single note, checking that the result of
* processing the given `.enex` input file matches the contents of the given
* `.html` file.
*
* Note that this does not test the importing of an entire exported `.enex`
* archive, but rather a single node of such a file. Thus, the test data files
* (e.g. `./enex_to_html/code1.enex`) correspond to the contents of a single
* `<note>...</note>` node in an `.enex` file already extracted from
* `<content><![CDATA[...]]</content>`.
*/
const compareOutputToExpected = (options) => {
const inputFile = fileWithPath(`${options.testName}.enex`);
const outputFile = fileWithPath(`${options.testName}.html`);
const testTitle = `should convert from Enex to Html: ${options.testName}`;
it(testTitle, (async () => {
const enexInput = await shim.fsDriver().readFile(inputFile);
const expectedOutput = await shim.fsDriver().readFile(outputFile);
const actualOutput = await beautifyHtml(await enexXmlToHtml(enexInput, options.resources));
expect(actualOutput).toEqual(expectedOutput);
}));
};
describe('EnexToHtml', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
compareOutputToExpected({
testName: 'checklist-list',
resources: [],
});
compareOutputToExpected({
testName: 'svg',
resources: [],
});
compareOutputToExpected({
testName: 'en-media--image',
resources: [{
filename: '',
id: '89ce7da62c6b2832929a6964237e98e9', // Mock id
mime: 'image/jpeg',
size: 50347,
title: '',
}],
});
compareOutputToExpected({
testName: 'en-media--audio',
resources: [audioResource],
});
compareOutputToExpected({
testName: 'attachment',
resources: [{
filename: 'attachment-1',
id: '21ca2b948f222a38802940ec7e2e5de3',
mime: 'application/pdf', // Any non-image/non-audio mime type will do
size: 1000,
}],
});
// it('fails when not given a matching resource', (async () => {
// // To test the promise-unexpectedly-resolved case, add `audioResource` to the array.
// const resources = [];
// const inputFile = fileWithPath('en-media--image.enex');
// const enexInput = await shim.fsDriver().readFile(inputFile);
// const promisedOutput = enexXmlToHtml(enexInput, resources);
// promisedOutput.then(() => {
// // Promise should not be resolved
// expect(false).toEqual(true);
// }, (reason) => {
// expect(reason)
// .toBe('Hash with no associated resource: 89ce7da62c6b2832929a6964237e98e9');
// });
// }));
});
+1 -1
View File
@@ -4,7 +4,7 @@
const os = require('os');
const time = require('@joplin/lib/time').default;
const { filename } = require('@joplin/lib/path-utils');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('@joplin/lib/testing/test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
+9 -1
View File
@@ -1,7 +1,7 @@
import MdToHtml from '@joplin/renderer/MdToHtml';
const os = require('os');
const { filename } = require('@joplin/lib/path-utils');
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
import shim from '@joplin/lib/shim';
const { themeStyle } = require('@joplin/lib/theme');
@@ -137,6 +137,14 @@ describe('MdToHtml', function() {
}
}));
it('should render an empty string', (async () => {
const mdToHtml = newTestMdToHtml();
const result = await mdToHtml.render('', null, { splitted: true });
// The TinyMCE component checks for this exact string to apply a hack,
// so make sure it doesn't change from version to version.
expect(result.html).toBe('<div id="rendered-md"></div>');
}));
it('should split HTML and CSS', (async () => {
const mdToHtml = newTestMdToHtml();
@@ -1,6 +1,6 @@
const mdImporterService = require('@joplin/lib/services/interop/InteropService_Importer_Md').default;
const Note = require('@joplin/lib/models/Note').default;
const { setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
const importer = new mdImporterService();
@@ -43,4 +43,9 @@ describe('InteropService_Importer_Md: importLocalImages', function() {
const items = await Note.linkedItems(note.body);
expect(items.length).toBe(1);
});
it('should import resources for files', async function() {
const note = await importer.importFile(`${__dirname}/md_to_md/sample-files.md`, 'notebook');
const items = await Note.linkedItems(note.body);
expect(items.length).toBe(4);
});
});
@@ -1,290 +0,0 @@
'use strict';
const __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); }
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator['throw'](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, '__esModule', { value: true });
const Setting_1 = require('@joplin/lib/models/Setting');
const test_utils_synchronizer_1 = require('./test-utils-synchronizer');
const { syncTargetName, afterAllCleanUp, synchronizerStart, setupDatabaseAndSynchronizer, synchronizer, sleep, switchClient, syncTargetId, fileApi } = require('./test-utils.js');
const Folder_1 = require('@joplin/lib/models/Folder');
const Note_1 = require('@joplin/lib/models/Note');
const BaseItem_1 = require('@joplin/lib/models/BaseItem');
const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
describe('Synchronizer.basics', function() {
beforeEach((done) => __awaiter(this, void 0, void 0, function* () {
yield setupDatabaseAndSynchronizer(1);
yield setupDatabaseAndSynchronizer(2);
yield switchClient(1);
done();
}));
afterAll(() => __awaiter(this, void 0, void 0, function* () {
yield afterAllCleanUp();
}));
it('should create remote items', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', parent_id: folder.id });
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield synchronizerStart();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should update remote items', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
const note = yield Note_1.default.save({ title: 'un', parent_id: folder.id });
yield synchronizerStart();
yield Note_1.default.save({ title: 'un UPDATE', id: note.id });
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield synchronizerStart();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should create local items', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', parent_id: folder.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should update local items', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
let note2 = yield Note_1.default.load(note1.id);
note2.title = 'Updated on client 2';
yield Note_1.default.save(note2);
note2 = yield Note_1.default.load(note2.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should delete remote notes', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
yield Note_1.default.delete(note1.id);
yield synchronizerStart();
const remotes = yield test_utils_synchronizer_1.remoteNotesAndFolders();
expect(remotes.length).toBe(1);
expect(remotes[0].id).toBe(folder1.id);
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
})));
it('should not created deleted_items entries for items deleted via sync', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Folder_1.default.delete(folder1.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
})));
it('should delete local notes', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', parent_id: folder1.id });
const note2 = yield Note_1.default.save({ title: 'deux', parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Note_1.default.delete(note1.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const items = yield test_utils_synchronizer_1.allNotesFolders();
expect(items.length).toBe(2);
const deletedItems = yield BaseItem_1.default.deletedItems(syncTargetId());
expect(deletedItems.length).toBe(0);
yield Note_1.default.delete(note2.id);
yield synchronizerStart();
})));
it('should delete remote folder', (() => __awaiter(this, void 0, void 0, function* () {
yield Folder_1.default.save({ title: 'folder1' });
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
yield Folder_1.default.delete(folder2.id);
yield synchronizerStart();
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should delete local folder', (() => __awaiter(this, void 0, void 0, function* () {
yield Folder_1.default.save({ title: 'folder1' });
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Folder_1.default.delete(folder2.id);
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const items = yield test_utils_synchronizer_1.allNotesFolders();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(items, expect);
})));
it('should cross delete all folders', (() => __awaiter(this, void 0, void 0, function* () {
// If client1 and 2 have two folders, client 1 deletes item 1 and client
// 2 deletes item 2, they should both end up with no items after sync.
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const folder2 = yield Folder_1.default.save({ title: 'folder2' });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield sleep(0.1);
yield Folder_1.default.delete(folder1.id);
yield switchClient(1);
yield Folder_1.default.delete(folder2.id);
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
const items2 = yield test_utils_synchronizer_1.allNotesFolders();
yield switchClient(1);
yield synchronizerStart();
const items1 = yield test_utils_synchronizer_1.allNotesFolders();
expect(items1.length).toBe(0);
expect(items1.length).toBe(items2.length);
})));
it('items should be downloaded again when user cancels in the middle of delta operation', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
yield Note_1.default.save({ title: 'un', is_todo: 1, parent_id: folder1.id });
yield synchronizerStart();
yield switchClient(2);
synchronizer().testingHooks_ = ['cancelDeltaLoop2'];
yield synchronizerStart();
let notes = yield Note_1.default.all();
expect(notes.length).toBe(0);
synchronizer().testingHooks_ = [];
yield synchronizerStart();
notes = yield Note_1.default.all();
expect(notes.length).toBe(1);
})));
it('should skip items that cannot be synced', (() => __awaiter(this, void 0, void 0, function* () {
const folder1 = yield Folder_1.default.save({ title: 'folder1' });
const note1 = yield Note_1.default.save({ title: 'un', is_todo: 1, parent_id: folder1.id });
const noteId = note1.id;
yield synchronizerStart();
let disabledItems = yield BaseItem_1.default.syncDisabledItems(syncTargetId());
expect(disabledItems.length).toBe(0);
yield Note_1.default.save({ id: noteId, title: 'un mod' });
synchronizer().testingHooks_ = ['notesRejectedByTarget'];
yield synchronizerStart();
synchronizer().testingHooks_ = [];
yield synchronizerStart(); // Another sync to check that this item is now excluded from sync
yield switchClient(2);
yield synchronizerStart();
const notes = yield Note_1.default.all();
expect(notes.length).toBe(1);
expect(notes[0].title).toBe('un');
yield switchClient(1);
disabledItems = yield BaseItem_1.default.syncDisabledItems(syncTargetId());
expect(disabledItems.length).toBe(1);
})));
it('should allow duplicate folder titles', (() => __awaiter(this, void 0, void 0, function* () {
yield Folder_1.default.save({ title: 'folder' });
yield switchClient(2);
let remoteF2 = yield Folder_1.default.save({ title: 'folder' });
yield synchronizerStart();
yield switchClient(1);
yield sleep(0.1);
yield synchronizerStart();
const localF2 = yield Folder_1.default.load(remoteF2.id);
expect(localF2.title == remoteF2.title).toBe(true);
// Then that folder that has been renamed locally should be set in such a way
// that synchronizing it applies the title change remotely, and that new title
// should be retrieved by client 2.
yield synchronizerStart();
yield switchClient(2);
yield sleep(0.1);
yield synchronizerStart();
remoteF2 = yield Folder_1.default.load(remoteF2.id);
expect(remoteF2.title == localF2.title).toBe(true);
})));
it('should create remote items with UTF-8 content', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'Fahrräder' });
yield Note_1.default.save({ title: 'Fahrräder', body: 'Fahrräder', parent_id: folder.id });
const all = yield test_utils_synchronizer_1.allNotesFolders();
yield synchronizerStart();
yield test_utils_synchronizer_1.localNotesFoldersSameAsRemote(all, expect);
})));
it('should update remote items but not pull remote changes', (() => __awaiter(this, void 0, void 0, function* () {
const folder = yield Folder_1.default.save({ title: 'folder1' });
const note = yield Note_1.default.save({ title: 'un', parent_id: folder.id });
yield synchronizerStart();
yield switchClient(2);
yield synchronizerStart();
yield Note_1.default.save({ title: 'deux', parent_id: folder.id });
yield synchronizerStart();
yield switchClient(1);
yield Note_1.default.save({ title: 'un UPDATE', id: note.id });
yield synchronizerStart(null, { syncSteps: ['update_remote'] });
const all = yield test_utils_synchronizer_1.allNotesFolders();
expect(all.length).toBe(2);
yield switchClient(2);
yield synchronizerStart();
const note2 = yield Note_1.default.load(note.id);
expect(note2.title).toBe('un UPDATE');
})));
it('should create a new Welcome notebook on each client', (() => __awaiter(this, void 0, void 0, function* () {
// Create the Welcome items on two separate clients
yield WelcomeUtils.createWelcomeItems();
yield synchronizerStart();
yield switchClient(2);
yield WelcomeUtils.createWelcomeItems();
const beforeFolderCount = (yield Folder_1.default.all()).length;
const beforeNoteCount = (yield Note_1.default.all()).length;
expect(beforeFolderCount === 1).toBe(true);
expect(beforeNoteCount > 1).toBe(true);
yield synchronizerStart();
const afterFolderCount = (yield Folder_1.default.all()).length;
const afterNoteCount = (yield Note_1.default.all()).length;
expect(afterFolderCount).toBe(beforeFolderCount * 2);
expect(afterNoteCount).toBe(beforeNoteCount * 2);
// Changes to the Welcome items should be synced to all clients
const f1 = (yield Folder_1.default.all())[0];
yield Folder_1.default.save({ id: f1.id, title: 'Welcome MOD' });
yield synchronizerStart();
yield switchClient(1);
yield synchronizerStart();
const f1_1 = yield Folder_1.default.load(f1.id);
expect(f1_1.title).toBe('Welcome MOD');
})));
it('should not wipe out user data when syncing with an empty target', (() => __awaiter(this, void 0, void 0, function* () {
// Only these targets support the wipeOutFailSafe flag (in other words, the targets that use basicDelta)
if (!['nextcloud', 'memory', 'filesystem', 'amazon_s3'].includes(syncTargetName())) { return; }
for (let i = 0; i < 10; i++) { yield Note_1.default.save({ title: 'note' }); }
Setting_1.default.setValue('sync.wipeOutFailSafe', true);
yield synchronizerStart();
yield fileApi().clearRoot(); // oops
yield synchronizerStart();
expect((yield Note_1.default.all()).length).toBe(10); // but since the fail-safe if on, the notes have not been deleted
Setting_1.default.setValue('sync.wipeOutFailSafe', false); // Now switch it off
yield synchronizerStart();
expect((yield Note_1.default.all()).length).toBe(0); // Since the fail-safe was off, the data has been cleared
// Handle case where the sync target has been wiped out, then the user creates one note and sync.
for (let i = 0; i < 10; i++) { yield Note_1.default.save({ title: 'note' }); }
Setting_1.default.setValue('sync.wipeOutFailSafe', true);
yield synchronizerStart();
yield fileApi().clearRoot();
yield Note_1.default.save({ title: 'ma note encore' });
yield synchronizerStart();
expect((yield Note_1.default.all()).length).toBe(11);
})));
});
// # sourceMappingURL=Synchronizer.share.js.map
-31
View File
@@ -1,31 +0,0 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { sortedIds, createNTestNotes, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
const Setting = require('@joplin/lib/models/Setting').default;
const BaseModel = require('@joplin/lib/BaseModel').default;
const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
describe('database', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should not modify cached field names', (async () => {
const db = BaseModel.db();
const fieldNames = db.tableFieldNames('notes');
const fieldCount = fieldNames.length;
fieldNames.push('type_');
expect(fieldCount).toBeGreaterThan(0);
expect(db.tableFieldNames('notes').length).toBe(fieldCount);
}));
});
+11 -9
View File
@@ -1,14 +1,16 @@
For example, consider a web page like this:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
```
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
<script src="page-scripts/page-script.js"></script>
</body>
</html>
<body>
<script src="page-scripts/page-script.js"></script>
</body>
</html>
```
The script "page-script.js" does this:
+7 -5
View File
@@ -1,7 +1,9 @@
Subshell:
(
set -e
false
echo Unreachable
) && echo Great success
```
(
set -e
false
echo Unreachable
) && echo Great success
```
+1 -1
View File
@@ -1 +1 @@
jq -r '.[]|[.index, .name, .section, .award, .industry]|join("\t")' raw.json |pbcopy
`jq -r '.[]|[.index, .name, .section, .award, .industry]|join("\t")' raw.json |pbcopy`
@@ -0,0 +1 @@
<div><div>code block:</div><div><br/></div><div style="box-sizing: border-box; padding: 8px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 12px; color: rgb(51, 51, 51); border-radius: 4px; background-color: rgb(251, 250, 248); border: 1px solid rgba(0, 0, 0, 0.15);-en-codeblock:true;"><div>public static void main(String[] args) {</div><div><span>&nbsp; &nbsp; System.out.println('Hello World');</span><br/></div><div>}</div></div><div><br/></div><div>end of code block</div></div>
@@ -0,0 +1,9 @@
code block:
```
public static void main(String[] args) {
    System.out.println('Hello World');
}
```
end of code block
@@ -1,4 +1,4 @@
const { id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('./test-utils.js');
const { id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('@joplin/lib/testing/test-utils.js');
const BaseModel = require('@joplin/lib/BaseModel').default;
const uuid = require('@joplin/lib/uuid').default;
const Note = require('@joplin/lib/models/Note').default;
+1 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
@@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
+1 -1
View File
@@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('./test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('@joplin/lib/testing/test-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const Folder = require('@joplin/lib/models/Folder').default;
const Note = require('@joplin/lib/models/Note').default;
@@ -0,0 +1,9 @@
# Markdown file test
![../support/photo.jpg](../support/photo.jpg)
[welcome.pdf](../support/welcome.pdf)
[sample.md](sample.md)
[sample2.md](./sample.md)
@@ -1,4 +1,4 @@
import PluginRunner from '../app/services/plugins/PluginRunner';
import PluginRunner from '../../../app/services/plugins/PluginRunner';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import { ContentScriptType } from '@joplin/lib/services/plugins/api/types';
import MdToHtml from '@joplin/renderer/MdToHtml';
@@ -7,10 +7,10 @@ import Setting from '@joplin/lib/models/Setting';
import * as fs from 'fs-extra';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import { newPluginScript } from './test-utils';
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir } from './test-utils.js';
import { newPluginScript } from '@joplin/lib/testing/test-utils';
import { expectNotThrow, setupDatabaseAndSynchronizer, switchClient, expectThrow, createTempDir, supportDir } from '@joplin/lib/testing/test-utils';
const testPluginDir = `${__dirname}/../tests/support/plugins`;
const testPluginDir = `${supportDir}/plugins`;
function newPluginService(appVersion: string = '1.4') {
const runner = new PluginRunner();
@@ -102,7 +102,7 @@ describe('services_PluginService', function() {
"homepage_url": "https://joplinapp.org"
}
*/
joplin.plugins.register({
onStart: async function() {
await joplin.data.post(['folders'], null, { title: "my plugin folder" });
@@ -141,14 +141,14 @@ describe('services_PluginService', function() {
"not_a_valid_manifest_at_all": 1
}
*/
joplin.plugins.register({
onStart: async function() {},
});
`, `
/* joplin-manifest:
*/
joplin.plugins.register({
onStart: async function() {},
});
@@ -189,7 +189,7 @@ describe('services_PluginService', function() {
"homepage_url": "https://joplinapp.org"
}
*/
joplin.plugins.register({
onStart: async function() {
await joplin.contentScripts.register('markdownItPlugin', 'justtesting', './markdownItTestPlugin.js');
@@ -236,7 +236,7 @@ describe('services_PluginService', function() {
"version": "1.0.0"
}
*/
joplin.plugins.register({
onStart: async function() { },
});
@@ -283,7 +283,7 @@ describe('services_PluginService', function() {
}));
it('should create the data directory', (async () => {
const pluginScript = newPluginScript(`
const pluginScript = newPluginScript(`
joplin.plugins.register({
onStart: async function() {
const dataDir = await joplin.plugins.dataDir();
@@ -1,6 +1,6 @@
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import shim from '@joplin/lib/shim';
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '../../test-utils';
import { setupDatabaseAndSynchronizer, switchClient, supportDir, createTempDir } from '@joplin/lib/testing/test-utils';
async function newRepoApi(): Promise<RepositoryApi> {
const repo = new RepositoryApi(`${supportDir}/pluginRepo`, await createTempDir());
@@ -1,6 +1,6 @@
import Setting from '@joplin/lib/models/Setting';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const { waitForFolderCount, newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
const { waitForFolderCount, newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('@joplin/lib/testing/test-utils');
import Folder from '@joplin/lib/models/Folder';
describe('JoplinSettings', () => {
@@ -1,6 +1,6 @@
import KeymapService from '@joplin/lib/services/KeymapService';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('../../../test-utils');
const { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } = require('@joplin/lib/testing/test-utils');
describe('JoplinViewMenuItem', () => {
@@ -1,5 +1,5 @@
import Setting from '@joplin/lib/models/Setting';
import { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '../../../test-utils';
import { newPluginService, newPluginScript, setupDatabaseAndSynchronizer, switchClient, afterEachCleanUp } from '@joplin/lib/testing/test-utils';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
import ItemChange from '@joplin/lib/models/ItemChange';
@@ -1,6 +1,6 @@
import sandboxProxy, { Target } from '@joplin/lib/services/plugins/sandboxProxy';
const { setupDatabaseAndSynchronizer, switchClient } = require('../../test-utils.js');
const { setupDatabaseAndSynchronizer, switchClient } = require('@joplin/lib/testing/test-utils.js');
describe('services_plugins_sandboxProxy', function() {
@@ -1,4 +1,4 @@
const {main} = require('./syncTargetUtils');
const {main} = require('@joplin/lib/testing/syncTargetUtils');
const syncTargetType = process.argv.length <= 2 ? 'normal' : process.argv[2];
@@ -166,6 +166,7 @@ export default class InteropServiceHelper {
exportOptions.format = module.format;
// exportOptions.modulePath = module.path;
if (options.plugins) exportOptions.plugins = options.plugins;
exportOptions.customCss = options.customCss;
exportOptions.target = module.target;
exportOptions.includeConflicts = !!options.includeConflicts;
if (options.sourceFolderIds) exportOptions.sourceFolderIds = options.sourceFolderIds;
@@ -13,8 +13,13 @@ import { PluginItem } from './PluginBox';
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import Setting from '@joplin/lib/models/Setting';
import useOnInstallHandler, { OnPluginSettingChangeEvent } from './useOnInstallHandler';
import Logger from '@joplin/lib/Logger';
import StyledMessage from '../../../style/StyledMessage';
import StyledLink from '../../../style/StyledLink';
const { space } = require('styled-system');
const logger = Logger.create('PluginState');
const maxWidth: number = 320;
const Root = styled.div`
@@ -32,6 +37,11 @@ const ToolsButton = styled(Button)`
margin-right: 6px;
`;
const RepoApiErrorMessage = styled(StyledMessage)`
max-width: ${props => props.maxWidth}px;
margin-bottom: 10px;
`;
interface Props {
value: any;
themeId: number;
@@ -84,6 +94,8 @@ export default function(props: Props) {
const [manifestsLoaded, setManifestsLoaded] = useState<boolean>(false);
const [updatingPluginsIds, setUpdatingPluginIds] = useState<Record<string, boolean>>({});
const [canBeUpdatedPluginIds, setCanBeUpdatedPluginIds] = useState<Record<string, boolean>>({});
const [repoApiError, setRepoApiError] = useState<Error>(null);
const [fetchManifestTime, setFetchManifestTime] = useState<number>(Date.now());
const pluginService = PluginService.instance();
@@ -96,9 +108,25 @@ export default function(props: Props) {
useEffect(() => {
let cancelled = false;
async function fetchManifests() {
await repoApi().loadManifests();
setManifestsLoaded(false);
setRepoApiError(null);
let loadError: Error = null;
try {
await repoApi().loadManifests();
} catch (error) {
logger.error(error);
loadError = error;
}
if (cancelled) return;
setManifestsLoaded(true);
if (loadError) {
setManifestsLoaded(false);
setRepoApiError(loadError);
} else {
setManifestsLoaded(true);
}
}
void fetchManifests();
@@ -106,7 +134,7 @@ export default function(props: Props) {
return () => {
cancelled = true;
};
}, []);
}, [fetchManifestTime]);
useEffect(() => {
if (!manifestsLoaded) return () => {};
@@ -252,7 +280,7 @@ export default function(props: Props) {
function renderSearchArea() {
return (
<div style={{ marginBottom: 20 }}>
<div style={{ marginBottom: 0 }}>
<SearchPlugins
disabled={!manifestsLoaded}
maxWidth={maxWidth}
@@ -268,11 +296,18 @@ export default function(props: Props) {
);
}
function renderRepoApiError() {
if (!repoApiError) return null;
return <RepoApiErrorMessage maxWidth={maxWidth} type="error">{_('Could not connect to plugin repository')} - <StyledLink href="#" onClick={() => { setFetchManifestTime(Date.now()); }}>{_('Try again')}</StyledLink></RepoApiErrorMessage>;
}
function renderBottomArea() {
if (searchQuery) return null;
return (
<div>
{renderRepoApiError()}
<div style={{ display: 'flex', flexDirection: 'row', maxWidth }}>
<ToolsButton tooltip={_('Plugin tools')} iconName="fas fa-cog" level={ButtonLevel.Secondary} onClick={onToolsClick}/>
<div style={{ display: 'flex', flex: 1 }}>
@@ -101,7 +101,7 @@ export default function(props: Props) {
onChange={onChange}
onSearchButtonClick={onSearchButtonClick}
searchStarted={searchStarted}
placeholder={_('Search for plugins...')}
placeholder={props.disabled ? _('Please wait...') : _('Search for plugins...')}
disabled={props.disabled}
/>
</div>
@@ -256,7 +256,7 @@ class EncryptionConfigScreenComponent extends React.Component {
<div>
<div style={containerStyle}>
{
<div style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
<div className="alert alert-warning" style={{ backgroundColor: theme.warningBackgroundColor, paddingLeft: 10, paddingRight: 10, paddingTop: 2, paddingBottom: 2 }}>
<p style={theme.textStyle}>
<span>{_('For more information about End-To-End Encryption (E2EE) and advice on how to enable it please check the documentation:')}</span>{' '}
<a
@@ -34,6 +34,7 @@ import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog';
import { ShareInvitation } from '@joplin/lib/services/share/reducer';
import ShareService from '@joplin/lib/services/share/ShareService';
import { reg } from '@joplin/lib/registry';
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
const { connect } = require('react-redux');
const { PromptDialog } = require('../PromptDialog.min.js');
@@ -234,6 +235,14 @@ class MainScreenComponent extends React.Component<Props, State> {
try {
output = loadLayout(Object.keys(userLayout).length ? userLayout : null, defaultLayout, rootLayoutSize);
// For unclear reasons, layout items sometimes end up witout a key.
// In that case, we can't do anything with them, so remove them
// here. It could be due to the deprecated plugin API, which allowed
// creating panel without a key, although in this case it should
// have been set automatically.
// https://github.com/laurent22/joplin/issues/4926
output = removeKeylessItems(output);
if (!findItemByKey(output, 'sideBar') || !findItemByKey(output, 'noteList') || !findItemByKey(output, 'editor')) {
throw new Error('"sideBar", "noteList" and "editor" must be present in the layout');
}
@@ -18,6 +18,6 @@ export const runtime = (comp: any): CommandRuntime => {
},
});
},
enabledCondition: 'folderIsShareRootAndOwnedByUser || !folderIsShared',
enabledCondition: 'joplinServerConnected && (folderIsShareRootAndOwnedByUser || !folderIsShared)',
};
};
@@ -18,5 +18,6 @@ export const runtime = (comp: any): CommandRuntime => {
},
});
},
enabledCondition: 'joplinServerConnected && someNotesSelected',
};
};
+15 -2
View File
@@ -90,6 +90,7 @@ interface Props {
['spellChecker.enabled']: boolean;
['spellChecker.language']: string;
plugins: PluginStates;
customCss: string;
}
const commandNames: string[] = menuCommandNames();
@@ -313,7 +314,10 @@ function useMenu(props: Props) {
await InteropServiceHelper.export(
(action: any) => props.dispatch(action),
module,
{ plugins: props.plugins }
{
plugins: props.plugins,
customCss: props.customCss,
}
);
},
});
@@ -695,11 +699,18 @@ function useMenu(props: Props) {
},
],
},
folder: {
label: _('Note&book'),
submenu: [
menuItemDic.showShareFolderDialog,
],
},
note: {
label: _('&Note'),
submenu: [
menuItemDic.toggleExternalEditing,
menuItemDic.setTags,
menuItemDic.showShareNoteDialog,
separator(),
menuItemDic.showNoteContentProperties,
],
@@ -818,6 +829,7 @@ function useMenu(props: Props) {
rootMenus.edit,
rootMenus.view,
rootMenus.go,
rootMenus.folder,
rootMenus.note,
rootMenus.tools,
rootMenus.help,
@@ -852,7 +864,7 @@ function useMenu(props: Props) {
clearTimeout(timeoutId);
timeoutId = null;
};
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins]);
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime, props['spellChecker.language'], props['spellChecker.enabled'], props.plugins, props.customCss]);
useMenuStates(menu, props);
@@ -909,6 +921,7 @@ const mapStateToProps = (state: AppState) => {
['spellChecker.language']: state.settings['spellChecker.language'],
['spellChecker.enabled']: state.settings['spellChecker.enabled'],
plugins: state.pluginService.plugins,
customCss: state.customCss,
};
};
@@ -13,6 +13,7 @@ interface MultiNoteActionsProps {
watchedNoteFiles: string[];
plugins: PluginStates;
inConflictFolder: boolean;
customCss: string;
}
function styles_(props: MultiNoteActionsProps) {
@@ -53,6 +54,7 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
watchedNoteFiles: props.watchedNoteFiles,
plugins: props.plugins,
inConflictFolder: props.inConflictFolder,
customCss: props.customCss,
});
const itemComps = [];
@@ -24,7 +24,7 @@ interface KeyToLabelMap {
let markupToHtml_: any = null;
function markupToHtml() {
if (markupToHtml_) return markupToHtml_;
markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return markupToHtml_;
}
@@ -1,7 +1,7 @@
import * as React from 'react';
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml } from '../../utils/resourceHandling';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos } from '../../utils/types';
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent, processPastedHtml, attachedResources } from '../../utils/resourceHandling';
import useScroll from './utils/useScroll';
import styles_ from './styles';
import CommandService from '@joplin/lib/services/CommandService';
@@ -20,6 +20,7 @@ const taboverride = require('taboverride');
import { reg } from '@joplin/lib/registry';
import BaseItem from '@joplin/lib/models/BaseItem';
import setupToolbarButtons from './utils/setupToolbarButtons';
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
const { themeStyle } = require('@joplin/lib/theme');
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@@ -66,6 +67,26 @@ function newBlockSource(language: string = '', content: string = ''): any {
};
}
// In TinyMCE 5.2, when setting the body to '<div id="rendered-md"></div>',
// it would end up as '<div id="rendered-md"><br/></div>' once rendered
// (an additional <br/> was inserted).
//
// This behaviour was "fixed" later on, possibly in 5.6, which has this change:
//
// - Fixed getContent with text format returning a new line when the editor is empty #TINY-6281
//
// The problem is that the list plugin was, unknown to me, relying on this <br/>
// being present. Without it, trying to add a bullet point or checkbox on an
// empty document, does nothing. The exact reason for this is unclear
// so as a workaround we manually add this <br> for empty documents,
// which fixes the issue.
//
// Perhaps upgrading the list plugin (which is a fork of TinyMCE own list plugin)
// would help?
function awfulBrHack(html: string): string {
return html === '<div id="rendered-md"></div>' ? '<div id="rendered-md"><br/></div>' : html;
}
function findEditableContainer(node: any): any {
while (node) {
if (node.classList && node.classList.contains('joplin-editable')) return node;
@@ -127,6 +148,12 @@ const joplinCommandToTinyMceCommands: JoplinCommandToTinyMceCommands = {
'search': { name: 'SearchReplace' },
};
interface LastOnChangeEventInfo {
content: string;
resourceInfos: ResourceInfos;
contentKey: string;
}
let loadedCssFiles_: string[] = [];
let loadedJsFiles_: string[] = [];
let dispatchDidUpdateIID_: any = null;
@@ -147,7 +174,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const markupToHtml = useRef(null);
markupToHtml.current = props.markupToHtml;
const lastOnChangeEventInfo = useRef<any>({
const lastOnChangeEventInfo = useRef<LastOnChangeEventInfo>({
content: null,
resourceInfos: null,
contentKey: null,
@@ -338,7 +365,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
element.id = script.id;
element.onload = () => {
resolve();
resolve(null);
};
document.getElementsByTagName('head')[0].appendChild(element);
@@ -807,6 +834,25 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
};
function resourceInfosEqual(ri1: ResourceInfos, ri2: ResourceInfos): boolean {
if (ri1 && !ri2 || !ri1 && ri2) return false;
if (!ri1 && !ri2) return true;
const keys1 = Object.keys(ri1);
const keys2 = Object.keys(ri2);
if (keys1.length !== keys2.length) return false;
// The attachedResources() call that generates the ResourceInfos object
// uses cache for the resource objects, so we can use strict equality
// for comparison.
for (const k of keys1) {
if (ri1[k] !== ri2[k]) return false;
}
return true;
}
useEffect(() => {
if (!editor) return () => {};
@@ -818,11 +864,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
let cancelled = false;
const loadContent = async () => {
if (lastOnChangeEventInfo.current.content !== props.content || lastOnChangeEventInfo.current.resourceInfos !== props.resourceInfos) {
const resourcesEqual = resourceInfosEqual(lastOnChangeEventInfo.current.resourceInfos, props.resourceInfos);
if (lastOnChangeEventInfo.current.content !== props.content || !resourcesEqual) {
const result = await props.markupToHtml(props.contentMarkupLanguage, props.content, markupRenderOptions({ resourceInfos: props.resourceInfos }));
if (cancelled) return;
editor.setContent(result.html);
editor.setContent(awfulBrHack(result.html));
if (lastOnChangeEventInfo.current.contentKey !== props.contentKey) {
// Need to clear UndoManager to avoid this problem:
@@ -922,6 +970,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const contentMd = await prop_htmlToMarkdownRef.current(info.contentMarkupLanguage, info.editor.getContent(), info.contentOriginalCss);
lastOnChangeEventInfo.current.content = contentMd;
lastOnChangeEventInfo.current.resourceInfos = await attachedResources(contentMd);
props_onChangeRef.current({
changeId: info.changeId,
@@ -1017,31 +1066,37 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
async function onPaste(event: any) {
// We do not use the default pasting behaviour because the input has
// to be processed in various ways.
event.preventDefault();
const resourceMds = await handlePasteEvent(event);
if (resourceMds.length) {
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMds.join('\n'), markupRenderOptions({ bodyOnly: true }));
editor.insertContent(result.html);
} else {
const pastedText = event.clipboardData.getData('text');
const pastedText = event.clipboardData.getData('text/plain');
if (BaseItem.isMarkdownTag(pastedText)) { // Paste a link to a note
event.preventDefault();
const result = await markupToHtml.current(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, pastedText, markupRenderOptions({ bodyOnly: true }));
editor.insertContent(result.html);
} else { // Paste regular text
// HACK: TinyMCE doesn't add an undo step when pasting, for unclear reasons
// so we manually add it here. We also can't do it immediately it seems, or
// else nothing is added to the stack, so do it on the next frame.
const pastedHtml = clipboard.readHTML();
if (pastedHtml) {
event.preventDefault();
const pastedHtml = event.clipboardData.getData('text/html');
if (pastedHtml) { // Handles HTML
const modifiedHtml = await processPastedHtml(pastedHtml);
editor.insertContent(modifiedHtml);
} else { // Handles plain text
pasteAsPlainText(pastedText);
}
window.requestAnimationFrame(() => editor.undoManager.add());
onChangeHandler();
// This code before was necessary to get undo working after
// pasting but it seems it's no longer necessary, so
// removing it for now. We also couldn't do it immediately
// it seems, or else nothing is added to the stack, so do it
// on the next frame.
//
// window.requestAnimationFrame(() =>
// editor.undoManager.add()); onChangeHandler();
}
}
}
@@ -1060,6 +1115,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
onChangeHandler();
}
function pasteAsPlainText(text: string = null) {
const pastedText = text === null ? clipboard.readText() : text;
if (pastedText) {
editor.insertContent(plainTextToHtml(pastedText));
}
}
function onKeyDown(event: any) {
// It seems "paste as text" is handled automatically by
// on Windows so the code below so we need to run the below
@@ -1072,8 +1134,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
// it here and we don't need to do anything special in onPaste
if (!shim.isWindows()) {
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
const pastedText = clipboard.readText();
if (pastedText) editor.insertContent(pastedText);
pasteAsPlainText();
}
}
}
@@ -156,10 +156,11 @@ function NoteEditor(props: NoteEditorProps) {
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: props.customCss,
});
return markupToHtml.allAssets(markupLanguage, theme);
}, [props.themeId]);
}, [props.themeId, props.customCss]);
const handleProvisionalFlag = useCallback(() => {
if (props.isProvisional) {
@@ -458,6 +459,7 @@ function NoteEditor(props: NoteEditorProps) {
watchedNoteFiles={props.watchedNoteFiles}
plugins={props.plugins}
inConflictFolder={props.selectedFolderId === Folder.conflictFolderId()}
customCss={props.customCss}
/>;
}
@@ -549,7 +551,7 @@ function NoteEditor(props: NoteEditorProps) {
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{renderSearchBar()}
</div>
<div style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
<div className="tag-bar" style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
{renderTagButton()}
{renderTagBar()}
</div>
@@ -91,7 +91,7 @@ export default function NoteTitleBar(props: Props) {
}, []);
function renderTitleBarDate() {
return <span style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
return <span className="updated-time-label" style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
}
function renderNoteToolbar() {
@@ -104,6 +104,7 @@ export default function NoteTitleBar(props: Props) {
return (
<StyledRoot>
<input
className="title-input"
type="text"
ref={props.titleInputRef}
placeholder={props.isProvisional ? _('Creating new %s...', props.noteIsTodo ? _('to-do') : _('note')) : ''}
@@ -135,6 +135,13 @@ export async function processPastedHtml(html: string) {
const allImageUrls: string[] = [];
const mappedResources: Record<string, string> = {};
// When copying text from eg. GitHub, the HTML might contain non-breaking
// spaces instead of regular spaces. If these non-breaking spaces are
// inserted into the TinyMCE editor (using insertContent), they will be
// dropped. So here we convert them to regular spaces.
// https://stackoverflow.com/a/31790544/561309
html = html.replace(/[\u202F\u00A0]/g, ' ');
htmlUtils.replaceImageUrls(html, (src: string) => {
allImageUrls.push(src);
});
@@ -2,6 +2,8 @@
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { MarkupLanguage } from '@joplin/renderer';
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
export interface ToolbarButtonInfos {
[key: string]: ToolbarButtonInfo;
@@ -49,9 +51,9 @@ export interface NoteBodyEditorProps {
onWillChange(event: any): void;
onMessage(event: any): void;
onScroll(event: any): void;
markupToHtml: Function;
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: any)=> Promise<RenderResult>;
htmlToMarkdown: Function;
allAssets: Function;
allAssets: (markupLanguage: MarkupLanguage)=> Promise<RenderResultPluginAsset[]>;
disabled: boolean;
dispatch: Function;
noteToolbar: any;
@@ -24,6 +24,7 @@ export default function useMarkupToHtml(deps: HookDependencies) {
const markupToHtml = useMemo(() => {
return markupLanguageUtils.newMarkupToHtml(deps.plugins, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: customCss || '',
});
}, [plugins]);
@@ -49,7 +50,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
const result = await markupToHtml.render(markupLanguage, md, theme, Object.assign({}, {
codeTheme: theme.codeThemeCss,
userCss: customCss || '',
resources: resources,
postMessageSyntax: 'ipcProxySendToHost',
splitted: true,
@@ -125,6 +125,7 @@ class NoteListComponent extends React.Component {
watchedNoteFiles: this.props.watchedNoteFiles,
plugins: this.props.plugins,
inConflictFolder: this.props.selectedFolderId === Folder.conflictFolderId(),
customCss: this.props.customCss,
});
menu.popup(bridge().window());
@@ -513,6 +514,7 @@ const mapStateToProps = (state: AppState) => {
noteSortOrder: state.settings['notes.sortOrder.field'],
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
customCss: state.customCss,
};
};
@@ -54,12 +54,14 @@ export default function NoteListControls(props: Props) {
return (
<ButtonContainer>
<StyledButton
className="new-todo-button"
tooltip={CommandService.instance().label('newTodo')}
iconName="far fa-check-square"
level={ButtonLevel.Primary}
onClick={onNewTodoButtonClick}
/>
<StyledButton
className="new-note-button"
tooltip={CommandService.instance().label('newNote')}
iconName="icon-note"
level={ButtonLevel.Primary}
+11 -1
View File
@@ -110,6 +110,7 @@ function NoteListItem(props: NoteListItemProps, ref: any) {
let listItemTitleStyle = Object.assign({}, props.style.listItemTitle);
listItemTitleStyle.paddingLeft = !item.is_todo ? hPadding : 4;
if (item.is_shared) listItemTitleStyle.color = theme.colorWarn;
if (item.is_todo && !!item.todo_completed) listItemTitleStyle = Object.assign(listItemTitleStyle, props.style.listItemTitleCompleted);
const displayTitle = Note.displayTitle(item);
@@ -149,12 +150,21 @@ function NoteListItem(props: NoteListItemProps, ref: any) {
color: theme.color,
};
const watchedIcon = props.isWatched ? null : <i style={watchedIconStyle} className={'fa fa-share-square'}></i>;
const classNames = [
'list-item-container',
props.isSelected && 'selected',
item.todo_completed && 'todo-completed',
item.is_todo ? 'todo-list-item' : 'note-list-item',
(props.index + 1) % 2 === 0 ? 'even' : 'odd',
]
.filter(e => !!e)
.join(' ');
// Need to include "todo_completed" in key so that checkbox is updated when
// item is changed via sync.
return (
<StyledRoot
className="list-item-container"
className={classNames}
onDragOver={props.onNoteDragOver}
onDrop={props.onNoteDrop}
width={props.width}
@@ -118,11 +118,11 @@ class NoteRevisionViewerComponent extends React.PureComponent {
const markupToHtml = markupLanguageUtils.newMarkupToHtml({}, {
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
customCss: this.props.customCss ? this.props.customCss : '',
});
const result = await markupToHtml.render(markupLanguage, noteBody, theme, {
codeTheme: theme.codeThemeCss,
userCss: this.props.customCss ? this.props.customCss : '',
resources: await shared.attachedResources(noteBody),
postMessageSyntax: 'ipcProxySendToHost',
});
+1 -1
View File
@@ -158,7 +158,7 @@ class NoteSearchBarComponent extends React.Component {
);
return (
<div style={this.props.style}>
<div className="note-search-bar" style={this.props.style}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{closeButton}
<input
+5 -5
View File
@@ -221,11 +221,11 @@ class PromptDialog extends React.Component {
let inputComp = null;
if (this.props.inputType === 'datetime') {
inputComp = <Datetime value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
inputComp = <Datetime className="datetime-picker" value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={momentObject => onDateTimeChange(momentObject)} />;
} else if (this.props.inputType === 'tags') {
inputComp = <CreatableSelect styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
inputComp = <CreatableSelect className="tag-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
} else if (this.props.inputType === 'dropdown') {
inputComp = <Select styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
inputComp = <Select className="item-selector" styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} components={makeAnimated()} value={this.props.answer} defaultValue={this.props.defaultValue} isClearable={false} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={event => onKeyDown(event)} />;
} else {
inputComp = <input style={styles.input} ref={this.answerInput_} value={this.state.answer} type="text" onChange={event => onChange(event)} onKeyDown={event => onKeyDown(event)} />;
}
@@ -254,8 +254,8 @@ class PromptDialog extends React.Component {
}
return (
<div style={styles.modalLayer}>
<div style={styles.promptDialog}>
<div className="modal-layer" style={styles.modalLayer}>
<div className="modal-dialog" style={styles.promptDialog}>
<label style={styles.label}>{this.props.label ? this.props.label : ''}</label>
<div style={{ display: 'inline-block', color: 'black', backgroundColor: theme.backgroundColor }}>
{inputComp}
@@ -0,0 +1,30 @@
import produce from 'immer';
import iterateItems from './iterateItems';
import { LayoutItem } from './types';
import validateLayout from './validateLayout';
interface ItemToRemove {
parent: LayoutItem;
index: number;
}
export default function(layout: LayoutItem): LayoutItem {
const itemsToRemove: ItemToRemove[] = [];
const output = produce(layout, (layoutDraft: LayoutItem) => {
iterateItems(layoutDraft, (itemIndex: number, item: LayoutItem, parent: LayoutItem) => {
if (!item.key) itemsToRemove.push({ parent, index: itemIndex });
return true;
});
itemsToRemove.sort((a: ItemToRemove, b: ItemToRemove) => {
return a.index > b.index ? -1 : +1;
});
for (const item of itemsToRemove) {
item.parent.children.splice(item.index, 1);
}
});
return output !== layout ? validateLayout(output) : layout;
}
@@ -126,7 +126,7 @@ function SearchBar(props: Props) {
}, [props.notesParentType, onExitSearch]);
return (
<Root>
<Root className="search-bar">
<SearchInput
inputRef={props.inputRef}
value={query}
+70 -32
View File
@@ -10,19 +10,21 @@ import { reg } from '@joplin/lib/registry';
import Dialog from './Dialog';
import DialogTitle from './DialogTitle';
import ShareService from '@joplin/lib/services/share/ShareService';
import { StateShare } from '@joplin/lib/services/share/reducer';
import { NoteEntity } from '@joplin/lib/services/database/types';
import Button from './Button/Button';
import { connect } from 'react-redux';
import { AppState } from '../app';
const { clipboard } = require('electron');
interface ShareNoteDialogProps {
interface Props {
themeId: number;
noteIds: Array<string>;
onClose: Function;
shares: StateShare[];
}
interface SharesMap {
[key: string]: any;
}
function styles_(props: ShareNoteDialogProps) {
function styles_(props: Props) {
return buildStyle('ShareNoteDialog', props.themeId, (theme: any) => {
return {
noteList: {
@@ -60,17 +62,21 @@ function styles_(props: ShareNoteDialogProps) {
});
}
export default function ShareNoteDialog(props: ShareNoteDialogProps) {
export function ShareNoteDialog(props: Props) {
console.info('Render ShareNoteDialog');
const [notes, setNotes] = useState<any[]>([]);
const [notes, setNotes] = useState<NoteEntity[]>([]);
const [sharesState, setSharesState] = useState<string>('unknown');
const [shares, setShares] = useState<SharesMap>({});
// const [shares, setShares] = useState<SharesMap>({});
const noteCount = notes.length;
const theme = themeStyle(props.themeId);
const styles = styles_(props);
useEffect(() => {
void ShareService.instance().refreshShares();
}, []);
useEffect(() => {
async function fetchNotes() {
const result = [];
@@ -87,9 +93,9 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
props.onClose();
};
const copyLinksToClipboard = (shares: SharesMap) => {
const copyLinksToClipboard = (shares: StateShare[]) => {
const links = [];
for (const n in shares) links.push(ShareService.instance().shareUrl(shares[n]));
for (const share of shares) links.push(ShareService.instance().shareUrl(share));
clipboard.writeText(links.join('\n'));
};
@@ -109,15 +115,13 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
setSharesState('creating');
const newShares = Object.assign({}, shares);
const newShares: StateShare[] = [];
for (const note of notes) {
const share = await service.shareNote(note.id);
newShares[note.id] = share;
newShares.push(share);
}
setShares(newShares);
setSharesState('synchronizing');
await reg.waitForSyncFinishedThenSync();
setSharesState('creating');
@@ -125,6 +129,8 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
copyLinksToClipboard(newShares);
setSharesState('created');
await ShareService.instance().refreshShares();
} catch (error) {
if (error.code === 404 && !hasSynced) {
reg.logger().info('ShareNoteDialog: Note does not exist on server - trying to sync it.', error);
@@ -142,34 +148,53 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
}
};
const removeNoteButton_click = (event: any) => {
const newNotes = [];
for (let i = 0; i < notes.length; i++) {
const n = notes[i];
if (n.id === event.noteId) continue;
newNotes.push(n);
}
setNotes(newNotes);
// const removeNoteButton_click = (event: any) => {
// const newNotes = [];
// for (let i = 0; i < notes.length; i++) {
// const n = notes[i];
// if (n.id === event.noteId) continue;
// newNotes.push(n);
// }
// setNotes(newNotes);
// };
const unshareNoteButton_click = async (event: any) => {
await ShareService.instance().unshareNote(event.noteId);
await ShareService.instance().refreshShares();
};
const renderNote = (note: any) => {
const removeButton = notes.length <= 1 ? null : (
<button onClick={() => removeNoteButton_click({ noteId: note.id })} style={styles.noteRemoveButton}>
<i style={styles.noteRemoveButtonIcon} className={'fa fa-times'}></i>
</button>
const renderNote = (note: NoteEntity) => {
const unshareButton = !props.shares.find(s => s.note_id === note.id) ? null : (
<Button tooltip={_('Unshare note')} iconName="fas fa-share-alt" onClick={() => unshareNoteButton_click({ noteId: note.id })}/>
);
// const removeButton = notes.length <= 1 ? null : (
// <Button iconName="fa fa-times" onClick={() => removeNoteButton_click({ noteId: note.id })}/>
// );
// const unshareButton = !shares[note.id] ? null : (
// <button onClick={() => unshareNoteButton_click({ noteId: note.id })} style={styles.noteRemoveButton}>
// <i style={styles.noteRemoveButtonIcon} className={'fas fa-share-alt'}></i>
// </button>
// );
// const removeButton = notes.length <= 1 ? null : (
// <button onClick={() => removeNoteButton_click({ noteId: note.id })} style={styles.noteRemoveButton}>
// <i style={styles.noteRemoveButtonIcon} className={'fa fa-times'}></i>
// </button>
// );
return (
<div key={note.id} style={styles.note}>
<span style={styles.noteTitle}>{note.title}</span>{removeButton}
<span style={styles.noteTitle}>{note.title}</span>{unshareButton}
</div>
);
};
const renderNoteList = (notes: any) => {
const noteComps = [];
for (const noteId of Object.keys(notes)) {
noteComps.push(renderNote(notes[noteId]));
for (const note of notes) {
noteComps.push(renderNote(note));
}
return <div style={styles.noteList}>{noteComps}</div>;
};
@@ -194,7 +219,12 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
<button disabled={['creating', 'synchronizing'].indexOf(sharesState) >= 0} style={styles.copyShareLinkButton} onClick={shareLinkButton_click}>{_n('Copy Shareable Link', 'Copy Shareable Links', noteCount)}</button>
<div style={theme.textStyle}>{statusMessage(sharesState)}</div>
{renderEncryptionWarningMessage()}
<DialogButtonRow themeId={props.themeId} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
<DialogButtonRow
themeId={props.themeId}
onClick={buttonRow_click}
okButtonShow={false}
cancelButtonLabel={_('Close')}
/>
</div>
);
}
@@ -203,3 +233,11 @@ export default function ShareNoteDialog(props: ShareNoteDialogProps) {
<Dialog renderContent={renderContent}/>
);
}
const mapStateToProps = (state: AppState) => {
return {
shares: state.shareService.shares.filter(s => !!s.note_id),
};
};
export default connect(mapStateToProps)(ShareNoteDialog as any);
+19 -8
View File
@@ -77,12 +77,12 @@ function ExpandLink(props: any) {
function FolderItem(props: any) {
const { hasChildren, isExpanded, parentId, depth, selected, folderId, folderTitle, anchorRef, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props;
const noteCountComp = noteCount ? <StyledNoteCount>{noteCount}</StyledNoteCount> : null;
const noteCountComp = noteCount ? <StyledNoteCount className="note-count-label">{noteCount}</StyledNoteCount> : null;
const shareIcon = shareId && !parentId ? <StyledShareIcon className="fas fa-share-alt"></StyledShareIcon> : null;
return (
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth} ${selected ? 'selected' : ''}`} onDragStart={onFolderDragStart_} onDragOver={onFolderDragOver_} onDrop={onFolderDrop_} draggable={true} data-folder-id={folderId}>
<ExpandLink themeId={props.themeId} hasChildren={hasChildren} folderId={folderId} onClick={onFolderToggleClick_} isExpanded={isExpanded}/>
<StyledListItemAnchor
ref={anchorRef}
@@ -100,7 +100,8 @@ function FolderItem(props: any) {
}}
onDoubleClick={onFolderToggleClick_}
>
{folderTitle} {shareIcon} {noteCountComp}
<span className="title">{folderTitle}</span>
{shareIcon} {noteCountComp}
</StyledListItemAnchor>
</StyledListItem>
);
@@ -382,7 +383,7 @@ class SidebarComponent extends React.Component<Props, State> {
}
renderNoteCount(count: number) {
return count ? <StyledNoteCount>{count}</StyledNoteCount> : null;
return count ? <StyledNoteCount className="note-count-label">{count}</StyledNoteCount> : null;
}
renderExpandIcon(isExpanded: boolean, isVisible: boolean = true) {
@@ -394,7 +395,7 @@ class SidebarComponent extends React.Component<Props, State> {
renderAllNotesItem(selected: boolean) {
return (
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0'} isSpecialItem={true}>
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0 all-notes'} isSpecialItem={true}>
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
<StyledAllNotesIcon className="icon-notes"/>
<StyledListItemAnchor
@@ -451,7 +452,12 @@ class SidebarComponent extends React.Component<Props, State> {
const noteCount = Setting.value('showNoteCounts') ? this.renderNoteCount(tag.note_count) : '';
return (
<StyledListItem selected={selected} className={'list-item-container'} key={tag.id} onDrop={this.onTagDrop_} data-tag-id={tag.id}>
<StyledListItem selected={selected}
className={`list-item-container ${selected ? 'selected' : ''}`}
key={tag.id}
onDrop={this.onTagDrop_}
data-tag-id={tag.id}
>
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
<StyledListItemAnchor
ref={anchorRef}
@@ -465,7 +471,8 @@ class SidebarComponent extends React.Component<Props, State> {
this.tagItem_click(tag);
}}
>
{Tag.displayTitle(tag)} {noteCount}
<span className="tag-label">{Tag.displayTitle(tag)}</span>
{noteCount}
</StyledListItemAnchor>
</StyledListItem>
);
@@ -657,7 +664,11 @@ class SidebarComponent extends React.Component<Props, State> {
const folderItems = [this.renderAllNotesItem(allNotesSelected)].concat(result.items);
this.folderItemsOrder_ = result.order;
items.push(
<div className="folders" key="folder_items" style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}>
<div
className={`folders ${this.state.folderHeaderIsExpanded ? 'expanded' : ''}`}
key="folder_items"
style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}
>
{folderItems}
</div>
);
@@ -69,7 +69,6 @@ function listItemTextColor(props: any) {
export const StyledListItemAnchor = styled.a`
font-size: ${(props: any) => Math.round(props.theme.fontSize * 1.0833333)}px;
// font-weight: 500;
text-decoration: none;
color: ${(props: any) => listItemTextColor(props)};
cursor: default;
@@ -87,12 +87,15 @@ function StatusScreen(props: Props) {
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
let currentListKey = '';
let listItems: any[] = [];
for (const n in section.body) {
if (!section.body.hasOwnProperty(n)) continue;
const item = section.body[n];
let text = '';
let retryLink = null;
let itemType = null;
if (typeof item === 'object') {
if (item.canRetry) {
const onClick = async () => {
@@ -107,18 +110,40 @@ function StatusScreen(props: Props) {
);
}
text = item.text;
itemType = item.type;
} else {
text = item;
}
if (itemType === 'openList') {
currentListKey = item.key;
continue;
}
if (itemType === 'closeList') {
itemsHtml.push(<ul key={currentListKey}>{listItems}</ul>);
currentListKey = '';
listItems = [];
continue;
}
if (!text) text = '\xa0';
itemsHtml.push(
<div style={theme.textStyle} key={`item_${n}`}>
<span>{text}</span>
{retryLink}
</div>
);
if (currentListKey) {
listItems.push(
<li style={theme.textStyle} key={`item_${n}`}>
<span>{text}</span>
{retryLink}
</li>
);
} else {
itemsHtml.push(
<div style={theme.textStyle} key={`item_${n}`}>
<span>{text}</span>
{retryLink}
</div>
);
}
}
if (section.canRetryAll) {
@@ -17,7 +17,16 @@ export default function ToggleEditorsButton(props: Props) {
const style = styles_(props);
return (
<button style={style.button} disabled={!props.toolbarButtonInfo.enabled} aria-label={props.toolbarButtonInfo.tooltip} title={props.toolbarButtonInfo.tooltip} type="button" className="tox-tbtn" aria-pressed="false" onClick={props.toolbarButtonInfo.onClick}>
<button
style={style.button}
disabled={!props.toolbarButtonInfo.enabled}
aria-label={props.toolbarButtonInfo.tooltip}
title={props.toolbarButtonInfo.tooltip}
type="button"
className={`tox-tbtn ${props.value}-active`}
aria-pressed="false"
onClick={props.toolbarButtonInfo.onClick}
>
<div style={style.leftInnerButton}>
<i style={style.leftIcon} className="fab fa-markdown"></i>
</div>
@@ -45,5 +45,7 @@ export default function() {
'editor.swapLineUp',
'editor.swapLineDown',
'toggleSafeMode',
'showShareNoteDialog',
'showShareFolderDialog',
];
}
@@ -0,0 +1,9 @@
import styled from 'styled-components';
const StyledLink = styled.a`
font-size: ${props => props.theme.fontSize}px;
color: ${props => props.theme.urlColor};
font-family: ${props => props.theme.fontFamily};
`;
export default StyledLink;
@@ -7,6 +7,7 @@ const StyledMessage = styled.div`
color: ${props => props.theme.color};
font-family: ${props => props.theme.fontFamily};
padding: ${props => props.type === 'error' ? props.theme.mainPadding : '0'}px;
word-break: break-all;
`;
export default StyledMessage;
@@ -22,6 +22,7 @@ interface ContextMenuProps {
watchedNoteFiles: string[];
plugins: PluginStates;
inConflictFolder: boolean;
customCss: string;
}
export default class NoteListUtils {
@@ -158,6 +159,7 @@ export default class NoteListUtils {
sourceNoteIds: noteIds,
includeConflicts: props.inConflictFolder,
plugins: props.plugins,
customCss: props.customCss,
});
},
})
@@ -246,7 +246,7 @@ class Dialog extends React.PureComponent<Props, State> {
markupToHtml() {
if (this.markupToHtml_) return this.markupToHtml_;
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml({});
this.markupToHtml_ = markupLanguageUtils.newMarkupToHtml();
return this.markupToHtml_;
}
@@ -432,7 +432,8 @@ class Dialog extends React.PureComponent<Props, State> {
renderItem(item: SearchResult) {
const theme = themeStyle(this.props.themeId);
const style = this.style();
const rowStyle = item.id === this.state.selectedItemId ? style.rowSelected : style.row;
const isSelected = item.id === this.state.selectedItemId;
const rowStyle = isSelected ? style.rowSelected : style.row;
const titleHtml = item.fragments
? `<span style="font-weight: bold; color: ${theme.colorBright};">${item.title}</span>`
: surroundKeywords(this.state.keywords, item.title, `<span style="font-weight: bold; color: ${theme.colorBright};">`, '</span>', { escapeHtml: true });
@@ -444,7 +445,7 @@ class Dialog extends React.PureComponent<Props, State> {
const fragmentComp = !fragmentsHtml ? null : <div style={style.rowFragments} dangerouslySetInnerHTML={{ __html: (fragmentsHtml) }}></div>;
return (
<div key={item.id} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
<div key={item.id} className={isSelected ? 'selected' : null} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
<div style={style.rowTitle} dangerouslySetInnerHTML={{ __html: titleHtml }}></div>
{fragmentComp}
{pathComp}
@@ -521,11 +522,11 @@ class Dialog extends React.PureComponent<Props, State> {
render() {
const theme = themeStyle(this.props.themeId);
const style = this.style();
const helpComp = !this.state.showHelp ? null : <div style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
const helpComp = !this.state.showHelp ? null : <div className="help-text" style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
return (
<div onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
<div style={style.dialogBox}>
<div className="modal-layer" onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
<div className="modal-dialog" style={style.dialogBox}>
{helpComp}
<div style={style.inputHelpWrapper}>
<input autoFocus type="text" style={style.input} ref={this.inputRef} value={this.state.query} onChange={this.input_onChange} onKeyDown={this.input_onKeyDown} />
+24 -20
View File
@@ -14,31 +14,35 @@ if [ "$1" == "" ]; then
fi
USER_NUM=$1
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
rm -f "$CMD_FILE"
USER_EMAIL="user$USER_NUM@example.com"
RESET_ALL=$2
PROFILE_DIR=~/.config/joplindev-desktop-$USER_NUM
rm -rf "$PROFILE_DIR"
echo "config keychain.supported 0" >> "$CMD_FILE"
echo "config sync.target 9" >> "$CMD_FILE"
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
echo "config sync.9.password 123456" >> "$CMD_FILE"
if [ "$RESET_ALL" == "1" ]; then
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
rm -f "$CMD_FILE"
if [ "$1" == "1" ]; then
curl --data '{"action": "createTestUsers"}' http://localhost:22300/api/debug
USER_EMAIL="user$USER_NUM@example.com"
rm -rf "$PROFILE_DIR"
echo 'mkbook "shared"' >> "$CMD_FILE"
echo 'mkbook "other"' >> "$CMD_FILE"
echo 'use "shared"' >> "$CMD_FILE"
echo 'mknote "note 1"' >> "$CMD_FILE"
echo "config keychain.supported 0" >> "$CMD_FILE"
echo "config sync.target 9" >> "$CMD_FILE"
echo "config sync.9.path http://localhost:22300" >> "$CMD_FILE"
echo "config sync.9.username $USER_EMAIL" >> "$CMD_FILE"
echo "config sync.9.password 123456" >> "$CMD_FILE"
if [ "$1" == "1" ]; then
curl --data '{"action": "createTestUsers"}' http://localhost:22300/api/debug
echo 'mkbook "shared"' >> "$CMD_FILE"
echo 'mkbook "other"' >> "$CMD_FILE"
echo 'use "shared"' >> "$CMD_FILE"
echo 'mknote "note 1"' >> "$CMD_FILE"
echo 'mknote "note 2"' >> "$CMD_FILE"
fi
cd "$ROOT_DIR/packages/app-cli"
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
fi
cd "$ROOT_DIR/packages/app-cli"
npm start -- --profile "$PROFILE_DIR" batch "$CMD_FILE"
cd "$ROOT_DIR/packages/app-desktop"
npm start -- --profile "$PROFILE_DIR"
@@ -1,11 +1,14 @@
import { MarkupLanguageUtils as BaseMarkupLanguageUtils } from '@joplin/lib/markupLanguageUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { contentScriptsToRendererRules } from '@joplin/lib/services/plugins/utils/loadContentScripts';
import { Options } from '@joplin/renderer/MarkupToHtml';
class MarkupLanguageUtils extends BaseMarkupLanguageUtils {
public newMarkupToHtml(plugins: PluginStates, options: any = null) {
return super.newMarkupToHtml({
public newMarkupToHtml(plugins: PluginStates = null, options: Options = null) {
plugins = plugins || {};
return super.newMarkupToHtml(null, {
extraRendererRules: contentScriptsToRendererRules(plugins),
...options,
});
+2 -2
View File
@@ -141,8 +141,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097631
versionName "2.0.0"
versionCode 2097632
versionName "2.0.1"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
@@ -8,12 +8,6 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- RN-NOTIFICATION -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- /RN-NOTIFICATION -->
<!-- Make these features optional to enable Chromebooks -->
<!-- https://github.com/laurent22/joplin/issues/37 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
@@ -41,35 +35,6 @@
android:resizeableActivity="true"
android:theme="@style/AppTheme">
<!-- RN-NOTIFICATION -->
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="ACTION_DISMISS" />
<action android:name="ACTION_SNOOZE" />
</intent-filter>
</receiver>
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmDismissReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name="com.emekalites.react.alarm.notification.AlarmBootReceiver"
android:directBootAware="true"
android:enabled="false"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- /RN-NOTIFICATION -->
<!--
2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification
Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed.
@@ -3,8 +3,13 @@ const shared = require('@joplin/lib/components/shared/note-screen-shared');
export default function useOnMessage(onCheckboxChange: Function, noteBody: string, onMarkForDownload: Function, onJoplinLinkClick: Function, onResourceLongPress: Function) {
return useCallback((event: any) => {
// Since RN 58 (or 59) messages are now escaped twice???
const msg = unescape(unescape(event.nativeEvent.data));
// 2021-05-19: Historically this was unescaped twice as it was
// apparently needed after an upgrade to RN 58 (or 59). However this is
// no longer needed and in fact would break certain URLs so it can be
// removed. Keeping the comment here anyway in case we find some URLs
// that end up being broken after removing the double unescaping.
// https://github.com/laurent22/joplin/issues/4494
const msg = event.nativeEvent.data;
console.info('Got IPC message: ', msg);
+4 -4
View File
@@ -240,7 +240,7 @@ PODS:
- React-cxxreact (= 0.63.3)
- React-jsi (= 0.63.3)
- React-jsinspector (0.63.3)
- react-native-alarm-notification (1.7.1):
- react-native-alarm-notification (1.0.3):
- React
- react-native-camera (3.40.0):
- React-Core
@@ -391,7 +391,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-alarm-notification (from `../node_modules/react-native-alarm-notification`)
- react-native-alarm-notification (from `../node_modules/joplin-rn-alarm-notification`)
- react-native-camera (from `../node_modules/react-native-camera`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
@@ -475,7 +475,7 @@ EXTERNAL SOURCES:
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-alarm-notification:
:path: "../node_modules/react-native-alarm-notification"
:path: "../node_modules/joplin-rn-alarm-notification"
react-native-camera:
:path: "../node_modules/react-native-camera"
react-native-document-picker:
@@ -568,7 +568,7 @@ SPEC CHECKSUMS:
React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
react-native-alarm-notification: 8f2adb4521bf7ca867278d4c236553e154dbbbc6
react-native-alarm-notification: 466e4ad56fbd948ecac26e657f292dca8bf483d5
react-native-camera: 5c1fbfecf63b802b8ca4a71c60d30a71550fb348
react-native-document-picker: b3e78a8f7fef98b5cb069f20fc35797d55e68e28
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
+1 -1
View File
@@ -23,6 +23,7 @@
"@react-native-community/slider": "^3.0.3",
"buffer": "^5.0.8",
"events": "^3.2.0",
"joplin-rn-alarm-notification": "^1.0.3",
"jsc-android": "241213.1.0",
"md5": "^2.2.1",
"prop-types": "^15.6.0",
@@ -30,7 +31,6 @@
"react": "16.13.1",
"react-native": "0.63.3",
"react-native-action-button": "^2.8.5",
"react-native-alarm-notification": "^1.7.1",
"react-native-camera": "^3.40.0",
"react-native-dialogbox": "^0.6.10",
"react-native-document-picker": "^4.0.0",
+5 -2
View File
@@ -2,7 +2,7 @@ const React = require('react');
import shim from '@joplin/lib/shim';
shim.setReact(React);
import setUpQuickActions from './setUpQuickActions';
import setupQuickActions from './setupQuickActions';
import PluginAssetsLoader from './PluginAssetsLoader';
import AlarmService from '@joplin/lib/services/AlarmService';
import Alarm from '@joplin/lib/models/Alarm';
@@ -98,6 +98,7 @@ import MigrationService from '@joplin/lib/services/MigrationService';
import { clearSharedFilesCache } from './utils/ShareUtils';
import setIgnoreTlsErrors from './utils/TlsUtils';
import ShareService from '@joplin/lib/services/share/ShareService';
import setupNotifications from './utils/setupNotifications';
let storeDispatch = function(_action: any) {};
@@ -715,7 +716,9 @@ class AppComponent extends React.Component {
await this.handleShareData();
setUpQuickActions(this.props.dispatch, this.props.selectedFolderId);
setupQuickActions(this.props.dispatch, this.props.selectedFolderId);
await setupNotifications(this.props.dispatch);
}
componentWillUnmount() {
@@ -1,7 +1,7 @@
import Logger from '@joplin/lib/Logger';
import { Notification } from '@joplin/lib/models/Alarm';
const ReactNativeAN = require('react-native-alarm-notification').default;
const ReactNativeAN = require('joplin-rn-alarm-notification').default;
export default class AlarmServiceDriver {
@@ -30,9 +30,11 @@ export default class AlarmServiceDriver {
// Returns -1 if could not be found
private alarmJoplinAlarmId(alarm: any): number {
if (!alarm.data) return -1;
const m = alarm.data.match(/joplinNotificationId==>(\d+)/);
return m ? Number(m[1]) : -1;
if (!alarm.data || !alarm.data.joplinNotificationId) {
return -1;
} else {
return alarm.data.joplinNotificationId;
}
}
private async alarmByJoplinNotificationId(joplinNotificationId: number) {
@@ -51,9 +53,12 @@ export default class AlarmServiceDriver {
title: notification.title,
message: notification.body ? notification.body : '-', // Required
channel: 'net.cozic.joplin.notification',
small_icon: 'ic_launcher',
color: 'white',
data: { joplinNotificationId: `${notification.id}` },
small_icon: 'ic_launcher_foreground', // Android requires the icon to be transparent
color: 'blue',
data: {
joplinNotificationId: `${notification.id}`,
noteId: notification.noteId,
},
};
// ReactNativeAN expects a string as a date and it seems this utility
@@ -0,0 +1,37 @@
import { Dispatch } from 'redux';
const { NativeEventEmitter, NativeModules, Platform } = require('react-native');
interface NotificationData {
joplinNotificationId: string;
noteId: string;
}
export default async (dispatch: Dispatch) => {
if (Platform.OS === 'android') {
const RNAlarmNotification = NativeModules.RNAlarmNotification;
const RNAlarmEmitter = new NativeEventEmitter(RNAlarmNotification);
const handleNotification = async (notification: NotificationData) => {
if (notification) {
const noteId = notification.noteId;
if (noteId) {
dispatch({ type: 'NAV_BACK' });
dispatch({ type: 'SIDE_MENU_CLOSE' });
dispatch({
type: 'NAV_GO',
noteId: noteId,
routeName: 'Note',
});
}
}
};
// receive notification click events when the app is running
RNAlarmEmitter.addListener('OnNotificationOpened', handleNotification);
// retrieve notification info if the app was started after the user clicked notification
const notification = await RNAlarmNotification.getAlarmInfo();
await handleNotification(notification);
}
};
@@ -1,9 +1,4 @@
/* eslint-disable no-unused-vars */
const time = require('@joplin/lib/time').default;
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const ArrayUtils = require('@joplin/lib/ArrayUtils');
const ArrayUtils = require('./ArrayUtils');
describe('ArrayUtils', function() {
@@ -1,5 +1,5 @@
import InMemoryCache from '@joplin/lib/InMemoryCache';
import time from '@joplin/lib/time';
import InMemoryCache from './InMemoryCache';
import time from './time';
describe('InMemoryCache', function() {
@@ -1,6 +1,6 @@
/* eslint-disable no-unused-vars */
const StringUtils = require('@joplin/lib/string-utils');
const StringUtils = require('./string-utils');
describe('StringUtils', function() {
+17 -1
View File
@@ -28,6 +28,22 @@ interface RemoteItem {
type_?: number;
}
function isCannotSyncError(error: any): boolean {
if (!error) return false;
if (['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) return true;
// If the request times out we give up too because sometimes it's due to the
// file being large or some other connection issues, and we don't want that
// file to block the sync process. The user can choose to retry later on.
//
// message: "network timeout at: .....
// name: "FetchError"
// type: "request-timeout"
if (error.type === 'request-timeout' || error.message.includes('network timeout')) return true;
return false;
}
export default class Synchronizer {
private db_: any;
@@ -514,7 +530,7 @@ export default class Synchronizer {
await this.apiCall('put', remoteContentPath, null, { path: localResourceContentPath, source: 'file', shareId: local.share_id });
} catch (error) {
if (error && ['rejectedByTarget', 'fileNotFound'].indexOf(error.code) >= 0) {
if (isCannotSyncError(error)) {
await handleCannotSyncItem(ItemClass, syncTargetId, local, error.message);
action = null;
} else {
@@ -1,8 +1,5 @@
/* eslint-disable no-unused-vars */
const { fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('./test-utils.js');
const TaskQueue = require('@joplin/lib/TaskQueue.js');
const { setupDatabaseAndSynchronizer, sleep, switchClient } = require('./testing/test-utils.js');
const TaskQueue = require('./TaskQueue.js');
describe('TaskQueue', function() {
+22
View File
@@ -0,0 +1,22 @@
const { setupDatabaseAndSynchronizer, switchClient } = require('./testing/test-utils.js');
const BaseModel = require('./BaseModel').default;
describe('database', function() {
beforeEach(async (done) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
});
it('should not modify cached field names', (async () => {
const db = BaseModel.db();
const fieldNames = db.tableFieldNames('notes');
const fieldCount = fieldNames.length;
fieldNames.push('type_');
expect(fieldCount).toBeGreaterThan(0);
expect(db.tableFieldNames('notes').length).toBe(fieldCount);
}));
});
@@ -1,8 +1,8 @@
'use strict';
const { checkThrow } = require('./test-utils.js');
const eventManager = require('@joplin/lib/eventManager').default;
const { checkThrow } = require('./testing/test-utils.js');
const eventManager = require('./eventManager').default;
describe('eventManager', function() {
+12 -3
View File
@@ -1,3 +1,4 @@
import JoplinError from './JoplinError';
import JoplinServerApi from './JoplinServerApi';
import { trimSlashes } from './path-utils';
@@ -148,9 +149,17 @@ export default class FileApiDriverJoplinServer {
}
public async put(path: string, content: any, options: any = null) {
return this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
'Content-Type': 'application/octet-stream',
}, options);
try {
const output = await this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, options && options.shareId ? { share_id: options.shareId } : null, content, {
'Content-Type': 'application/octet-stream',
}, options);
return output;
} catch (error) {
if (error.code === 413) {
throw new JoplinError(error.message, 'rejectedByTarget');
}
throw error;
}
}
public async delete(path: string) {
@@ -1,4 +1,4 @@
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, fileApi } from './test-utils';
import { afterAllCleanUp, setupDatabaseAndSynchronizer, switchClient, fileApi } from './testing/test-utils';
describe('file-api-driver', function() {
@@ -1,6 +1,6 @@
import FsDriverNode from '@joplin/lib/fs-driver-node';
import shim from '@joplin/lib/shim';
const { expectThrow } = require('./test-utils.js');
import FsDriverNode from './fs-driver-node';
import shim from './shim';
const { expectThrow } = require('./testing/test-utils.js');
// On Windows, path.resolve is going to convert a path such as
// /tmp/file.txt to c:\tmp\file.txt
+28
View File
@@ -0,0 +1,28 @@
import { plainTextToHtml } from './htmlUtils';
describe('htmlUtils', () => {
test('should convert a plain text string to its HTML equivalent', () => {
const testCases = [
[
'',
'',
],
[
'line 1\nline 2',
'<p>line 1</p><p>line 2</p>',
],
[
'<img onerror="http://downloadmalware.com"/>',
'&lt;img onerror=&quot;http://downloadmalware.com&quot;/&gt;',
],
];
for (const t of testCases) {
const [input, expected] = t;
const actual = plainTextToHtml(input);
expect(actual).toBe(expected);
}
});
});
+14
View File
@@ -2,6 +2,7 @@ const urlUtils = require('./urlUtils.js');
const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = new Entities().encode;
const htmlparser2 = require('@joplin/fork-htmlparser2');
const { escapeHtml } = require('./string-utils.js');
// [\s\S] instead of . for multiline matching
// https://stackoverflow.com/a/16119722/561309
@@ -153,3 +154,16 @@ class HtmlUtils {
}
export default new HtmlUtils();
export function plainTextToHtml(plainText: string): string {
const lines = plainText
.replace(/[\n\r]/g, '\n')
.split('\n');
const lineOpenTag = lines.length > 1 ? '<p>' : '';
const lineCloseTag = lines.length > 1 ? '</p>' : '';
return lines
.map(line => lineOpenTag + escapeHtml(line) + lineCloseTag)
.join('');
}
@@ -1,4 +1,4 @@
import htmlUtils from '@joplin/lib/htmlUtils';
import htmlUtils from './htmlUtils';
describe('htmlUtils', function() {
@@ -1,17 +1,17 @@
import { NoteEntity, ResourceEntity, TagEntity } from '@joplin/lib/services/database/types';
import shim from '@joplin/lib/shim';
import { NoteEntity, ResourceEntity, TagEntity } from './services/database/types';
import shim from './shim';
const fs = require('fs-extra');
const os = require('os');
const { filename } = require('@joplin/lib/path-utils');
const { setupDatabaseAndSynchronizer, switchClient, expectNotThrow } = require('./test-utils.js');
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
const { importEnex } = require('@joplin/lib/import-enex');
import Note from '@joplin/lib/models/Note';
import Tag from '@joplin/lib/models/Tag';
import Resource from '@joplin/lib/models/Resource';
const { filename } = require('./path-utils');
const { setupDatabaseAndSynchronizer, switchClient, expectNotThrow, supportDir } = require('./testing/test-utils.js');
const { enexXmlToMd } = require('./import-enex-md-gen.js');
const { importEnex } = require('./import-enex');
import Note from './models/Note';
import Tag from './models/Tag';
import Resource from './models/Resource';
const enexSampleBaseDir = `${__dirname}/enex_to_md`;
const enexSampleBaseDir = `${supportDir}/../enex_to_md`;
describe('EnexToMd', function() {
+71 -32
View File
@@ -31,6 +31,7 @@ interface Section {
interface ParserStateTag {
name: string;
visible: boolean;
isCodeBlock: boolean;
}
interface ParserStateList {
@@ -443,6 +444,20 @@ function isInvisibleBlock(context: any, attributes: any) {
return display && display.indexOf('none') === 0;
}
function trimBlockOpenAndClose(lines: string[]): string[] {
const output = lines.slice();
while (output.length && [BLOCK_OPEN, BLOCK_CLOSE, ''].includes(output[0])) {
output.splice(0, 1);
}
while (output.length && [BLOCK_OPEN, BLOCK_CLOSE, ''].includes(output[output.length - 1])) {
output.pop();
}
return output;
}
function isSpanWithStyle(attributes: any) {
if (attributes != undefined) {
if ('style' in attributes) {
@@ -484,6 +499,16 @@ function displaySaxWarning(context: any, message: string) {
console.warn(line.join(': '));
}
function isCodeBlock(context: any, nodeName: string, attributes: any) {
if (nodeName === 'code') return true;
if (attributes && attributes.style) {
const enCodeBlock = cssValue(context, attributes.style, '-en-codeblock');
if (enCodeBlock && enCodeBlock.toLowerCase() === 'true') return true;
}
return false;
}
// function removeSectionParent(section:Section | string) {
// if (typeof section === 'string') return section;
@@ -575,11 +600,13 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
const nodeAttributes = attributeToLowerCase(node);
const n = node.name.toLowerCase();
const isVisible = !isInvisibleBlock(this, nodeAttributes);
state.tags.push({
const tagInfo: ParserStateTag = {
name: n,
visible: isVisible,
});
isCodeBlock: isCodeBlock(this, n, nodeAttributes),
};
state.tags.push(tagInfo);
const currentList = state.lists && state.lists.length ? state.lists[state.lists.length - 1] : null;
@@ -673,6 +700,25 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
lines: [],
parent: section,
};
section.lines.push(newSection);
section = newSection;
} else if (tagInfo.isCodeBlock) {
// state.inPre = false;
// const previousIsPre = state.tags.length ? state.tags[state.tags.length - 1].name === 'pre' : false;
// if (previousIsPre) {
// section.lines.pop();
// }
state.inCode.push(true);
state.currentCode = '';
const newSection: Section = {
type: SectionType.Code,
lines: [],
parent: section,
};
section.lines.push(newSection);
section = newSection;
} else if (isBlockTag(n)) {
@@ -750,18 +796,6 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
} else if (n == 'blockquote') {
section.lines.push(BLOCK_OPEN);
state.inQuote = true;
} else if (n === 'code') {
state.inCode.push(true);
state.currentCode = '';
const newSection: Section = {
type: SectionType.Code,
lines: [],
parent: section,
};
section.lines.push(newSection);
section = newSection;
} else if (n === 'pre') {
section.lines.push(BLOCK_OPEN);
state.inPre = true;
@@ -871,6 +905,28 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
// End of note
} else if (!poppedTag.visible) {
if (section && section.parent) section = section.parent;
} else if (poppedTag.isCodeBlock) {
state.inCode.pop();
if (!state.inCode.length) {
// When a codeblock is wrapped in <pre><code>, it will have
// extra empty lines added by the "pre" logic, but since we
// are in a codeblock we should actually trim those.
const codeLines = trimBlockOpenAndClose(processMdArrayNewLines(section.lines).split('\n'));
section.lines = [];
if (codeLines.length > 1) {
section.lines.push('\n\n```\n');
for (let i = 0; i < codeLines.length; i++) {
if (i > 0) section.lines.push('\n');
section.lines.push(codeLines[i]);
}
section.lines.push('\n```\n\n');
} else {
section.lines.push(`\`${markdownUtils.escapeInlineCode(codeLines.join(''))}\``);
}
if (section && section.parent) section = section.parent;
}
} else if (isNewLineOnlyEndTag(n)) {
section.lines.push(BLOCK_CLOSE);
} else if (n == 'td' || n == 'th') {
@@ -897,23 +953,6 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
} else if (n == 'blockquote') {
section.lines.push(BLOCK_OPEN);
state.inQuote = false;
} else if (n === 'code') {
state.inCode.pop();
if (!state.inCode.length) {
const codeLines = processMdArrayNewLines(section.lines).split('\n');
section.lines = [];
if (codeLines.length > 1) {
for (let i = 0; i < codeLines.length; i++) {
if (i > 0) section.lines.push('\n');
section.lines.push(`\t${codeLines[i]}`);
}
} else {
section.lines.push(`\`${codeLines.join('')}\``);
}
if (section && section.parent) section = section.parent;
}
} else if (n === 'pre') {
state.inPre = false;
section.lines.push(BLOCK_CLOSE);
+3
View File
@@ -10,4 +10,7 @@ module.exports = {
],
testEnvironment: 'node',
setupFilesAfterEnv: [`${__dirname}/jest.setup.js`],
slowTestThreshold: 40,
};
+18
View File
@@ -0,0 +1,18 @@
const { afterEachCleanUp } = require('./testing/test-utils.js');
const { shimInit } = require('./shim-init-node.js');
const shim = require('./shim').default;
const sharp = require('sharp');
let keytar;
try {
keytar = shim.platformSupportsKeyChain() ? require('keytar') : null;
} catch (error) {
console.error('Cannot load keytar - keychain support will be disabled', error);
keytar = null;
}
shimInit(sharp, keytar);
global.afterEach(async () => {
await afterEachCleanUp();
});
+1 -1
View File
@@ -578,7 +578,7 @@ function localesFromLanguageCode(languageCode: string, locales: string[]): strin
});
}
function _(s: string, ...args: any[]) {
function _(s: string, ...args: any[]): string {
const strings = localeStrings(currentLocale_);
let result = strings[s];
if (result === '' || result === undefined) result = s;
+1 -1
View File
@@ -1 +1 @@
{"OK":"موافق","Cancel":"إلغاء","The app is now going to close. Please relaunch it to complete the process.":"سيتم إغلاق التطبيق الآن. يرجى إعادة تشغيله لإكمال العملية.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"اكتب عنوان ملاحظة أو جزء من محتواه للانتقال إليه. أو اكتب # متبوعاً بإسم وسم، أو @ متبوعاً باسم دفتر ملاحظات. أو اكتب : للبحث عن الأوامر.","Goto Anything...":"اذهب إلى أي شيء...","Command palette":"لوحة الأوامر","Exporting to \"%s\" as \"%s\" format. Please wait...":"التصدير إلى \"%s\" بتنسيق \"%s\". فضلاً انتظر...","Could not export notes: %s":"تعذر تصدير الملاحظات: %s","Current version is up-to-date.":"النسخة الحالية محدَّثة.","%s (pre-release)":"%s (ما قبل الإصدار)","An update is available, do you want to download it now?":"يتوفر تحديث ، هل تريد تنزيله الآن؟","Your version: %s":"النسخة لديك: %s","New version: %s":"النسخة الجديدة: %s","Download":"تحميل","The note \"%s\" has been successfully restored to the notebook \"%s\".":"تمت استعادة الملاحظة \"%s\" بنجاح إلى دفتر الملاحظات \"%s\".","This note has no history":"هذه المذكرة ليس لها تاريخ","Unsupported link or message: %s":"رابط أو رسالة غير مدعومان: %s","Restore":"استعادة","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"انقر \"%s\" لاستعادة الملاحظة. سيتم نسخه في دفتر الملاحظات المسمى \"%s\". لن يتم استبدال أو تعديل الإصدار الحالي من الملاحظة.","Back":"عودة","Remove":"إزالة","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"حذف دفتر الملاحظات \"%s\" ؟\n\nسيتم حذف كل الملاحظات و دفاتر الملاحظات الفرعية ضمن هذا الدفتر أيضاً.","Delete":"حذف","Remove tag \"%s\" from all notes?":"إزالة الوسم \"%s\" من كل الملاحظات؟","Remove this search from the sidebar?":"حذف هذا البحث من الشريط الجانبي؟","Export":"تصدير","All notes":"جميع الملاحظات","Synchronise":"زامِن","Notebooks":"دفاتر ملاحظات","Tags":"وسوم","Decrypting items: %d/%d":"فك تشفير العناصر: %d/%d","Fetching resources: %d/%d":"جلب الموارد: %d/%d","Sidebar":"الشريط الجانبي","Focus":"تركيز","Please select where the sync status should be exported to":"فضلاً اختر أين سيتم تصدير حالة المزامنة","Retry All":"إعادة محاولة الجميع","Retry":"إعادة المحاولة","strong text":"نص مشدَّد","emphasised text":"نص مشدَّد علية","Insert Hyperlink":"إدراج ارتباط تشعبي","List item":"عنصر قائمة","Layout":"تخطيط","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"ليس لهذه الملاحظة أي محتوى. اضغط على \"%s\" لتبديل المحرّر وتحرير الملاحظة.","Cut":"قص","Copy":"نسخ","Paste":"لصق","Checkbox list":"قائمة خانات اختيار","Highlight":"تَمييز النَص","Strikethrough":"توسيط خط في النص","Insert":"إدخال","Superscript":"أحرف فوقية","Subscript":"حرف تحتي","Edit":"تحرير","Attach file":"إرفاق ملف","Code Block":"كتلة للغة البرمجة","Inline Code":"نص لغة برمجة في النسق","Insert Date Time":"إدراج التاريخ والوقت","Drop notes or files here":"قم بإسقاط الملاحظات أو الملفات هنا","Code View":"عرض بلغة البرمجة","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"يرجى الانتظار حتى يتم تنزيل جميع المرفقات وفك تشفيرها. يمكنك أيضًا التبديل إلى %s لتحرير الملاحظة.","There was an error downloading this attachment:":"حدث خطأ أثناء تنزيل هذا المرفق:","This attachment is not downloaded or not decrypted yet":"لم يجر تنزيل هذا المرفق أو فك تشفيره حتى الآن","Open...":"فتح...","Save as...":"حفظ باسم...","Reveal file in folder":"اضهار الملف في المجلد","Copy path to clipboard":"نسخ المسار إلى الحافظة","Copy Link Address":"نسخ عنوان الرابط","to-do":"قائمة مهام","note":Line truncated
{"OK":"موافق","Cancel":"إلغاء","The app is now going to close. Please relaunch it to complete the process.":"سيتم إغلاق التطبيق الآن. يرجى إعادة تشغيله لإكمال العملية.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"اكتب عنوان ملاحظة أو جزء من محتواه للانتقال إليه. أو اكتب # متبوعاً بإسم وسم، أو @ متبوعاً باسم دفتر ملاحظات. أو اكتب : للبحث عن الأوامر.","Goto Anything...":"اذهب إلى أي شيء...","Command palette":"لوحة الأوامر","Exporting to \"%s\" as \"%s\" format. Please wait...":"التصدير إلى \"%s\" بتنسيق \"%s\". فضلاً انتظر...","Could not export notes: %s":"تعذر تصدير الملاحظات: %s","Current version is up-to-date.":"النسخة الحالية محدَّثة.","%s (pre-release)":"%s (ما قبل الإصدار)","An update is available, do you want to download it now?":"يتوفر تحديث ، هل تريد تنزيله الآن؟","Your version: %s":"النسخة لديك: %s","New version: %s":"النسخة الجديدة: %s","Download":"تحميل","The note \"%s\" has been successfully restored to the notebook \"%s\".":"تمت استعادة الملاحظة \"%s\" بنجاح إلى دفتر الملاحظات \"%s\".","This note has no history":"هذه المذكرة ليس لها تاريخ","Unsupported link or message: %s":"رابط أو رسالة غير مدعومان: %s","Restore":"استعادة","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"انقر \"%s\" لاستعادة الملاحظة. سيتم نسخه في دفتر الملاحظات المسمى \"%s\". لن يتم استبدال أو تعديل الإصدار الحالي من الملاحظة.","Back":"عودة","Remove":"إزالة","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"حذف دفتر الملاحظات \"%s\" ؟\n\nسيتم حذف كل الملاحظات و دفاتر الملاحظات الفرعية ضمن هذا الدفتر أيضاً.","Delete":"حذف","Remove tag \"%s\" from all notes?":"إزالة الوسم \"%s\" من كل الملاحظات؟","Remove this search from the sidebar?":"حذف هذا البحث من الشريط الجانبي؟","Export":"تصدير","All notes":"جميع الملاحظات","Synchronise":"زامِن","Notebooks":"دفاتر ملاحظات","Tags":"وسوم","Decrypting items: %d/%d":"فك تشفير العناصر: %d/%d","Fetching resources: %d/%d":"جلب الموارد: %d/%d","Sidebar":"الشريط الجانبي","Focus":"تركيز","Please select where the sync status should be exported to":"فضلاً اختر أين سيتم تصدير حالة المزامنة","Retry All":"إعادة محاولة الجميع","Retry":"إعادة المحاولة","strong text":"نص مشدَّد","emphasised text":"نص مشدَّد علية","Insert Hyperlink":"إدراج ارتباط تشعبي","List item":"عنصر قائمة","Layout":"تخطيط","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"ليس لهذه الملاحظة أي محتوى. اضغط على \"%s\" لتبديل المحرّر وتحرير الملاحظة.","Cut":"قص","Copy":"نسخ","Paste":"لصق","Checkbox list":"قائمة خانات اختيار","Highlight":"تَمييز النَص","Strikethrough":"توسيط خط في النص","Insert":"إدخال","Superscript":"أحرف فوقية","Subscript":"حرف تحتي","Edit":"تحرير","Attach file":"إرفاق ملف","Code Block":"كتلة للغة البرمجة","Inline Code":"نص لغة برمجة في النسق","Insert Date Time":"إدراج التاريخ والوقت","Drop notes or files here":"قم بإسقاط الملاحظات أو الملفات هنا","Code View":"عرض بلغة البرمجة","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"يرجى الانتظار حتى يتم تنزيل جميع المرفقات وفك تشفيرها. يمكنك أيضًا التبديل إلى %s لتحرير الملاحظة.","There was an error downloading this attachment:":"حدث خطأ أثناء تنزيل هذا المرفق:","This attachment is not downloaded or not decrypted yet":"لم يجر تنزيل هذا المرفق أو فك تشفيره حتى الآن","Open...":"فتح...","Save as...":"حفظ باسم...","Reveal file in folder":"اضهار الملف في المجلد","Copy path to clipboard":"نسخ المسار إلى الحافظة","Copy Link Address":"نسخ عنوان الرابط","to-do":"قائمة مهام","note":Line truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"Добре","Cancel":"Отмени","Goto Anything...":"Отиди на нещо...","Exporting to \"%s\" as \"%s\" format. Please wait...":"Експортирам към \"%s\" във формат \"%s\". Моля изчакайте...","Current version is up-to-date.":"Настоящата версия е най-новата.","%s (pre-release)":"%s (експериментална)","An update is available, do you want to download it now?":"Има обновление, искате ли да го свалите сега?","Your version: %s":"Вашата версия: %s","New version: %s":"Нова версия: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Бележка \"%s\" бе успешно създадена и сложена в тетрадка \"%s\".","This note has no history":"Тази бележка няма история","Unsupported link or message: %s":"Неподдържан линк или съобщение: %s","Restore":"Възстанови","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Натиснете \"%s\" за да възстановите бележката. Тя ще бъде копирана в тетрадка с име \"%s\". Настоящата версия на бележката няма да бъде подменена или променена.","Back":"Назад","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Да изтрия ли тетрадка \"%s\"?\n\nВсички бележки и под-тетрадки също ще бъдат изтрити.","Delete":"Изтрий","Remove tag \"%s\" from all notes?":"Да премахна ли таг \"%s\" от всички бележки?","Remove this search from the sidebar?":"Да премахна ли това търсене от страничния панел?","Export":"Експортиране","Synchronise":"Синхронизация","Notebooks":"Тетрадки","Tags":"Тагове","Decrypting items: %d/%d":"Декриптиране на обекти: %d/%d","Fetching resources: %d/%d":"Взимане на ресурси: %d/%d","Sidebar":"Страничен панел","Focus":"Фокусирай","Please select where the sync status should be exported to":"Моля изберете къде да бъде експортиран статуса на синхронизацията","Retry":"Нов опит","strong text":"силно наблегнат текст","Insert Hyperlink":"Вкарай линк","List item":"Елемент на списък","Layout":"Разположеня","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Тази бележка няма съдържание. Натиснете \"%s\" за да покажете редакторя и да редактирате бележката.","Cut":"Изрежи","Copy":"Копирай","Paste":"Постави","Edit":"Редактиране","Attach file":"Прикачи файл","Insert Date Time":"Постави датата и часа","Open...":"Отвори...","Save as...":"Запазаи като...","Copy path to clipboard":"Копирай пътя в клипборда","Copy Link Address":"Копирай адреса на линка","to-do":"задача","note":"бележка","Creating new %s...":"Създаване на нова %s...","In: %s":"В: %s","Select all":"Избери всичко","Bold":"Удебелен","Italic":"Курсив","Hyperlink":"Линк","Code":"Код","Numbered List":"Номериран списък","Bulleted List":"Неномериран списък","Checkbox":"Поле за отметка","Heading":"Заглавие","Horizontal Rule":"Хоризонтална черта","Note title":"Заглавие на бележката","Search in current note":"Търсене в настоящата бележка","Note body":"Съдържание на бележката","ID":"Идентификатор","Created":"Създаден","Updated":"Обновен","Location":"Местоположение","URL":"Уеб адрес (URL)","Note History":"История на бележката","Previous versions of this note":"Предишни версии на бележката","Note properties":"Атрибути на бележката","Disabled":"Изключено","Search...":"Търсене...","Import":"Импортиране","Quit":"Изход","Website and documentation":"Уебсайт и документация","Close Window":"Затвори прозорец","Options":"Конфигурация","Save":"Запази","Some items cannot be synchronised.":"Някои обекти не можаха да бъдат синхронизирани.","View them now":"Покажи ги сега","One or more master keys need a password.":"Поне едиLine truncated
{"OK":"Добре","Cancel":"Отмени","Goto Anything...":"Отиди на нещо...","Exporting to \"%s\" as \"%s\" format. Please wait...":"Експортирам към \"%s\" във формат \"%s\". Моля изчакайте...","Current version is up-to-date.":"Настоящата версия е най-новата.","%s (pre-release)":"%s (експериментална)","An update is available, do you want to download it now?":"Има обновление, искате ли да го свалите сега?","Your version: %s":"Вашата версия: %s","New version: %s":"Нова версия: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Бележка \"%s\" бе успешно създадена и сложена в тетрадка \"%s\".","This note has no history":"Тази бележка няма история","Unsupported link or message: %s":"Неподдържан линк или съобщение: %s","Restore":"Възстанови","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Натиснете \"%s\" за да възстановите бележката. Тя ще бъде копирана в тетрадка с име \"%s\". Настоящата версия на бележката няма да бъде подменена или променена.","Back":"Назад","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Да изтрия ли тетрадка \"%s\"?\n\nВсички бележки и под-тетрадки също ще бъдат изтрити.","Delete":"Изтрий","Remove tag \"%s\" from all notes?":"Да премахна ли таг \"%s\" от всички бележки?","Remove this search from the sidebar?":"Да премахна ли това търсене от страничния панел?","Export":"Експортиране","Synchronise":"Синхронизация","Notebooks":"Тетрадки","Tags":"Тагове","Decrypting items: %d/%d":"Декриптиране на обекти: %d/%d","Fetching resources: %d/%d":"Взимане на ресурси: %d/%d","Sidebar":"Страничен панел","Focus":"Фокусирай","Please select where the sync status should be exported to":"Моля изберете къде да бъде експортиран статуса на синхронизацията","Retry":"Нов опит","strong text":"силно наблегнат текст","Insert Hyperlink":"Вкарай линк","List item":"Елемент на списък","Layout":"Разположеня","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Тази бележка няма съдържание. Натиснете \"%s\" за да покажете редакторя и да редактирате бележката.","Cut":"Изрежи","Copy":"Копирай","Paste":"Постави","Edit":"Редактиране","Attach file":"Прикачи файл","Insert Date Time":"Постави датата и часа","Open...":"Отвори...","Save as...":"Запазаи като...","Copy path to clipboard":"Копирай пътя в клипборда","Copy Link Address":"Копирай адреса на линка","to-do":"задача","note":"бележка","Creating new %s...":"Създаване на нова %s...","In: %s":"В: %s","Select all":"Избери всичко","Bold":"Удебелен","Italic":"Курсив","Hyperlink":"Линк","Code":"Код","Numbered List":"Номериран списък","Bulleted List":"Неномериран списък","Checkbox":"Поле за отметка","Heading":"Заглавие","Horizontal Rule":"Хоризонтална черта","Note title":"Заглавие на бележката","Search in current note":"Търсене в настоящата бележка","Note body":"Съдържание на бележката","ID":"Идентификатор","Created":"Създаден","Updated":"Обновен","Location":"Местоположение","URL":"Уеб адрес (URL)","Note History":"История на бележката","Previous versions of this note":"Предишни версии на бележката","Note properties":"Атрибути на бележката","Disabled":"Изключено","Search...":"Търсене...","Import":"Импортиране","Quit":"Изход","Website and documentation":"Уебсайт и документация","Close Window":"Затвори прозорец","Options":"Конфигурация","Save":"Запази","View them now":"Покажи ги сега","Some items cannot be synchronised.":"Някои обекти не можаха да бъдат синхронизирани.","One or more master keys need a password.":"Поне едиLine truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"Uredu","Cancel":"Otkaži","The app is now going to close. Please relaunch it to complete the process.":"Aplikacija ce se zatvoriti. Molimo ponovo otvorite da bi se radnja potpuno zavrsila.","Goto Anything...":"Pronađite bilo šta...","Current version is up-to-date.":"Trenutna je verzija ažurirana.","%s (pre-release)":"%s (verzija pred objavljivanje)","An update is available, do you want to download it now?":"Nova je verzija dostupna. Želite li je preuzeti?","Your version: %s":"Vaša verzija: %s","New version: %s":"Nova verzija: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Bilješka \"%s\" uspješno je rekonstruisana i pohranjena u bilježnicu \"%s\".","This note has no history":"Bilješka nema ranijih verzija","Unsupported link or message: %s":"Nepodržana veza ili poruka: %s","Restore":"Rekonstruiši","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Pritisnite na \"%s\" da rekonstruišete bilješku. Rezultat će biti kopiran u bilježnicu \"%s\". Trenutna verzija bilješke ostat će netaknuta.","Back":"Nazad","Remove":"Ukloni","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Izbrisati bilježnicu \"%s\"?\n\nSve bilješke i bilježnice unutar ove bilježnice također će biti izbrisane.","Delete":"Izbriši","Remove tag \"%s\" from all notes?":"Ukloniti oznaku \"%s\" iz svih bilješki?","Remove this search from the sidebar?":"Ukloniti ovu pretragu iz pomoćnog menija?","Export":"Iznesi","All notes":"Sve bilješke","Synchronise":"Sinhronizuj","Notebooks":"Bilježnice","Tags":"Oznake","Decrypting items: %d/%d":"Dešifrujem stavke: %d/%d","Fetching resources: %d/%d":"Preuzimam resurse: %d/%d","Sidebar":"Bočni meni","Focus":"Fokus","Please select where the sync status should be exported to":"Navedite gdje će biti pohranjen status sinhronizacije","Retry":"Ponovi","strong text":"podebljani tekst","Insert Hyperlink":"Unesi poveznicu","List item":"Stavka nabrajanja","Layout":"Prikaz","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Bilješka je prazna. Pritisnite na \"%s\" da otvorite program za rad s tekstom i uredite bilješku.","Cut":"Izreži","Copy":"Kopiraj","Paste":"Zalijepi","Highlight":"Oznaci","Edit":"Uredi","Attach file":"Priloži datoteku","Code Block":"Kōd (izdvojen)","Inline Code":"Kōd (unutar teksta)","Insert Date Time":"Unesi datum i vrijeme","Drop notes or files here":"Ispustite bilješke ili datoteke ovdje","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Pričekajte dok svi prilozi ne budu preuzeti i dešifrovani. Također možete otvoriti %s da uredite bilješku.","There was an error downloading this attachment:":"Došlo je do greške prilikom preuzimanja priloga:","Open...":"Otvori...","Save as...":"Pohrani kao...","Reveal file in folder":"Pokaži datoteku u direktoriju","Copy path to clipboard":"Kopiraj putanju u spremnik","Copy Link Address":"Kopiraj adresu veze","to-do":"zadatak","note":"bilješka","Creating new %s...":"Kreiram: %s...","This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.":"Ovaj Text editor ima par ogranicenja i preporucujemo da budete upoznati sa njima prije koristenja.","Read more about it":"Saznajte vise","Dismiss":"Odbaci","In: %s":"U: %s","Select all":"Označi sve","Bold":"Masna slova","Italic":"Kurziv","Hyperlink":"Poveznica","Code":"Kōd","Numbered List":"Numerisano nabrajanje","Bulleted List":"Nabrajanje","Checkbox":"Polje za kvačicu","Heading":"Naslov","Horizontal Rule":"Vodoravna linija","Sort selected lines":"Poredaj oznacene linije","Search in current note":"Potraži u trenutnoj bilješci","ID":"ID","Created":"Kreirano","Updated":"Ažurirano","Location":"Lokacija","URL":"URL","Note History":"Historija bilješke","Markup":"Markup","Previous versions of this note":"Ranije verzije ove bilješke","Note properties":"Svojstva bilješke","Disabled":"Isključeno","Search...":"Pretraživanje...","Import":"Unesi","Quit":"Zatvori","Actual Size":"Početne dimenzije","Website and documentation":"Internet stranica i dokumentacija","Close Window":"Zatvori prozor","Options":"Opcije","Save":"Pohrani","Safe mode is currently active. Note rendering and all plugins are temporarily disabled.":" ","Disable safe mode and restart":" ","Some items cannot be synchronised.":"Neke stavke nije moguće sinhronizovati.","View them now":"Pregledajte ih sada","One or more master keys need a password.":"Jedan ili više glavnih ključeva zahtijevaju lozinku.","Set the password":"Zadajte lozinku","Statistics...":"Statistika...","Rename":"Preimenuj","Rename notebook:":"Preimenujte bilježnicu:","New to-do":"Novi zadatak","Toggle sidebar":"Bočni meni","Set alarm":"Podesi zvučnu napomenu","Set alarm:":"Podesite zvučnu napomenu:","PDF File":"PDF datotLine truncated
{"OK":"Uredu","Cancel":"Otkaži","The app is now going to close. Please relaunch it to complete the process.":"Aplikacija ce se zatvoriti. Molimo ponovo otvorite da bi se radnja potpuno zavrsila.","Goto Anything...":"Pronađite bilo šta...","Current version is up-to-date.":"Trenutna je verzija ažurirana.","%s (pre-release)":"%s (verzija pred objavljivanje)","An update is available, do you want to download it now?":"Nova je verzija dostupna. Želite li je preuzeti?","Your version: %s":"Vaša verzija: %s","New version: %s":"Nova verzija: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Bilješka \"%s\" uspješno je rekonstruisana i pohranjena u bilježnicu \"%s\".","This note has no history":"Bilješka nema ranijih verzija","Unsupported link or message: %s":"Nepodržana veza ili poruka: %s","Restore":"Rekonstruiši","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Pritisnite na \"%s\" da rekonstruišete bilješku. Rezultat će biti kopiran u bilježnicu \"%s\". Trenutna verzija bilješke ostat će netaknuta.","Back":"Nazad","Remove":"Ukloni","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Izbrisati bilježnicu \"%s\"?\n\nSve bilješke i bilježnice unutar ove bilježnice također će biti izbrisane.","Delete":"Izbriši","Remove tag \"%s\" from all notes?":"Ukloniti oznaku \"%s\" iz svih bilješki?","Remove this search from the sidebar?":"Ukloniti ovu pretragu iz pomoćnog menija?","Export":"Iznesi","All notes":"Sve bilješke","Synchronise":"Sinhronizuj","Notebooks":"Bilježnice","Tags":"Oznake","Decrypting items: %d/%d":"Dešifrujem stavke: %d/%d","Fetching resources: %d/%d":"Preuzimam resurse: %d/%d","Sidebar":"Bočni meni","Focus":"Fokus","Please select where the sync status should be exported to":"Navedite gdje će biti pohranjen status sinhronizacije","Retry":"Ponovi","strong text":"podebljani tekst","Insert Hyperlink":"Unesi poveznicu","List item":"Stavka nabrajanja","Layout":"Prikaz","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Bilješka je prazna. Pritisnite na \"%s\" da otvorite program za rad s tekstom i uredite bilješku.","Cut":"Izreži","Copy":"Kopiraj","Paste":"Zalijepi","Highlight":"Oznaci","Edit":"Uredi","Attach file":"Priloži datoteku","Code Block":"Kōd (izdvojen)","Inline Code":"Kōd (unutar teksta)","Insert Date Time":"Unesi datum i vrijeme","Drop notes or files here":"Ispustite bilješke ili datoteke ovdje","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Pričekajte dok svi prilozi ne budu preuzeti i dešifrovani. Također možete otvoriti %s da uredite bilješku.","There was an error downloading this attachment:":"Došlo je do greške prilikom preuzimanja priloga:","Open...":"Otvori...","Save as...":"Pohrani kao...","Reveal file in folder":"Pokaži datoteku u direktoriju","Copy path to clipboard":"Kopiraj putanju u spremnik","Copy Link Address":"Kopiraj adresu veze","to-do":"zadatak","note":"bilješka","Creating new %s...":"Kreiram: %s...","This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.":"Ovaj Text editor ima par ogranicenja i preporucujemo da budete upoznati sa njima prije koristenja.","Read more about it":"Saznajte vise","Dismiss":"Odbaci","In: %s":"U: %s","Select all":"Označi sve","Bold":"Masna slova","Italic":"Kurziv","Hyperlink":"Poveznica","Code":"Kōd","Numbered List":"Numerisano nabrajanje","Bulleted List":"Nabrajanje","Checkbox":"Polje za kvačicu","Heading":"Naslov","Horizontal Rule":"Vodoravna linija","Sort selected lines":"Poredaj oznacene linije","Search in current note":"Potraži u trenutnoj bilješci","ID":"ID","Created":"Kreirano","Updated":"Ažurirano","Location":"Lokacija","URL":"URL","Note History":"Historija bilješke","Markup":"Markup","Previous versions of this note":"Ranije verzije ove bilješke","Note properties":"Svojstva bilješke","Disabled":"Isključeno","Search...":"Pretraživanje...","Import":"Unesi","Quit":"Zatvori","Actual Size":"Početne dimenzije","Website and documentation":"Internet stranica i dokumentacija","Close Window":"Zatvori prozor","Options":"Opcije","Save":"Pohrani","Safe mode is currently active. Note rendering and all plugins are temporarily disabled.":" ","Disable safe mode and restart":" ","View them now":"Pregledajte ih sada","Some items cannot be synchronised.":"Neke stavke nije moguće sinhronizovati.","One or more master keys need a password.":"Jedan ili više glavnih ključeva zahtijevaju lozinku.","Set the password":"Zadajte lozinku","Statistics...":"Statistika...","Rename":"Preimenuj","Rename notebook:":"Preimenujte bilježnicu:","New to-do":"Novi zadatak","Toggle sidebar":"Bočni meni","Set alarm":"Podesi zvučnu napomenu","Set alarm:":"Podesite zvučnu napomenu:","PDF File":"PDF datotLine truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"D'acord","Cancel":"Cancel·la","Goto Anything...":"Anar a qualsevol cosa...","Exporting to \"%s\" as \"%s\" format. Please wait...":"S'està exportant a «%s» com a format «%s». Espereu...","Could not export notes: %s":"No s'han pogut exportar les notes: %s","Current version is up-to-date.":"La versió actual està actualitzada.","%s (pre-release)":"%s (pre-llençament)","An update is available, do you want to download it now?":"Hi ha disponible una actualització. Voleu descarregar-la ara?","Your version: %s":"La vostra versió: %s","New version: %s":"Nova versió: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"La nota «%s» s'ha restaurat amb èxit en el bloc de notes «%s».","This note has no history":"Aquesta nota no té historial","Unsupported link or message: %s":"Missatge o enllaç no suportat: %s","Restore":"Restaurar","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Cliqueu «%s» per restaurar la nota. Aquesta serà copiada al el notebook anomenat «%s». La versió actual de la nota no serà substituïda o modificada.","Back":"Enrere","Remove":"Elimina","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Voleu suprimir el bloc de notes \"%s\"? \n\nTambé se suprimiran totes les notes i els sub-blocs d'aquest bloc de notes.","Delete":"Suprimeix","Remove tag \"%s\" from all notes?":"Voleu suprimir l'etiqueta \"%s\" de totes les notes?","Remove this search from the sidebar?":"Voleu suprimir aquesta cerca de la barra lateral?","Export":"Exporta","All notes":"Totes les notes","Synchronise":"Sincronitza","Notebooks":"Blocs de notes","Tags":"Etiquetes","Decrypting items: %d/%d":"S'estan desxifrant els elements: %d/%d","Fetching resources: %d/%d":"Obtenint recursos: %d/%d","Sidebar":"Barra lateral","Focus":"Focus","Please select where the sync status should be exported to":"Seleccioneu on s'hauria d'exportar l'estat de la sincronització","Retry All":"Torna a provar-los tots","Retry":"Torna a provar","strong text":"text destacat","emphasised text":"text ressaltat","Insert Hyperlink":"Introdueix un enllaç","List item":"Element de llista","Layout":"Disposició","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Aquesta nota no té contingut. Feu clic a «%s» per a anar a l'editor i modificar-la.","Cut":"Retalla","Copy":"Copia","Paste":"Enganxa","Checkbox list":"Llista de caselles de verificació","Edit":"Edita","Attach file":"Adjunta un fitxer","Code Block":"Bloc de codi","Inline Code":"Codi en línia","Insert Date Time":"Insereix la data i hora","Drop notes or files here":"Arrossega les notes o fitxers aquí","Code View":"Vista de codi","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Espereu que tots els adjunts hagin estat descarregats i desxifrats. També podeu canviar a %s per editar la nota.","There was an error downloading this attachment:":"Hi ha hagut un error al descarregar aquest adjunt:","This attachment is not downloaded or not decrypted yet":"Aquest adjunt no ha estat descarregat o desxifrat encara","Open...":"Obre...","Save as...":"Anomena i desa...","Reveal file in folder":"Mostra el fitxer a la carpeta","Copy path to clipboard":"Copia el camí al porta-retalls","Copy Link Address":"Copia l'adreça de l'enllaç","to-do":"tasques pendents","note":"nota","Creating new %s...":"S'està creant una %s...","In: %s":"A: %s","Select all":"Seleccioneu tot","Bold":"Negreta","Italic":"Cursiva","Hyperlink":"Enllaç","Code":"Codi","Numbered List":"Llista numerada","Bulleted List":"Llista de pics","Checkbox":"Casella de verificació","Heading":"Capçalera","Horizontal Rule":"Línia horitzontal","Note title":"Títol de la nota","Search in current note":"Cerca en la nota actual","Note body":"Cos de la nota","ID":"ID","Created":"Creació","Updated":"Actualització","Completed":"Finalitzat","Location":"Ubicació","URL":"URL","Note History":"Historial de la nota","Markup":"Marcatge","Previous versions of this note":"Versions prèvies d'aquesta nota","Note properties":"Propietats de la nota","Disabled":"Desactivat","Search...":"Cerca...","Import":"Importació","Quit":"Surt","Insert template":"Insereix plantilla","Actual Size":"Mida real","Website and documentation":"Lloc web i documentació","Close Window":"Tanca la finestra","Options":"Opcions","Save":"Desa","Some items cannot be synchronised.":"Alguns elements no s'han pogut sincronitzar.","View them now":"Mostra'ls ara","Some items cannot be decrypted.":"Alguns elements no s'han pogut desxifrar.","One or more master keys need a password.":"Una o més claus mestres necessiten una contrasenya.","Set the password":"Establiu la contrasenya","One of your master keys use an obsolete encryption method.":"Una de les vostres claus mestres utilitza un mètode de xifratge obsolet."Line truncated
{"OK":"D'acord","Cancel":"Cancel·la","Goto Anything...":"Anar a qualsevol cosa...","Exporting to \"%s\" as \"%s\" format. Please wait...":"S'està exportant a «%s» com a format «%s». Espereu...","Could not export notes: %s":"No s'han pogut exportar les notes: %s","Current version is up-to-date.":"La versió actual està actualitzada.","%s (pre-release)":"%s (pre-llençament)","An update is available, do you want to download it now?":"Hi ha disponible una actualització. Voleu descarregar-la ara?","Your version: %s":"La vostra versió: %s","New version: %s":"Nova versió: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"La nota «%s» s'ha restaurat amb èxit en el bloc de notes «%s».","This note has no history":"Aquesta nota no té historial","Unsupported link or message: %s":"Missatge o enllaç no suportat: %s","Restore":"Restaurar","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Cliqueu «%s» per restaurar la nota. Aquesta serà copiada al el notebook anomenat «%s». La versió actual de la nota no serà substituïda o modificada.","Back":"Enrere","Remove":"Elimina","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Voleu suprimir el bloc de notes \"%s\"? \n\nTambé se suprimiran totes les notes i els sub-blocs d'aquest bloc de notes.","Delete":"Suprimeix","Remove tag \"%s\" from all notes?":"Voleu suprimir l'etiqueta \"%s\" de totes les notes?","Remove this search from the sidebar?":"Voleu suprimir aquesta cerca de la barra lateral?","Export":"Exporta","All notes":"Totes les notes","Synchronise":"Sincronitza","Notebooks":"Blocs de notes","Tags":"Etiquetes","Decrypting items: %d/%d":"S'estan desxifrant els elements: %d/%d","Fetching resources: %d/%d":"Obtenint recursos: %d/%d","Sidebar":"Barra lateral","Focus":"Focus","Please select where the sync status should be exported to":"Seleccioneu on s'hauria d'exportar l'estat de la sincronització","Retry All":"Torna a provar-los tots","Retry":"Torna a provar","strong text":"text destacat","emphasised text":"text ressaltat","Insert Hyperlink":"Introdueix un enllaç","List item":"Element de llista","Layout":"Disposició","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Aquesta nota no té contingut. Feu clic a «%s» per a anar a l'editor i modificar-la.","Cut":"Retalla","Copy":"Copia","Paste":"Enganxa","Checkbox list":"Llista de caselles de verificació","Edit":"Edita","Attach file":"Adjunta un fitxer","Code Block":"Bloc de codi","Inline Code":"Codi en línia","Insert Date Time":"Insereix la data i hora","Drop notes or files here":"Arrossega les notes o fitxers aquí","Code View":"Vista de codi","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Espereu que tots els adjunts hagin estat descarregats i desxifrats. També podeu canviar a %s per editar la nota.","There was an error downloading this attachment:":"Hi ha hagut un error al descarregar aquest adjunt:","This attachment is not downloaded or not decrypted yet":"Aquest adjunt no ha estat descarregat o desxifrat encara","Open...":"Obre...","Save as...":"Anomena i desa...","Reveal file in folder":"Mostra el fitxer a la carpeta","Copy path to clipboard":"Copia el camí al porta-retalls","Copy Link Address":"Copia l'adreça de l'enllaç","to-do":"tasques pendents","note":"nota","Creating new %s...":"S'està creant una %s...","In: %s":"A: %s","Select all":"Seleccioneu tot","Bold":"Negreta","Italic":"Cursiva","Hyperlink":"Enllaç","Code":"Codi","Numbered List":"Llista numerada","Bulleted List":"Llista de pics","Checkbox":"Casella de verificació","Heading":"Capçalera","Horizontal Rule":"Línia horitzontal","Note title":"Títol de la nota","Search in current note":"Cerca en la nota actual","Note body":"Cos de la nota","ID":"ID","Created":"Creació","Updated":"Actualització","Completed":"Finalitzat","Location":"Ubicació","URL":"URL","Note History":"Historial de la nota","Markup":"Marcatge","Previous versions of this note":"Versions prèvies d'aquesta nota","Note properties":"Propietats de la nota","Disabled":"Desactivat","Search...":"Cerca...","Import":"Importació","Quit":"Surt","Insert template":"Insereix plantilla","Actual Size":"Mida real","Website and documentation":"Lloc web i documentació","Close Window":"Tanca la finestra","Options":"Opcions","Save":"Desa","Some items cannot be decrypted.":"Alguns elements no s'han pogut desxifrar.","View them now":"Mostra'ls ara","One of your master keys use an obsolete encryption method.":"Una de les vostres claus mestres utilitza un mètode de xifratge obsolet.","The default encryption method has been changed, you should re-encrypt your data.":"El mètode de xifratge per defecte s'ha canviat, hauríeu de tornar a xifrar les vostres dades.","More info":"Més informació","Some items caLine truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"OK","Cancel":"Zrušit","The app is now going to close. Please relaunch it to complete the process.":"Aplikace se nyní vypne. Pro dokončení procesu ji znovu spusťte.","Goto Anything...":"Přejít kamkoliv...","Exporting to \"%s\" as \"%s\" format. Please wait...":"Exportuji do \"%s\" jako formát \"%s\". Chvíli strpení...","Could not export notes: %s":"Nelze exportovat poznámky: %s","Current version is up-to-date.":"Současná verze je aktuální.","%s (pre-release)":"%s (pre-release)","An update is available, do you want to download it now?":"Je k dispozici update, chcete jej stáhnout?","Your version: %s":"Vaše verze: %s","New version: %s":"Nová verze: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Poznámka \"%s\" byla úspěšně obnovena v zápisníku \"%s\".","This note has no history":"Tato poznámka nemá historii","Unsupported link or message: %s":"Nepodporovaný link či zpráva: %s","Restore":"Obnovit","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Klikněte na \"%s\" pro obnovení poznámky. Poznámka bude zkopírována do zápisníku s názvem \"%s\". Současná verze poznámky nebude nahrazena či změněna.","Back":"Zpět","Remove":"Odstranit","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Smazat zápisník \"%s\"?\n\nBudou smazány i všechny poznámky a pod-zápisníky v tomto zápisníku.","Delete":"Odstranit","Remove tag \"%s\" from all notes?":"Odebrat tag \"%s\" ze všech poznámek?","Remove this search from the sidebar?":"Smazat tento hledaný výraz z panelu?","Export":"Export","All notes":"Všechny poznámky","Synchronise":"Synchronizovat","Notebooks":"Zápisníky","Tags":"Tagy","Decrypting items: %d/%d":"Rozšifrované položky: %d/%d","Fetching resources: %d/%d":"Stahování zdrojů: %d/%d","Sidebar":"Postranní lišta","Focus":"Přejít na","Please select where the sync status should be exported to":"Prosím vyberte, kam má být stav synchronizace exportován","Retry All":"Opakovat vše","Retry":"Znovu","strong text":"tučný text","emphasised text":"zvýrazněný text","Insert Hyperlink":"Vložit odkaz","List item":"Seznam položek","Layout":"Rozvržení","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Tato poznámka je prázdný. Klikněte na \"%s\" pro otevření editoru.","Cut":"Vyjmout","Copy":"Kopírovat","Paste":"Vložit","Checkbox list":"Zaškrtávací pole","Edit":"Upravit","Attach file":"Přiložit soubor","Code Block":"Blok kódu","Inline Code":"Vložený kód","Insert Date Time":"Vložit datum a čas","Drop notes or files here":"Sem přetáhněte poznámky nebo soubory","Code View":"Zobrazit kód","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Počkejte prosím na stažení a dešifrování všech příloh. Můžete také přepnout na %s a poznámku upravit.","There was an error downloading this attachment:":"Při stahování této přílohy došlo k chybě:","This attachment is not downloaded or not decrypted yet":"Tato příloha ještě není stažena nebo dešifrována","Open...":"Otevřít...","Save as...":"Uložit jako...","Reveal file in folder":"Ukázat soubor ve složce","Copy path to clipboard":"Kopírovat cestu do schránky","Copy Link Address":"Kopírovat adresu odkazu","to-do":"úkol","note":"poznámka","Creating new %s...":"Vytvářím %s...","The following attachments are being watched for changes:":"Následující přílohy jsou sledovány, zda došlo k jejich změně:","The attachments will no longer be watched when you switch to a different note.":"Když přepnete na jinou poznámku, přílohy již nebudou sledovány.","In: %s":"V: %s","Select all":"Vybrat vše","Bold":"Tučně","Italic":"Kurzíva","Hyperlink":"Odkaz","Code":"Kód","Numbered List":"Číslovanáý seznam","Bulleted List":"Seznam s odrážkami","Checkbox":"Zaškrtávací pole","Heading":"Nadpis","Horizontal Rule":"Horizontální čára","Note title":"Název zápisníku","Search in current note":"Hledat v aktuální poznámce","Note body":"Tělo poznámky","ID":"ID","Created":"Vytvořeno","Updated":"Upraveno","Completed":"Dokončeno","Location":"Lokace","URL":"URL","Note History":"Historie poznámky","Markup":"Markup","Previous versions of this note":"Předchozí verze poznámky","Note properties":"Nastavení poznámek","Error: %s":"Chyba: %s","Disabled":"Vypnuto","Search...":"Hledat...","Import":"Import","Command":"Příkaz","Keyboard Shortcut":"Klávesová zkratka","Quit":"Ukončit","Insert template":"Vložit šablonu","Actual Size":"Skutečná velikost","Website and documentation":"Web a dokumentace","Hide Joplin":"Skrýt Joplin","Close Window":"Zavřít akno","Preferences":"Nastavení","Options":"Nastavení","Press the shortcut":"Stiskněte klávesovou zkratku","Press the shortcut aLine truncated
{"OK":"OK","Cancel":"Zrušit","The app is now going to close. Please relaunch it to complete the process.":"Aplikace se nyní vypne. Pro dokončení procesu ji znovu spusťte.","Goto Anything...":"Přejít kamkoliv...","Exporting to \"%s\" as \"%s\" format. Please wait...":"Exportuji do \"%s\" jako formát \"%s\". Chvíli strpení...","Could not export notes: %s":"Nelze exportovat poznámky: %s","Current version is up-to-date.":"Současná verze je aktuální.","%s (pre-release)":"%s (pre-release)","An update is available, do you want to download it now?":"Je k dispozici update, chcete jej stáhnout?","Your version: %s":"Vaše verze: %s","New version: %s":"Nová verze: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Poznámka \"%s\" byla úspěšně obnovena v zápisníku \"%s\".","This note has no history":"Tato poznámka nemá historii","Unsupported link or message: %s":"Nepodporovaný link či zpráva: %s","Restore":"Obnovit","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Klikněte na \"%s\" pro obnovení poznámky. Poznámka bude zkopírována do zápisníku s názvem \"%s\". Současná verze poznámky nebude nahrazena či změněna.","Back":"Zpět","Remove":"Odstranit","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Smazat zápisník \"%s\"?\n\nBudou smazány i všechny poznámky a pod-zápisníky v tomto zápisníku.","Delete":"Odstranit","Remove tag \"%s\" from all notes?":"Odebrat tag \"%s\" ze všech poznámek?","Remove this search from the sidebar?":"Smazat tento hledaný výraz z panelu?","Export":"Export","All notes":"Všechny poznámky","Synchronise":"Synchronizovat","Notebooks":"Zápisníky","Tags":"Tagy","Decrypting items: %d/%d":"Rozšifrované položky: %d/%d","Fetching resources: %d/%d":"Stahování zdrojů: %d/%d","Sidebar":"Postranní lišta","Focus":"Přejít na","Please select where the sync status should be exported to":"Prosím vyberte, kam má být stav synchronizace exportován","Retry All":"Opakovat vše","Retry":"Znovu","strong text":"tučný text","emphasised text":"zvýrazněný text","Insert Hyperlink":"Vložit odkaz","List item":"Seznam položek","Layout":"Rozvržení","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Tato poznámka je prázdný. Klikněte na \"%s\" pro otevření editoru.","Cut":"Vyjmout","Copy":"Kopírovat","Paste":"Vložit","Checkbox list":"Zaškrtávací pole","Edit":"Upravit","Attach file":"Přiložit soubor","Code Block":"Blok kódu","Inline Code":"Vložený kód","Insert Date Time":"Vložit datum a čas","Drop notes or files here":"Sem přetáhněte poznámky nebo soubory","Code View":"Zobrazit kód","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Počkejte prosím na stažení a dešifrování všech příloh. Můžete také přepnout na %s a poznámku upravit.","There was an error downloading this attachment:":"Při stahování této přílohy došlo k chybě:","This attachment is not downloaded or not decrypted yet":"Tato příloha ještě není stažena nebo dešifrována","Open...":"Otevřít...","Save as...":"Uložit jako...","Reveal file in folder":"Ukázat soubor ve složce","Copy path to clipboard":"Kopírovat cestu do schránky","Copy Link Address":"Kopírovat adresu odkazu","to-do":"úkol","note":"poznámka","Creating new %s...":"Vytvářím %s...","The following attachments are being watched for changes:":"Následující přílohy jsou sledovány, zda došlo k jejich změně:","The attachments will no longer be watched when you switch to a different note.":"Když přepnete na jinou poznámku, přílohy již nebudou sledovány.","In: %s":"V: %s","Select all":"Vybrat vše","Bold":"Tučně","Italic":"Kurzíva","Hyperlink":"Odkaz","Code":"Kód","Numbered List":"Číslovanáý seznam","Bulleted List":"Seznam s odrážkami","Checkbox":"Zaškrtávací pole","Heading":"Nadpis","Horizontal Rule":"Horizontální čára","Note title":"Název zápisníku","Search in current note":"Hledat v aktuální poznámce","Note body":"Tělo poznámky","ID":"ID","Created":"Vytvořeno","Updated":"Upraveno","Completed":"Dokončeno","Location":"Lokace","URL":"URL","Note History":"Historie poznámky","Markup":"Markup","Previous versions of this note":"Předchozí verze poznámky","Note properties":"Nastavení poznámek","Error: %s":"Chyba: %s","Disabled":"Vypnuto","Search...":"Hledat...","Import":"Import","Command":"Příkaz","Keyboard Shortcut":"Klávesová zkratka","Quit":"Ukončit","Insert template":"Vložit šablonu","Actual Size":"Skutečná velikost","Website and documentation":"Web a dokumentace","Hide Joplin":"Skrýt Joplin","Close Window":"Zavřít akno","Preferences":"Nastavení","Options":"Nastavení","Press the shortcut":"Stiskněte klávesovou zkratku","Press the shortcut aLine truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"OK","Cancel":"Annullér","The app is now going to close. Please relaunch it to complete the process.":"Appen lukker nu. Genstart den for at fuldføre processen.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"Skriv titlen på en note eller noget af notens indhold for at hoppe til den. Eller skriv # efterfulgt af et etikettenavn, eller @ efterfulgt af en notesbogs navn. Eller skriv: for at søge efter kommandoer.","Goto Anything...":"Gå til tilfældig...","Command palette":"Kommando-palet","Exporting to \"%s\" as \"%s\" format. Please wait...":"Eksporterer til \"%s\" som \"%s\"-format. Vent venligst...","Could not export notes: %s":"Kunne ikke eksportere noterne: %s","Current version is up-to-date.":"Aktuel version er den nyeste.","%s (pre-release)":"%s (pre-release)","An update is available, do you want to download it now?":"Opdatering er tilgængelig, vil du hente den nu?","Your version: %s":"Din version: %s","New version: %s":"Ny version: %s","Download":"Download","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Noten \"%s\" er blevet gendannet til notesbogen \"%s\".","This note has no history":"Denne note har ingen historie","Unsupported link or message: %s":"Ugyldigt link eller besked: %s","Restore":"Gendan","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Klik på \"%s\" for at gendanne noten. Den vil blive kopieret til notesbogen \"%s\". Den nuværende version af noten vil ikke blive erstattet eller ændret.","Back":"Tilbage","Remove":"Fjern","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Slet notesbogen \"%s\"?\n\nAlle noter og undernotesbøger i denne notesbog bliver også slettet.","Delete":"Slet","Remove tag \"%s\" from all notes?":"Slet etikette \"%s\" fra alle noter?","Remove this search from the sidebar?":"Slet denne søgning fra sidebjælken?","Export":"Eksportér","All notes":"Alle noter","Synchronise":"Synkroniser","Notebooks":"Notesbøger","Tags":"Etiketter","Decrypting items: %d/%d":"Dekrypterede emner: %d/%d","Fetching resources: %d/%d":"Henter ressourcer: %d/%d","Sidebar":"Sidebjælke","Focus":"Fokuser","Please select where the sync status should be exported to":"Vælg hvor synk-status skal eksporteres til","Retry All":"Prøv Alle","Retry":"Prøv igen","strong text":"fed (strong) tekst","emphasised text":"fremhævet tekst","Insert Hyperlink":"Indsæt hyperlink","List item":"Listepunkt","Layout":"Layout","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Denne note er har intet indhold. Klik på \"%s\" for at starte editoren og redigere noten.","Cut":"Klip","Copy":"Kopier","Paste":"Indsæt","Checkbox list":"Afkrydsningsliste","Highlight":"Fremhæv","Strikethrough":"Gennemstregning","Insert":"Indsæt","Superscript":"Hævet skrift","Subscript":"Sænket skrift","Edit":"Rediger","Attach file":"Vedhæft fil","Code Block":"Kode blok","Inline Code":"Inline-kode","Insert Date Time":"Indsæt dato og tid","Drop notes or files here":"Slip noter eller filer her","Code View":"Kodevisning","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Vent venligst på at alle vedhæftninger downloades og dekrypteres. Du kan også skifte til %s for at redigere noten.","There was an error downloading this attachment:":"Der opstod en fejl ved download af denne vedhæftning:","This attachment is not downloaded or not decrypted yet":"Denne vedhæftning er ikke downloadet eller ikke dekrypteret endnu","Open...":"Åbn...","Save as...":"Gem som...","Reveal file in folder":"Vis fil i mappe","Copy path to clipboard":"Kopier sti til udklipsholder","Copy Link Address":"Kopier linkadresse","to-do":"opgave","note":"note","Creating new %s...":"Opretter ny %s...","Click to add tags...":"Klik for at tilføje etiketter...","This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.":"Denne Rich Text editor har en række begrænsninger, og det anbefales at være opmærksom på disse, før den bruges.","Read more about it":"Læs mere om det","Dismiss":"Afvis","The following attachments are being watched for changes:":"Følgende vedhæftninger overvåges for ændringer:","The attachments will no longer be watched when you switch to a different note.":"Vedhæftningerne vil ikke længere blive overvåget, hvis du skifter til en anden note.","In: %s":"I: %s","Select all":"Vælg alt","Bold":"Fed","Italic":"Kursiv","Hyperlink":"Hyperlink","Code":"Kode","Numbered List":"Nummeret liste","Bulleted List":"Punktliste","Checkbox":"Afkrydsningsfelt","Heading":"Overskrift","Horizontal Rule":"Vandret linje","Delete line":"Slet linje","Duplicate line":"Dupliker linje","Undo":"Fortryd","Redo":"Gentag","Indent Line truncated
{"OK":"OK","Cancel":"Annullér","The app is now going to close. Please relaunch it to complete the process.":"Appen lukker nu. Genstart den for at fuldføre processen.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"Skriv titlen på en note eller noget af notens indhold for at hoppe til den. Eller skriv # efterfulgt af et etikettenavn, eller @ efterfulgt af en notesbogs navn. Eller skriv: for at søge efter kommandoer.","Goto Anything...":"Gå til tilfældig...","Command palette":"Kommando-palet","Exporting to \"%s\" as \"%s\" format. Please wait...":"Eksporterer til \"%s\" som \"%s\"-format. Vent venligst...","Could not export notes: %s":"Kunne ikke eksportere noterne: %s","Current version is up-to-date.":"Aktuel version er den nyeste.","%s (pre-release)":"%s (pre-release)","An update is available, do you want to download it now?":"Opdatering er tilgængelig, vil du hente den nu?","Your version: %s":"Din version: %s","New version: %s":"Ny version: %s","Download":"Download","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Noten \"%s\" er blevet gendannet til notesbogen \"%s\".","This note has no history":"Denne note har ingen historie","Unsupported link or message: %s":"Ugyldigt link eller besked: %s","Restore":"Gendan","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Klik på \"%s\" for at gendanne noten. Den vil blive kopieret til notesbogen \"%s\". Den nuværende version af noten vil ikke blive erstattet eller ændret.","Back":"Tilbage","Remove":"Fjern","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Slet notesbogen \"%s\"?\n\nAlle noter og undernotesbøger i denne notesbog bliver også slettet.","Delete":"Slet","Remove tag \"%s\" from all notes?":"Slet etikette \"%s\" fra alle noter?","Remove this search from the sidebar?":"Slet denne søgning fra sidebjælken?","Export":"Eksportér","All notes":"Alle noter","Synchronise":"Synkroniser","Notebooks":"Notesbøger","Tags":"Etiketter","Decrypting items: %d/%d":"Dekrypterede emner: %d/%d","Fetching resources: %d/%d":"Henter ressourcer: %d/%d","Sidebar":"Sidebjælke","Focus":"Fokuser","Please select where the sync status should be exported to":"Vælg hvor synk-status skal eksporteres til","Retry All":"Prøv Alle","Retry":"Prøv igen","strong text":"fed (strong) tekst","emphasised text":"fremhævet tekst","Insert Hyperlink":"Indsæt hyperlink","List item":"Listepunkt","Layout":"Layout","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Denne note er har intet indhold. Klik på \"%s\" for at starte editoren og redigere noten.","Cut":"Klip","Copy":"Kopier","Paste":"Indsæt","Checkbox list":"Afkrydsningsliste","Highlight":"Fremhæv","Strikethrough":"Gennemstregning","Insert":"Indsæt","Superscript":"Hævet skrift","Subscript":"Sænket skrift","Edit":"Rediger","Attach file":"Vedhæft fil","Code Block":"Kode blok","Inline Code":"Inline-kode","Insert Date Time":"Indsæt dato og tid","Drop notes or files here":"Slip noter eller filer her","Code View":"Kodevisning","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Vent venligst på at alle vedhæftninger downloades og dekrypteres. Du kan også skifte til %s for at redigere noten.","There was an error downloading this attachment:":"Der opstod en fejl ved download af denne vedhæftning:","This attachment is not downloaded or not decrypted yet":"Denne vedhæftning er ikke downloadet eller ikke dekrypteret endnu","Open...":"Åbn...","Save as...":"Gem som...","Reveal file in folder":"Vis fil i mappe","Copy path to clipboard":"Kopier sti til udklipsholder","Copy Link Address":"Kopier linkadresse","to-do":"opgave","note":"note","Creating new %s...":"Opretter ny %s...","Click to add tags...":"Klik for at tilføje etiketter...","This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.":"Denne Rich Text editor har en række begrænsninger, og det anbefales at være opmærksom på disse, før den bruges.","Read more about it":"Læs mere om det","Dismiss":"Afvis","The following attachments are being watched for changes:":"Følgende vedhæftninger overvåges for ændringer:","The attachments will no longer be watched when you switch to a different note.":"Vedhæftningerne vil ikke længere blive overvåget, hvis du skifter til en anden note.","In: %s":"I: %s","Select all":"Vælg alt","Bold":"Fed","Italic":"Kursiv","Hyperlink":"Hyperlink","Code":"Kode","Numbered List":"Nummeret liste","Bulleted List":"Punktliste","Checkbox":"Afkrydsningsfelt","Heading":"Overskrift","Horizontal Rule":"Vandret linje","Delete line":"Slet linje","Duplicate line":"Dupliker linje","Undo":"Fortryd","Redo":"Gentag","Indent Line truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"OK","Cancel":"Abbrechen","The app is now going to close. Please relaunch it to complete the process.":"Die App wird nun geschlossen. Bitte starte sie erneut, um den Vorgang abzuschließen.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"Gib den Titel einer Notiz oder einen Teil seines Inhalts ein, um dorthin zu springen. Oder gib # gefolgt von einem Schlagwort oder @ gefolgt vom Namen eines Notizbuchs ein. Oder gib : für eine Suche nach Kommandos ein.","Goto Anything...":"Gehe zu ...","Command palette":"Befehlspalette","Exporting to \"%s\" as \"%s\" format. Please wait...":"Es wird nach „%s“ im Format „%s“ exportiert. Bitte warten ...","Could not export notes: %s":"Konnte Notizen nicht exportieren: %s","Current version is up-to-date.":"Die aktuelle Version ist auf dem neuesten Stand.","%s (pre-release)":"%s (Vorabversion)","An update is available, do you want to download it now?":"Es ist eine Aktualisierung verfügbar. Soll sie jetzt heruntergeladen werden?","Your version: %s":"Deine Version: %s","New version: %s":"Neue Version: %s","Download":"Herunterladen","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Die Notiz „%s“ wurde erfolgreich im Notizbuch „%s“ wiederhergestellt.","This note has no history":"Diese Notiz hat keinen Verlauf","Unsupported link or message: %s":"Nicht unterstützter Link oder Nachricht: %s","Restore":"Wiederherstellen","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Klicke „%s“, um die Notiz wiederherzustellen. Sie wird in das Notizbuch namens „%s“ kopiert. Die aktuelle Version der Notiz wird nicht ersetzt oder verändert.","Back":"Zurück","Remove":"Entfernen","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Notizbuch „%s“ löschen?\n\nAlle Notizen und Unter-Notizbücher in diesem Notizbuch werden ebenfalls gelöscht.","Delete":"Löschen","Remove tag \"%s\" from all notes?":"Schlagwort „%s“ von allen Notizen entfernen?","Remove this search from the sidebar?":"Diese Suche von der Seitenleiste entfernen?","Export":"Exportieren","All notes":"Alle Notizen","Synchronise":"Synchronisieren","Notebooks":"Notizbücher","Tags":"Schlagwörter","Decrypting items: %d/%d":"Entschlüsselte Objekte: %d/%d","Fetching resources: %d/%d":"Anhänge abrufen: %d/%d","Sidebar":"Seitenleiste","Focus":"Fokus","Please select where the sync status should be exported to":"Bitte wähle aus, wohin der Synchronisations-Status exportiert werden soll","Retry All":"Erneut alle versuchen","Retry":"Erneut versuchen","strong text":"fetter Text","emphasised text":"hervorgehobener Text","Insert Hyperlink":"Weblink einfügen","List item":"Listeneintrag","Layout":"Layout","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Diese Notiz hat keinen Inhalt. Klicke auf „%s“, um den Editor zu aktivieren und die Notiz zu bearbeiten.","Cut":"Ausschneiden","Copy":"Kopieren","Paste":"Einfügen","Checkbox list":"Liste mit Kontrollkästchen","Highlight":"Hervorheben","Strikethrough":"Durchstreichen","Insert":"Einfügen","Superscript":"Hochgestellt","Subscript":"Tiefgestellt","Edit":"Bearbeiten","Attach file":"Datei anhängen","Code Block":"Code-Block","Inline Code":"Code im Fließtext","Insert Date Time":"Datum / Uhrzeit einfügen","Drop notes or files here":"Notizen oder Dateien hierher ziehen","Code View":"Code-Ansicht","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Bitte warte, bis alle Anhänge heruntergeladen und entschlüsselt sind. Du kannst auch auf/zu %s wechseln, um die Notiz zu bearbeiten.","There was an error downloading this attachment:":"Es gab einen Fehler beim Herunterladen dieses Anhangs:","This attachment is not downloaded or not decrypted yet":"Dieser Anhang wurde noch nicht heruntergeladen oder entschlüsselt","Open...":"Öffnen ...","Save as...":"Speichern unter ...","Reveal file in folder":"Datei im Ordner anzeigen","Copy path to clipboard":"Pfad in Zwischenablage kopieren","Copy Link Address":"Link-Adresse kopieren","to-do":"Aufgabe","note":"Notiz","Creating new %s...":"Neue(s) %s wird erstellt ...","Click to add tags...":"Zum Hinzufügen von Schlagwörtern klicken ...","This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.":"Dieser Rich Text Editor hat eine Reihe von Unzulänglichkeiten, die bei der Nutzung beachtet werden sollten.","Read more about it":"Mehr erfahren","Dismiss":"Ausblenden","The following attachments are being watched for changes:":"Die folgenden Anhänge werden auf Änderungen überwacht:","The attachments will no longer be watched when you switch to a different note.":"Die Anhänge werden nicht mehr überwachtLine truncated
{"OK":"OK","Cancel":"Abbrechen","The app is now going to close. Please relaunch it to complete the process.":"Die App wird nun geschlossen. Bitte starte sie erneut, um den Vorgang abzuschließen.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"Gib den Titel einer Notiz oder einen Teil seines Inhalts ein, um dorthin zu springen. Oder gib # gefolgt von einem Schlagwort oder @ gefolgt vom Namen eines Notizbuchs ein. Oder gib : für eine Suche nach Kommandos ein.","Goto Anything...":"Gehe zu ...","Command palette":"Befehlspalette","Exporting to \"%s\" as \"%s\" format. Please wait...":"Es wird nach „%s“ im Format „%s“ exportiert. Bitte warten ...","Could not export notes: %s":"Konnte Notizen nicht exportieren: %s","Current version is up-to-date.":"Die aktuelle Version ist auf dem neuesten Stand.","%s (pre-release)":"%s (Vorabversion)","An update is available, do you want to download it now?":"Es ist eine Aktualisierung verfügbar. Soll sie jetzt heruntergeladen werden?","Your version: %s":"Deine Version: %s","New version: %s":"Neue Version: %s","Download":"Herunterladen","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Die Notiz „%s“ wurde erfolgreich im Notizbuch „%s“ wiederhergestellt.","This note has no history":"Diese Notiz hat keinen Verlauf","Unsupported link or message: %s":"Nicht unterstützter Link oder Nachricht: %s","Restore":"Wiederherstellen","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Klicke „%s“, um die Notiz wiederherzustellen. Sie wird in das Notizbuch namens „%s“ kopiert. Die aktuelle Version der Notiz wird nicht ersetzt oder verändert.","Back":"Zurück","Remove":"Entfernen","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Notizbuch „%s“ löschen?\n\nAlle Notizen und Unter-Notizbücher in diesem Notizbuch werden ebenfalls gelöscht.","Delete":"Löschen","Remove tag \"%s\" from all notes?":"Schlagwort „%s“ von allen Notizen entfernen?","Remove this search from the sidebar?":"Diese Suche von der Seitenleiste entfernen?","Export":"Exportieren","All notes":"Alle Notizen","Synchronise":"Synchronisieren","Notebooks":"Notizbücher","Tags":"Schlagwörter","Decrypting items: %d/%d":"Entschlüsselte Objekte: %d/%d","Fetching resources: %d/%d":"Anhänge abrufen: %d/%d","Sidebar":"Seitenleiste","Focus":"Fokus","Please select where the sync status should be exported to":"Bitte wähle aus, wohin der Synchronisations-Status exportiert werden soll","Retry All":"Erneut alle versuchen","Retry":"Erneut versuchen","strong text":"fetter Text","emphasised text":"hervorgehobener Text","Insert Hyperlink":"Weblink einfügen","List item":"Listeneintrag","Layout":"Layout","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Diese Notiz hat keinen Inhalt. Klicke auf „%s“, um den Editor zu aktivieren und die Notiz zu bearbeiten.","Cut":"Ausschneiden","Copy":"Kopieren","Paste":"Einfügen","Checkbox list":"Liste mit Kontrollkästchen","Highlight":"Hervorheben","Strikethrough":"Durchstreichen","Insert":"Einfügen","Superscript":"Hochgestellt","Subscript":"Tiefgestellt","Edit":"Bearbeiten","Attach file":"Datei anhängen","Code Block":"Code-Block","Inline Code":"Code im Fließtext","Insert Date Time":"Datum / Uhrzeit einfügen","Drop notes or files here":"Notizen oder Dateien hierher ziehen","Code View":"Code-Ansicht","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Bitte warte, bis alle Anhänge heruntergeladen und entschlüsselt sind. Du kannst auch auf/zu %s wechseln, um die Notiz zu bearbeiten.","There was an error downloading this attachment:":"Es gab einen Fehler beim Herunterladen dieses Anhangs:","This attachment is not downloaded or not decrypted yet":"Dieser Anhang wurde noch nicht heruntergeladen oder entschlüsselt","Open...":"Öffnen ...","Save as...":"Speichern unter ...","Reveal file in folder":"Datei im Ordner anzeigen","Copy path to clipboard":"Pfad in Zwischenablage kopieren","Copy Link Address":"Link-Adresse kopieren","to-do":"Aufgabe","note":"Notiz","Creating new %s...":"Neue(s) %s wird erstellt ...","Click to add tags...":"Zum Hinzufügen von Schlagwörtern klicken ...","This Rich Text editor has a number of limitations and it is recommended to be aware of them before using it.":"Dieser Rich Text Editor hat eine Reihe von Unzulänglichkeiten, die bei der Nutzung beachtet werden sollten.","Read more about it":"Mehr erfahren","Dismiss":"Ausblenden","The following attachments are being watched for changes:":"Die folgenden Anhänge werden auf Änderungen überwacht:","The attachments will no longer be watched when you switch to a different note.":"Die Anhänge werden nicht mehr überwachtLine truncated
+1 -1
View File
@@ -1 +1 @@
{"OK":"Εντάξει","Cancel":"Άκυρο","Goto Anything...":"Γρήγορη Μετακίνηση...","Exporting to \"%s\" as \"%s\" format. Please wait...":"Γίνεται εξαγωγή στο \"%s\" με μορφή \"%s\". Παρακαλώ περιμένετε...","Could not export notes: %s":"Δεν ήταν δυνατή η εξαγωγή σημειώσεων: %s","Current version is up-to-date.":"Η τρέχουσα έκδοση είναι ενημερωμένη.","%s (pre-release)":"%s (pre-release)","An update is available, do you want to download it now?":"Υπάρχει διαθέσιμη μια ενημέρωση, θέλετε να την κατεβάσετε τώρα;","Your version: %s":"Η έκδοσή σου: %s","New version: %s":"Νέα έκδοση: %s","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Η σημείωση \"%s\" έχει επαναφερθεί με επιτυχία στο σημειωματάριο \"%s\".","This note has no history":"Αυτή η σημείωση δεν έχει ιστορικό","Unsupported link or message: %s":"Μη υποστηριζόμενος σύνδεσμος ή μήνυμα: %s","Restore":"Επαναφορά","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Κάντε κλικ στο \"%s\" για να επαναφέρετε τη σημείωση. Θα αντιγραφεί στο σημειωματάριο που ονομάζεται \"%s\". Η τρέχουσα έκδοση της σημείωσης δεν αντικαθίσταται ή τροποποιείται.","Back":"Πίσω","Remove":"Κατάργηση","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Διαγραφή σημειωματάριου \"%s\";\n\nΌλες οι σημειώσεις και τα υπο-σημειωματάρια που περιέχει θα διαγραφούν επίσης.","Delete":"Διαγραφή","Remove tag \"%s\" from all notes?":"Κατάργηση της ετικέτας \"%s\" από όλες τις σημειώσεις;","Remove this search from the sidebar?":"Κατάργηση αυτής της αναζήτησης από την πλευρική μπάρα;","Export":"Εξαγωγή","All notes":"Όλες οι σημειώσεις","Synchronise":"Συγχρονισμός","Notebooks":"Σημειωματάρια","Tags":"Ετικέτες","Decrypting items: %d/%d":"Αποκρυπτογράφηση αντικειμένων: %d/%d","Fetching resources: %d/%d":"Λήψη πόρων: %d/%d","Sidebar":"Πλευρική μπάρα","Focus":"Εστίαση σε","Please select where the sync status should be exported to":"Επιλέξτε την τοποθεσία στην οποία θα εξαχθεί η κατάσταση του συγχρονισμού","Retry All":"Δοκιμή ξανά για Όλα","Retry":"Δοκιμή ξανά","strong text":"έντονη γραφή","emphasised text":"κείμενο με έμφαση","Insert Hyperlink":"Εισαγωγή υπερσύνδεσης","List item":"Στοιχείο λίστας","Layout":"Διάταξη","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Αυτή η σημείωση δεν περιέχει περιεχόμενο. Κάντε κλικ στο \"%s\" για να επεξεργαστείτε τη σημείωση στον editor.","Cut":"Αποκοπή","Copy":"Αντιγραφή","Paste":"Επικόλληση","Checkbox list":"Λίστα πλαισίων ελέγχου","Edit":"Επεξεργασία","Attach file":"Επισύναψη αρχείου","Code Block":"Μπλοκ Κώδικα","Inline Code":"Ενσωματωμένος κώδικας","Insert Date Time":"Εισαγωγή Ημερομηνίας Ώρας","Drop notes or files here":"Απόθεση σημειώσεων ή αρχείων εδώ","Code View":"Προβολή κώδικα","Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.":"Παρακαλώ περιμένετε να γίνει λήψη και αποκρυπτογράφηση όλων των συνημμένων. Μπορείτε επίσης να μεταβείτε στο %s για να επεξεργαστείτε τη σημείωση.","There was an error downloading this attachment:":"Παρουσιάστηκε σφάλμα κατά τη λήψη αυτού του συνημμένου:","This attachment is not downloaded or not decrypted yet":"Αυτό το συνημμένο δεν έχει ακόμη μεταφορτωθεί ή αποκρυπτογραφηθεί","Open...":"Άνοιγμα...","Save as...":"Αποθήκευση ως...","Reveal file in folder":"Αποκάλυψη αρχείου στο φάκελο","Copy path to clipboard":"Αντιγραφή διαδρομής στο πρόχLine truncated
{"OK":"Εντάξει","Cancel":"Άκυρο","The app is now going to close. Please relaunch it to complete the process.":"Η εφαρμογή θα κλείσει τώρα. Παρακαλώ ανοίξτε την ξανά για να ολοκληρώσετε τη διαδικασία.","Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.":"Πληκτρολογήστε έναν τίτλο σημείωσης ή μέρος του περιεχομένου του για να μεταβείτε σε αυτόν. Ή πληκτρολογήστε # ακολουθούμενο από ένα όνομα ετικέτας ή @ ακολουθούμενο από ένα σημειωματάριο. Ή πληκτρολογίστε : για να ψάξετε για εντολές.","Goto Anything...":"Γρήγορη Μετακίνηση...","Command palette":"Παλέτα εντολών","Exporting to \"%s\" as \"%s\" format. Please wait...":"Γίνεται εξαγωγή στο \"%s\" με μορφή \"%s\". Παρακαλώ περιμένετε...","Could not export notes: %s":"Δεν ήταν δυνατή η εξαγωγή σημειώσεων: %s","Current version is up-to-date.":"Η τρέχουσα έκδοση είναι ενημερωμένη.","%s (pre-release)":"%s (pre-release)","An update is available, do you want to download it now?":"Υπάρχει διαθέσιμη μια ενημέρωση, θέλετε να την κατεβάσετε τώρα;","Your version: %s":"Η έκδοσή σου: %s","New version: %s":"Νέα έκδοση: %s","Download":"Λήψη","Full changelog":"Πλήρες αρχείο αλλαγών","The note \"%s\" has been successfully restored to the notebook \"%s\".":"Η σημείωση \"%s\" έχει επαναφερθεί με επιτυχία στο σημειωματάριο \"%s\".","This note has no history":"Αυτή η σημείωση δεν έχει ιστορικό","Unsupported link or message: %s":"Μη υποστηριζόμενος σύνδεσμος ή μήνυμα: %s","Restore":"Επαναφορά","Click \"%s\" to restore the note. It will be copied in the notebook named \"%s\". The current version of the note will not be replaced or modified.":"Κάντε κλικ στο \"%s\" για να επαναφέρετε τη σημείωση. Θα αντιγραφεί στο σημειωματάριο που ονομάζεται \"%s\". Η τρέχουσα έκδοση της σημείωσης δεν αντικαθίσταται ή τροποποιείται.","Back":"Πίσω","Remove":"Κατάργηση","Delete notebook \"%s\"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.":"Διαγραφή σημειωματάριου \"%s\";\n\nΌλες οι σημειώσεις και τα υπο-σημειωματάρια που περιέχει θα διαγραφούν επίσης.","Delete":"Διαγραφή","Remove tag \"%s\" from all notes?":"Κατάργηση της ετικέτας \"%s\" από όλες τις σημειώσεις;","Remove this search from the sidebar?":"Κατάργηση αυτής της αναζήτησης από την πλευρική μπάρα;","Export":"Εξαγωγή","All notes":"Όλες οι σημειώσεις","Synchronise":"Συγχρονισμός","Notebooks":"Σημειωματάρια","Tags":"Ετικέτες","Decrypting items: %d/%d":"Αποκρυπτογράφηση αντικειμένων: %d/%d","Fetching resources: %d/%d":"Λήψη πόρων: %d/%d","Sidebar":"Πλευρική μπάρα","Focus":"Εστίαση σε","Please select where the sync status should be exported to":"Επιλέξτε την τοποθεσία στην οποία θα εξαχθεί η κατάσταση του συγχρονισμού","Retry All":"Δοκιμή ξανά για Όλα","Retry":"Δοκιμή ξανά","Advanced tools":"Προηγμένα εργαλεία","Export debug report":"Εξαγωγή Debug report","strong text":"έντονη γραφή","emphasised text":"κείμενο με έμφαση","Insert Hyperlink":"Εισαγωγή υπερσύνδεσης","List item":"Στοιχείο λίστας","Layout":"Διάταξη","This note has no content. Click on \"%s\" to toggle the editor and edit the note.":"Αυτή η σημείωση δεν περιέχει περιεχόμενο. Κάντε κλικ στο \"%s\" για να επεξεργαστείτε τη σημείωση στον editor.","Cut":"Αποκοπή","Copy":"Αντιγραφή","Paste":"Επικόλληση","Checkbox list":"Λίστα πλαισίων ελέγχου","Highlight":"Επισήμανση","Strikethrough":"Διακριτή διαγραφή","Insert":"Εισαγωγή","Superscript":"Εκθέτης","Subscript":"Δείκτης","Edit":"Επεξεργασία","Attach file":"Επισύναψη αρχείου","Code BlLine truncated
Loaded 100 of 295 files, more files were not shown because too many files have changed in this diff. Show more