1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-29 23:48:19 +02:00

Compare commits

...

47 Commits

Author SHA1 Message Date
Laurent Cozic
46c40ce9fa Merge branch 'dev' into plugin_editor_context_menu 2020-11-13 23:40:28 +00:00
Laurent Cozic
7151a48138 Merge branch 'dev' of github.com:laurent22/joplin into dev 2020-11-13 23:37:24 +00:00
Laurent Cozic
536788f1bc context 2020-11-13 23:28:00 +00:00
Laurent Cozic
aaaa4e4569 Plugins: Add command "editorSetText" for desktop app 2020-11-13 22:50:03 +00:00
Laurent Cozic
872d2942b4 All: Allow lowercase filters when doing search 2020-11-13 22:29:28 +00:00
Laurent Cozic
918b768634 Plugins: Fix crash when path includes trailing slash 2020-11-13 22:03:10 +00:00
Laurent Cozic
8305eb4403 API: Fix note and resource association end points 2020-11-13 21:45:25 +00:00
Laurent Cozic
91e7c66f9e Update website 2020-11-13 21:19:55 +00:00
Laurent Cozic
e392efc091 ignored files 2020-11-13 21:19:02 +00:00
Laurent Cozic
458ae3731d Doc: Add beta banner for plugin API 2020-11-13 19:12:49 +00:00
Laurent Cozic
a647272cf0 Plugins: Allow retrieving form values from dialogs 2020-11-13 18:48:42 +00:00
Laurent Cozic
72472c13c5 Desktop: Fixed issue with note being saved after word has been replaced by spell checker 2020-11-13 17:55:35 +00:00
Laurent Cozic
67f0739d3c Desktop: Allow customising application layout 2020-11-13 17:09:28 +00:00
Laurent Cozic
17d835d694 Tools: Apply rule @typescript-eslint/member-delimiter-style 2020-11-12 19:29:22 +00:00
Laurent Cozic
42799b0662 Tools: Apply rule @typescript-eslint/comma-dangle 2020-11-12 19:23:54 +00:00
Laurent Cozic
d20694e52c Tools: Apply rule @typescript-eslint/type-annotation-spacing 2020-11-12 19:13:28 +00:00
Caleb John
62feb7ff60 Desktop: Fix drag and drop behaviour to "copy" instead of "move" (#4031) 2020-11-12 18:44:23 +00:00
Helmut K. C. Tessarek
e29e745b96 Desktop: add {{bowm}} and {{bows}} - Beginning Of Week (Monday/Sunday) (#4023)
* add {{bowm}} and {{bows}} - Beginning Of Week (Monday/Sunday)

In certain situation it is useful to reference the beginning of the week.
Unfortunately the current system does not allow to that, not even with a custom datetime format.

I've also added the 2 new template variables to the documentation.

* README: better wording
2020-11-12 18:41:37 +00:00
Helmut K. C. Tessarek
340312fa80 Desktop: Fix handling of certain keys in shortcut editor (#4022)
String.fromCharCode expects unicode charcodes as an argument; e.keyCode returns javascript keycodes.
Javascript keycodes and unicode charcodes are not the same thing!

Therefore I used an array to map keycodes. This seems to be the only way to make this work properly.

I also changed the width of the shortcut recorder input field to 200px, since certain key combinations are cut off, because they don't fit in the field.
2020-11-12 18:40:00 +00:00
Rik
006239913e Doc: Update plugins.md (#4074) 2020-11-12 10:10:58 +00:00
Helmut K. C. Tessarek
f4bc8cc817 remove old template 2020-11-12 00:43:13 -05:00
Helmut K. C. Tessarek
fb1f43bb17 better gh issue template 2020-11-12 00:41:02 -05:00
Laurent Cozic
31ffd8d7e9 Merge branch 'dev' of https://github.com/laurent22/joplin into dev 2020-11-11 14:30:47 +00:00
Laurent Cozic
873184f69b Desktop: Regression: Fix application name 2020-11-11 14:22:42 +00:00
Laurent Cozic
da6977569f Electron release v1.4.9 2020-11-11 12:52:37 +00:00
Laurent Cozic
8808b38d83 Fixed desktop build 2020-11-11 12:51:51 +00:00
Laurent Cozic
ad654e30d5 Tools: fix git-changelog 2020-11-11 12:40:43 +00:00
Laurent Cozic
6f11487dba Electron release v1.4.8 2020-11-11 11:56:38 +00:00
Laurent Cozic
dd0d54d4d0 Update website 2020-11-11 11:53:35 +00:00
Laurent Cozic
0fd0451c17 API: Change pagination to has_more model 2020-11-11 11:52:47 +00:00
Laurent Cozic
06f2fda946 Tools: Replace Jasmine with Jest to run tests 2020-11-10 15:59:30 +00:00
Laurent Cozic
c249333e2a Tools: Fixed updateIgnored script 2020-11-10 11:26:36 +00:00
Johan Hammar
9755a69be8 Tools: Contributor fixes (#4068) 2020-11-09 19:36:49 +00:00
Laurent Cozic
18e4f7ffe7 Chore: clean up 2020-11-09 17:37:13 +00:00
Laurent Cozic
7cb60aaace Tools: Added API test to check that cursor queries return same fields 2020-11-09 17:17:51 +00:00
Laurent Cozic
fbcfe432c5 Tools: Ignore files 2020-11-09 15:32:33 +00:00
Laurent Cozic
ed71726e87 Desktop: Fixes #3876: Tags could not be selected in some cases 2020-11-09 13:33:12 +00:00
Laurent Cozic
31a0449e71 Tools: Added script to generate database types for TypeScript 2020-11-09 13:12:11 +00:00
Laurent Cozic
04450a6464 Doc: Update sponsors 2020-11-09 12:19:40 +00:00
Laurent Cozic
d0ec598ee4 Desktop: Fixes #3904, Fixes #3973: Fixed inconsistent note list state when using search 2020-11-09 12:07:37 +00:00
Laurent Cozic
ca8b05631a Tools: Fixed tests 2020-11-09 09:21:05 +00:00
Laurent Cozic
3223fd26f4 Update website 2020-11-08 19:22:58 +00:00
Laurent Cozic
7847aeda75 Doc: Exclude pre-release from changelog 2020-11-08 19:22:25 +00:00
Laurent Cozic
7d02ddfdb5 Update website 2020-11-08 19:14:47 +00:00
Johan Hammar
5079cd9ff2 Tools: Fix incorrect localization URLs (#4063) 2020-11-08 19:13:42 +00:00
Laurent Cozic
48f6412721 Tools: Make sure translation warnings are being displayed 2020-11-08 17:41:56 +00:00
Laurent Cozic
498e80f2d3 Tools: Display deleted strings when building translations, and run TypeScript before 2020-11-08 17:19:38 +00:00
615 changed files with 23325 additions and 9530 deletions

View File

@@ -169,9 +169,6 @@ packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/services/plugins/api/JoplinSetting.d.ts
packages/app-cli/tests/services/plugins/api/JoplinSetting.js
packages/app-cli/tests/services/plugins/api/JoplinSetting.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
@@ -436,6 +433,9 @@ packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js.map
packages/app-desktop/gui/MainScreen/commands/toggleEditors.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js.map
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map
@@ -502,6 +502,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js.map
@@ -583,6 +586,9 @@ packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map
packages/app-desktop/gui/NoteListItem.d.ts
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListItem.js.map
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.d.ts
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js.map
packages/app-desktop/gui/NoteTextViewer.d.ts
packages/app-desktop/gui/NoteTextViewer.js
packages/app-desktop/gui/NoteTextViewer.js.map
@@ -592,15 +598,60 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
packages/app-desktop/gui/OneDriveLoginScreen.js
packages/app-desktop/gui/OneDriveLoginScreen.js.map
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
packages/app-desktop/gui/ResizableLayout/ResizableLayout.d.ts
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.d.ts
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js.map
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.d.ts
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js.map
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.d.ts
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js.map
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.d.ts
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.js
packages/app-desktop/gui/ResizableLayout/utils/movements.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.js
packages/app-desktop/gui/ResizableLayout/utils/persist.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js
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/setLayoutItemProps.d.ts
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
packages/app-desktop/gui/ResizableLayout/utils/style.d.ts
packages/app-desktop/gui/ResizableLayout/utils/style.js
packages/app-desktop/gui/ResizableLayout/utils/style.js.map
packages/app-desktop/gui/ResizableLayout/utils/types.d.ts
packages/app-desktop/gui/ResizableLayout/utils/types.js
packages/app-desktop/gui/ResizableLayout/utils/types.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.d.ts
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js.map
packages/app-desktop/gui/ResourceScreen.d.ts
packages/app-desktop/gui/ResourceScreen.js
packages/app-desktop/gui/ResourceScreen.js.map
@@ -613,9 +664,6 @@ packages/app-desktop/gui/Root_UpgradeSyncTarget.js.map
packages/app-desktop/gui/SearchBar/SearchBar.d.ts
packages/app-desktop/gui/SearchBar/SearchBar.js
packages/app-desktop/gui/SearchBar/SearchBar.js.map
packages/app-desktop/gui/SearchBar/hooks/useSearch.d.ts
packages/app-desktop/gui/SearchBar/hooks/useSearch.js
packages/app-desktop/gui/SearchBar/hooks/useSearch.js.map
packages/app-desktop/gui/SearchBar/styles/index.d.ts
packages/app-desktop/gui/SearchBar/styles/index.js
packages/app-desktop/gui/SearchBar/styles/index.js.map
@@ -682,6 +730,9 @@ packages/app-desktop/plugins/GotoAnything.js.map
packages/app-desktop/services/bridge.d.ts
packages/app-desktop/services/bridge.js
packages/app-desktop/services/bridge.js.map
packages/app-desktop/services/commands/stateToWhenClauseContext.d.ts
packages/app-desktop/services/commands/stateToWhenClauseContext.js
packages/app-desktop/services/commands/stateToWhenClauseContext.js.map
packages/app-desktop/services/commands/types.d.ts
packages/app-desktop/services/commands/types.js
packages/app-desktop/services/commands/types.js.map
@@ -859,9 +910,9 @@ packages/lib/models/NoteResource.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/utils/modelFeed.d.ts
packages/lib/models/utils/modelFeed.js
packages/lib/models/utils/modelFeed.js.map
packages/lib/models/utils/paginatedFeed.d.ts
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginatedFeed.js.map
packages/lib/models/utils/paginationToSql.d.ts
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/paginationToSql.js.map
@@ -931,6 +982,9 @@ packages/lib/services/commands/stateToWhenClauseContext.js.map
packages/lib/services/contextkey/contextkey.d.ts
packages/lib/services/contextkey/contextkey.js
packages/lib/services/contextkey/contextkey.js.map
packages/lib/services/database/types.d.ts
packages/lib/services/database/types.js
packages/lib/services/database/types.js.map
packages/lib/services/debug/populateDatabase.d.ts
packages/lib/services/debug/populateDatabase.js
packages/lib/services/debug/populateDatabase.js.map
@@ -1120,6 +1174,9 @@ packages/lib/services/rest/routes/search.js.map
packages/lib/services/rest/routes/tags.d.ts
packages/lib/services/rest/routes/tags.js
packages/lib/services/rest/routes/tags.js.map
packages/lib/services/rest/utils/collectionToPaginatedResults.d.ts
packages/lib/services/rest/utils/collectionToPaginatedResults.js
packages/lib/services/rest/utils/collectionToPaginatedResults.js.map
packages/lib/services/rest/utils/defaultAction.d.ts
packages/lib/services/rest/utils/defaultAction.js
packages/lib/services/rest/utils/defaultAction.js.map

View File

@@ -25,6 +25,9 @@ module.exports = {
'afterEach': 'readonly',
'jasmine': 'readonly',
// Jest variables
'test': 'readonly',
// React Native variables
'__DEV__': 'readonly',
@@ -130,6 +133,28 @@ module.exports = {
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we
// make everything public which is not great. New code however should specify member accessibility.
'@typescript-eslint/explicit-member-accessibility': ['warn'],
'@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }],
'@typescript-eslint/comma-dangle': ['error', {
'arrays': 'always-multiline',
'objects': 'always-multiline',
'imports': 'always-multiline',
'exports': 'always-multiline',
'enums': 'always-multiline',
'generics': 'always-multiline',
'tuples': 'always-multiline',
'functions': 'never',
}],
'@typescript-eslint/semi': ['error', 'always'],
'@typescript-eslint/member-delimiter-style': ['error', {
'multiline': {
'delimiter': 'semi',
'requireLast': true,
},
'singleline': {
'delimiter': 'semi',
'requireLast': false,
},
}],
},
},
],

View File

@@ -1,15 +0,0 @@
---
name: "\U0001F914 Feature requests and support"
about: 'For non-bug issues we recommend using the forum, where you''ll be more likely
to get an answer: https://discourse.joplinapp.org/'
title: ''
labels: ''
assignees: ''
---
If this is a feature request or a support query, please note that you'll not get an answer here.
Instead we recommend using the forum where you'll are a lot more likely to get an answer: https://discourse.joplinapp.org/
The forum is also the right place to submit a feature request so that it can be discussed by other users.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: "\U0001F914 Feature requests and support"
url: https://discourse.joplinapp.org/
about: I have a question or feature request …

87
.gitignore vendored
View File

@@ -161,9 +161,6 @@ packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/services/plugins/api/JoplinSetting.d.ts
packages/app-cli/tests/services/plugins/api/JoplinSetting.js
packages/app-cli/tests/services/plugins/api/JoplinSetting.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
@@ -428,6 +425,9 @@ packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js.map
packages/app-desktop/gui/MainScreen/commands/toggleEditors.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js.map
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map
@@ -494,6 +494,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupContextMenu.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js.map
@@ -575,6 +578,9 @@ packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map
packages/app-desktop/gui/NoteListItem.d.ts
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListItem.js.map
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.d.ts
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js.map
packages/app-desktop/gui/NoteTextViewer.d.ts
packages/app-desktop/gui/NoteTextViewer.js
packages/app-desktop/gui/NoteTextViewer.js.map
@@ -584,15 +590,60 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
packages/app-desktop/gui/OneDriveLoginScreen.js
packages/app-desktop/gui/OneDriveLoginScreen.js.map
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
packages/app-desktop/gui/ResizableLayout/ResizableLayout.d.ts
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.d.ts
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js.map
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.d.ts
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js.map
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.d.ts
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js.map
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.d.ts
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.js
packages/app-desktop/gui/ResizableLayout/utils/movements.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.js
packages/app-desktop/gui/ResizableLayout/utils/persist.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js
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/setLayoutItemProps.d.ts
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
packages/app-desktop/gui/ResizableLayout/utils/style.d.ts
packages/app-desktop/gui/ResizableLayout/utils/style.js
packages/app-desktop/gui/ResizableLayout/utils/style.js.map
packages/app-desktop/gui/ResizableLayout/utils/types.d.ts
packages/app-desktop/gui/ResizableLayout/utils/types.js
packages/app-desktop/gui/ResizableLayout/utils/types.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.d.ts
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js.map
packages/app-desktop/gui/ResourceScreen.d.ts
packages/app-desktop/gui/ResourceScreen.js
packages/app-desktop/gui/ResourceScreen.js.map
@@ -605,9 +656,6 @@ packages/app-desktop/gui/Root_UpgradeSyncTarget.js.map
packages/app-desktop/gui/SearchBar/SearchBar.d.ts
packages/app-desktop/gui/SearchBar/SearchBar.js
packages/app-desktop/gui/SearchBar/SearchBar.js.map
packages/app-desktop/gui/SearchBar/hooks/useSearch.d.ts
packages/app-desktop/gui/SearchBar/hooks/useSearch.js
packages/app-desktop/gui/SearchBar/hooks/useSearch.js.map
packages/app-desktop/gui/SearchBar/styles/index.d.ts
packages/app-desktop/gui/SearchBar/styles/index.js
packages/app-desktop/gui/SearchBar/styles/index.js.map
@@ -674,6 +722,9 @@ packages/app-desktop/plugins/GotoAnything.js.map
packages/app-desktop/services/bridge.d.ts
packages/app-desktop/services/bridge.js
packages/app-desktop/services/bridge.js.map
packages/app-desktop/services/commands/stateToWhenClauseContext.d.ts
packages/app-desktop/services/commands/stateToWhenClauseContext.js
packages/app-desktop/services/commands/stateToWhenClauseContext.js.map
packages/app-desktop/services/commands/types.d.ts
packages/app-desktop/services/commands/types.js
packages/app-desktop/services/commands/types.js.map
@@ -851,9 +902,9 @@ packages/lib/models/NoteResource.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/utils/modelFeed.d.ts
packages/lib/models/utils/modelFeed.js
packages/lib/models/utils/modelFeed.js.map
packages/lib/models/utils/paginatedFeed.d.ts
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginatedFeed.js.map
packages/lib/models/utils/paginationToSql.d.ts
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/paginationToSql.js.map
@@ -923,6 +974,9 @@ packages/lib/services/commands/stateToWhenClauseContext.js.map
packages/lib/services/contextkey/contextkey.d.ts
packages/lib/services/contextkey/contextkey.js
packages/lib/services/contextkey/contextkey.js.map
packages/lib/services/database/types.d.ts
packages/lib/services/database/types.js
packages/lib/services/database/types.js.map
packages/lib/services/debug/populateDatabase.d.ts
packages/lib/services/debug/populateDatabase.js
packages/lib/services/debug/populateDatabase.js.map
@@ -1112,6 +1166,9 @@ packages/lib/services/rest/routes/search.js.map
packages/lib/services/rest/routes/tags.d.ts
packages/lib/services/rest/routes/tags.js
packages/lib/services/rest/routes/tags.js.map
packages/lib/services/rest/utils/collectionToPaginatedResults.d.ts
packages/lib/services/rest/utils/collectionToPaginatedResults.js
packages/lib/services/rest/utils/collectionToPaginatedResults.js.map
packages/lib/services/rest/utils/defaultAction.d.ts
packages/lib/services/rest/utils/defaultAction.js
packages/lib/services/rest/utils/defaultAction.js.map

1217
.ignore

File diff suppressed because it is too large Load Diff

View File

@@ -66,13 +66,11 @@ script:
# Only do it for pull requests because Travis randomly fails to run them
# and that would break the desktop release.
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
cd packages/app-cli
npm run test-ci
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
cd ../..
fi
# Run linter for pull requests only - this is so that

View File

@@ -64,7 +64,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
| :---: | :---: | :---: |
| <img width="50" src="https://avatars0.githubusercontent.com/u/6979755?s=96&v=4"/></br>[Devon Zuegel](https://github.com/devonzuegel) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[小西 孝宗](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[Alexander van der Berg](https://github.com/avanderberg)
| <img width="50" src="https://avatars0.githubusercontent.com/u/1168659?s=96&v=4"/></br>[Nicholas Head](https://github.com/nicholashead) | <img width="50" src="https://avatars2.githubusercontent.com/u/1439535?s=96&v=4"/></br>[Frank Bloise](https://github.com/fbloise) | <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[Thomas Broussard](https://github.com/thomasbroussard)
| <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[Brandon Johnson](https://github.com/dbrandonjohnson) | |
| <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[Brandon Johnson](https://github.com/dbrandonjohnson) | <img width="50" src="https://avatars1.githubusercontent.com/u/3061769?s=96&v=4"/></br>[@cnagy](https://github.com/c-nagy) |
<!-- TOC -->
# Table of contents
@@ -318,6 +318,8 @@ The currently supported template variables are:
| `{{time}}` | Current time formatted based on the settings format | 13:00 |
| `{{datetime}}` | Current date and time formatted based on the settings format | 01/01/19 1:00 PM |
| `{{#custom_datetime}}` | Current date and/or time formatted based on a supplied string (using [moment.js](https://momentjs.com/) formatting) | `{{#custom_datetime}}M d{{/custom_datetime}}` |
| `{{bowm}}` | Date of the beginning of the week (when week starts on Monday) based on the settings format | |
| `{{bows}}` | Date of the beginning of the week (when week starts on Sunday) based on the settings format | |
# Searching
@@ -411,7 +413,7 @@ Please see the guide for information on how to contribute to the development of
Joplin is currently available in the languages below. If you would like to contribute a **new translation**, it is quite straightforward, please follow these steps:
- [Download Poedit](https://poedit.net/), the translation editor, and install it.
- [Download the file to be translated](https://raw.githubusercontent.com/laurent22/joplin/dev/packages/app-cli/locales/joplin.pot).
- [Download the file to be translated](https://raw.githubusercontent.com/laurent22/joplin/dev/packages/tools/locales/joplin.pot).
- In Poedit, open this .pot file, go into the Catalog menu and click Configuration. Change "Country" and "Language" to your own country and language.
- From then you can translate the file.
- Once it is done, please [open a pull request](https://github.com/laurent22/joplin/pulls) and add the file to it.

View File

@@ -415,7 +415,7 @@ https://github.com/laurent22/joplin/blob/dev/readme/api/get_started/plugins.md
</code></pre>
<p>Doing so should compile all the files into the <code>dist/</code> directory. This is from here that Joplin will load the plugin.</p>
<h2>Testing the plugin<a name="testing-the-plugin" href="#testing-the-plugin" class="heading-anchor">🔗</a></h2>
<p>In order to test the plugin, you might want to run Joplin in <a href="https://github.com/laurent22/joplin/blob/dev/readme/api/references/development_mode/">Development Mode</a>. Doing so means that Joplin will run using a different profile, so you can experiment with the plugin without risking to accidentally change or delete your data.</p>
<p>In order to test the plugin, you might want to run Joplin in <a href="https://joplinapp.org/api/references/development_mode/">Development Mode</a>. Doing so means that Joplin will run using a different profile, so you can experiment with the plugin without risking to accidentally change or delete your data.</p>
<p>Finally, in order to test the plugin, open the Setting screen, then navigate the the <strong>Plugins</strong> section, and add the plugin path in the <strong>Development plugins</strong> text field. For example, if your plugin project path is <code>/home/user/src/joplin-plugin</code>, add this in the text field.</p>
<p>Restart the app, and Joplin should load the plugin and execute its <code>onStart</code> handler. If all went well you should see the test message in the plugin console: &quot;Test plugin started!&quot;.</p>
<h1>Next steps<a name="next-steps" href="#next-steps" class="heading-anchor">🔗</a></h1>

File diff suppressed because one or more lines are too long

View File

@@ -69,6 +69,15 @@
<div class="lead">
<p>This is the main entry point to the Joplin API. You can access various services using the provided accessors.</p>
</div>
<p><strong>This is a beta API</strong></p>
<p>Please note that the plugin API is relatively new and should be considered Beta state. Besides possible bugs, what it means is that there might be necessary breaking changes from one version to the next. Whenever such change is needed, best effort will be done to:</p>
<ul>
<li>Maintain backward compatibility;</li>
<li>When possible, deprecate features instead of removing them;</li>
<li>Document breaking changes in the changelog;</li>
</ul>
<p>So if you are developing a plugin, please keep an eye on the changelog as everything will be in there with information about how to update your code. There won&#39;t be any major API rewrite or architecture changes, but possibly small tweaks like function signature change, type change, etc.</p>
<p>Eventually, the plugin API will be versioned to make this process smoother.</p>
</div>
</section>
<!--
@@ -344,6 +353,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -291,6 +291,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -345,6 +345,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -244,6 +244,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -281,6 +281,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -372,6 +372,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -302,6 +302,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -68,8 +68,9 @@
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>Allows creating and managing dialogs. A dialog is modal window that contains a webview and a row of buttons. You can update the update the webview using the <code>setHtml</code> method.
Dialogs are hidden by default and you need to call <code>open()</code> to open them. Once the user clicks on a button, the <code>open</code> call will return and provide the button ID that was
clicked on. There is currently no &quot;close&quot; method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.</p>
Dialogs are hidden by default and you need to call <code>open()</code> to open them. Once the user clicks on a button, the <code>open</code> call will return an object indicating what button was clicked
on. If your HTML content included one or more form, a <code>formData</code> object will also be included with the key/value for each form.
There is currently no &quot;close&quot; method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.</p>
</div>
<p><a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog">View the demo plugin</a></p>
</div>
@@ -136,7 +137,7 @@
<a name="open" class="tsd-anchor"></a>
<h3>open</h3>
<ul class="tsd-signatures tsd-kind-method tsd-parent-kind-class">
<li class="tsd-signature tsd-kind-icon">open<span class="tsd-signature-symbol">(</span>handle<span class="tsd-signature-symbol">: </span><a href="../globals.html#viewhandle" class="tsd-signature-type">ViewHandle</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol">&lt;</span><a href="../globals.html#buttonid" class="tsd-signature-type">ButtonId</a><span class="tsd-signature-symbol">&gt;</span></li>
<li class="tsd-signature tsd-kind-icon">open<span class="tsd-signature-symbol">(</span>handle<span class="tsd-signature-symbol">: </span><a href="../globals.html#viewhandle" class="tsd-signature-type">ViewHandle</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol">&lt;</span><a href="../interfaces/dialogresult.html" class="tsd-signature-type">DialogResult</a><span class="tsd-signature-symbol">&gt;</span></li>
</ul>
<ul class="tsd-descriptions">
<li class="tsd-description">
@@ -154,7 +155,7 @@
</li>
</ul>
<!-- JOPLINCHANGE
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol">&lt;</span><a href="../globals.html#buttonid" class="tsd-signature-type">ButtonId</a><span class="tsd-signature-symbol">&gt;</span></h4>
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">Promise</span><span class="tsd-signature-symbol">&lt;</span><a href="../interfaces/dialogresult.html" class="tsd-signature-type">DialogResult</a><span class="tsd-signature-symbol">&gt;</span></h4>
-->
@@ -358,6 +359,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -230,6 +230,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -231,6 +231,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -327,6 +327,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -227,6 +227,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -378,6 +378,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -219,6 +219,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -186,6 +186,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -186,6 +186,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -241,6 +241,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -230,6 +230,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -196,6 +196,9 @@
<li class=" tsd-kind-interface">
<a href="../interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="../interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -100,6 +100,7 @@
<li class="tsd-kind-interface"><a href="interfaces/buttonspec.html" class="tsd-kind-icon">Button<wbr>Spec</a></li>
<li class="tsd-kind-interface"><a href="interfaces/command.html" class="tsd-kind-icon">Command</a></li>
<li class="tsd-kind-interface"><a href="interfaces/createmenuitemoptions.html" class="tsd-kind-icon">Create<wbr>Menu<wbr>Item<wbr>Options</a></li>
<li class="tsd-kind-interface"><a href="interfaces/dialogresult.html" class="tsd-kind-icon">Dialog<wbr>Result</a></li>
<li class="tsd-kind-interface"><a href="interfaces/editorcommand.html" class="tsd-kind-icon">Editor<wbr>Command</a></li>
<li class="tsd-kind-interface"><a href="interfaces/exportcontext.html" class="tsd-kind-icon">Export<wbr>Context</a></li>
<li class="tsd-kind-interface"><a href="interfaces/exportmodule.html" class="tsd-kind-icon">Export<wbr>Module</a></li>
@@ -234,6 +235,9 @@
<li class=" tsd-kind-interface">
<a href="interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -146,6 +146,9 @@
<li class=" tsd-kind-interface">
<a href="interfaces/createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="interfaces/dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="interfaces/editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -228,6 +228,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -318,6 +318,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -187,6 +187,9 @@
</li>
</ul>
<ul class="after-current">
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -0,0 +1,272 @@
<!doctype html>
<html class="default no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>DialogResult | Joplin Plugin API Documentation</title>
<meta name="description" content="Documentation for Joplin Plugin API Documentation">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../assets/css/main.css">
</head>
<body>
<header>
<div class="tsd-page-toolbar">
<div class="container">
<div class="table-wrap">
<div class="table-cell" id="tsd-search" data-index="../assets/js/search.json" data-base="..">
<div class="field">
<label for="tsd-search-field" class="tsd-widget search no-caption">Search</label>
<input id="tsd-search-field" type="text" />
</div>
<ul class="results">
<li class="state loading">Preparing search index...</li>
<li class="state failure">The search index is not available</li>
</ul>
<a href="../classes/joplin.html" class="title">Joplin Plugin API Documentation</a>
</div>
<div class="table-cell" id="tsd-widgets">
<div id="tsd-filter">
<a href="#" class="tsd-widget options no-caption" data-toggle="options">Options</a>
<div class="tsd-filter-group">
<div class="tsd-select" id="tsd-filter-visibility">
<span class="tsd-select-label">All</span>
<ul class="tsd-select-list">
<li data-value="public">Public</li>
<li data-value="protected">Public/Protected</li>
<li data-value="private" class="selected">All</li>
</ul>
</div>
<input type="checkbox" id="tsd-filter-inherited" checked />
<label class="tsd-widget" for="tsd-filter-inherited">Inherited</label>
</div>
</div>
<a href="#" class="tsd-widget menu no-caption" data-toggle="menu">Menu</a>
</div>
</div>
</div>
</div>
<div class="tsd-page-title">
<div class="container">
<ul class="tsd-breadcrumb">
<!--
<li>
<a href="../globals.html">Globals</a>
</li>
-->
<li>
<a href="dialogresult.html">DialogResult</a>
</li>
</ul>
<h1><!-- Interface -->DialogResult</h1>
</div>
</div>
</header>
<div class="container container-main">
<div class="row">
<div class="col-8 col-content">
<!--
<section class="tsd-panel tsd-hierarchy">
<h3>Hierarchy</h3>
<ul class="tsd-hierarchy">
<li>
<span class="target">DialogResult</span>
</li>
</ul>
</section>
-->
<section class="tsd-panel-group tsd-index-group">
<h2>Index</h2>
<section class="tsd-panel tsd-index-panel">
<div class="tsd-index-content">
<section class="tsd-index-section ">
<h3>Properties</h3>
<ul class="tsd-index-list">
<li class="tsd-kind-property tsd-parent-kind-interface"><a href="dialogresult.html#formdata" class="tsd-kind-icon">form<wbr>Data</a></li>
<li class="tsd-kind-property tsd-parent-kind-interface"><a href="dialogresult.html#id" class="tsd-kind-icon">id</a></li>
</ul>
</section>
</div>
</section>
</section>
<section class="tsd-panel-group tsd-member-group ">
<h2>Properties</h2>
<section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface">
<a name="formdata" class="tsd-anchor"></a>
<h3><span class="tsd-flag ts-flagOptional">Optional</span> form<wbr>Data</h3>
<div class="tsd-signature tsd-kind-icon">form<wbr>Data<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">any</span></div>
<aside class="tsd-sources">
</aside>
</section>
<section class="tsd-panel tsd-member tsd-kind-property tsd-parent-kind-interface">
<a name="id" class="tsd-anchor"></a>
<h3>id</h3>
<div class="tsd-signature tsd-kind-icon">id<span class="tsd-signature-symbol">:</span> <a href="../globals.html#buttonid" class="tsd-signature-type">ButtonId</a></div>
<aside class="tsd-sources">
</aside>
</section>
</section>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<!--
<nav class="tsd-navigation primary">
<ul>
<li class="globals ">
<a href="../globals.html"><em>Globals</em></a>
</li>
</ul>
</nav>
-->
<nav class="tsd-navigation secondary menu-sticky">
<ul class="before-current">
<li class=" tsd-kind-enum">
<a href="../enums/contentscripttype.html" class="tsd-kind-icon">ContentScriptType</a>
</li>
<li class=" tsd-kind-enum">
<a href="../enums/filesystemitem.html" class="tsd-kind-icon">FileSystemItem</a>
</li>
<li class=" tsd-kind-enum">
<a href="../enums/importmoduleoutputformat.html" class="tsd-kind-icon">ImportModuleOutputFormat</a>
</li>
<li class=" tsd-kind-enum">
<a href="../enums/menuitemlocation.html" class="tsd-kind-icon">MenuItemLocation</a>
</li>
<li class=" tsd-kind-enum">
<a href="../enums/settingitemtype.html" class="tsd-kind-icon">SettingItemType</a>
</li>
<li class=" tsd-kind-enum">
<a href="../enums/toolbarbuttonlocation.html" class="tsd-kind-icon">ToolbarButtonLocation</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplin.html" class="tsd-kind-icon">joplin</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplincommands.html" class="tsd-kind-icon">joplin.commands</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplindata.html" class="tsd-kind-icon">joplin.data</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplininterop.html" class="tsd-kind-icon">joplin.interop</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinplugins.html" class="tsd-kind-icon">joplin.plugins</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinsettings.html" class="tsd-kind-icon">joplin.settings</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinviews.html" class="tsd-kind-icon">joplin.views</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinviewsdialogs.html" class="tsd-kind-icon">joplin.views.dialogs</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinviewsmenuitems.html" class="tsd-kind-icon">joplin.views.menuItems</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinviewsmenus.html" class="tsd-kind-icon">joplin.views.menus</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinviewspanels.html" class="tsd-kind-icon">joplin.views.panels</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinviewstoolbarbuttons.html" class="tsd-kind-icon">joplin.views.toolbarButtons</a>
</li>
<li class=" tsd-kind-class">
<a href="../classes/joplinworkspace.html" class="tsd-kind-icon">joplin.workspace</a>
</li>
<li class=" tsd-kind-interface">
<a href="buttonspec.html" class="tsd-kind-icon">ButtonSpec</a>
</li>
<li class=" tsd-kind-interface">
<a href="command.html" class="tsd-kind-icon">Command</a>
</li>
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
</ul>
<ul class="current">
<li class="current tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
<ul>
<li class=" tsd-kind-property tsd-parent-kind-interface">
<a href="dialogresult.html#formdata" class="tsd-kind-icon">form<wbr>Data</a>
</li>
<li class=" tsd-kind-property tsd-parent-kind-interface">
<a href="dialogresult.html#id" class="tsd-kind-icon">id</a>
</li>
</ul>
</li>
</ul>
<ul class="after-current">
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>
<li class=" tsd-kind-interface">
<a href="exportcontext.html" class="tsd-kind-icon">ExportContext</a>
</li>
<li class=" tsd-kind-interface">
<a href="exportmodule.html" class="tsd-kind-icon">ExportModule</a>
</li>
<li class=" tsd-kind-interface">
<a href="exportoptions.html" class="tsd-kind-icon">ExportOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="importcontext.html" class="tsd-kind-icon">ImportContext</a>
</li>
<li class=" tsd-kind-interface">
<a href="importmodule.html" class="tsd-kind-icon">ImportModule</a>
</li>
<li class=" tsd-kind-interface">
<a href="menuitem.html" class="tsd-kind-icon">MenuItem</a>
</li>
<li class=" tsd-kind-interface">
<a href="script.html" class="tsd-kind-icon">Script</a>
</li>
<li class=" tsd-kind-interface">
<a href="settingitem.html" class="tsd-kind-icon">SettingItem</a>
</li>
<li class=" tsd-kind-interface">
<a href="settingsection.html" class="tsd-kind-icon">SettingSection</a>
</li>
<li class=" tsd-kind-type-alias">
<a href="../globals.html#buttonid" class="tsd-kind-icon">ButtonId</a>
</li>
<li class=" tsd-kind-type-alias">
<a href="../globals.html#path" class="tsd-kind-icon">Path</a>
</li>
<li class=" tsd-kind-type-alias">
<a href="../globals.html#viewhandle" class="tsd-kind-icon">ViewHandle</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<!-- JOPLINCHANGE
<footer class="with-border-bottom">
<div class="container">
<h2>Legend</h2>
<div class="tsd-legend-group">
<ul class="tsd-legend">
<li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li>
<li class="tsd-kind-method tsd-parent-kind-interface"><span class="tsd-kind-icon">Method</span></li>
</ul>
<ul class="tsd-legend">
<li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li>
<li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li>
</ul>
</div>
</div>
</footer>
<div class="container tsd-generator">
<p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p>
</div>
-->
<div class="overlay"></div>
<script src="../assets/js/main.js"></script>
</body>
</html>

View File

@@ -186,6 +186,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
</ul>
<ul class="current">
<li class="current tsd-kind-interface">

View File

@@ -199,6 +199,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -386,6 +386,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -218,6 +218,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -194,6 +194,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -288,6 +288,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -223,6 +223,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -195,6 +195,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -282,6 +282,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -202,6 +202,9 @@
<li class=" tsd-kind-interface">
<a href="createmenuitemoptions.html" class="tsd-kind-icon">CreateMenuItemOptions</a>
</li>
<li class=" tsd-kind-interface">
<a href="dialogresult.html" class="tsd-kind-icon">DialogResult</a>
</li>
<li class=" tsd-kind-interface">
<a href="editorcommand.html" class="tsd-kind-icon">EditorCommand</a>
</li>

View File

@@ -433,19 +433,39 @@ for (let portToTest = 41184; portToTest &lt;= 41194; portToTest++) {
</code></pre>
<p>By default API results will contain the following fields: <strong>id</strong>, <strong>parent_id</strong>, <strong>title</strong></p>
<h1>Pagination<a name="pagination" href="#pagination" class="heading-anchor">🔗</a></h1>
<p>All API calls that return multiple results will be paginated. The actual results will be under the <code>items</code> key, and if there are more results, there will also be a <code>cursor</code> key, which allows you to fetch the next results. If the <code>cursor</code> key is not present, it means you have reached the end of the data set.</p>
<p>All API calls that return multiple results will be paginated and will have the following structure:</p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Always present?</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>items</code></td>
<td>Yes</td>
<td>The array of items you have requested.</td>
</tr>
<tr>
<td><code>has_more</code></td>
<td>Yes</td>
<td>If <code>true</code>, there are more items after this page. If <code>false</code>, it means you have reached the end of the data set.</td>
</tr>
</tbody>
</table>
<p>You can specify how the results should be sorted using the <code>order_by</code> and <code>order_dir</code> query parameters, and you can specify the number of items to be returned using the <code>limit</code> parameter (the maximum being 100 items).</p>
<p>The following call for example will initiate a request to fetch all the notes, 10 at a time, and sorted by &quot;updated_time&quot; ascending:</p>
<pre><code>curl http://localhost:41184/notes?order_by=updated_time&amp;order_dir=ASC&amp;limit=10
</code></pre>
<p>This will return a result like this</p>
<pre><code>{ &quot;items&quot;: [ /* 10 notes */ ], &quot;cursor&quot;: &quot;somecursor&quot; }
<pre><code>{ &quot;items&quot;: [ /* 10 notes */ ], &quot;has_more&quot;: true }
</code></pre>
<p>Then you will resume fetching the results using this query:</p>
<pre><code>curl http://localhost:41184/notes?cursor=somecursor
<pre><code>curl http://localhost:41184/notes?order_by=updated_time&amp;order_dir=ASC&amp;limit=10&amp;page=2
</code></pre>
<p>Note that you only need to pass the cursor to the next request, as it will continue the fetching process using the same parameters you initially provided.</p>
<p>Eventually you will get some results that do not contain a &quot;cursor&quot; paramater, at which point you will have retrieved all the results</p>
<p>Eventually you will get some results that do not contain an &quot;has_more&quot; paramater, at which point you will have retrieved all the results</p>
<p>As an example the pseudo-code below could be used to fetch all the notes:</p>
<pre><code class="language-javascript">
async function fetchJson(url) {
@@ -453,15 +473,11 @@ async function fetchJson(url) {
}
async function fetchAllNotes() {
let query = '';
const url = 'http://localhost:41184/notes';
let pageNum = 1;
do {
const response = await fetchJson(url + query);
console.info('Printing notes:');
console.info(response.items);
query = '?cursor' + response.cursor;
} while (response.cursor)
const response = await fetchJson((http://localhost:41184/notes?page=' + pageNum++);
console.info('Printing notes:', response.items);
} while (response.has_more)
}
</code></pre>
<h1>Error handling<a name="error-handling" href="#error-handling" class="heading-anchor">🔗</a></h1>
@@ -916,6 +932,8 @@ async function fetchAllNotes() {
<p>Gets resource with ID :id</p>
<h2>GET /resources/:id/file<a name="get-resources-id-file" href="#get-resources-id-file" class="heading-anchor">🔗</a></h2>
<p>Gets the actual file associated with this resource.</p>
<h2>GET /resources/:id/notes<a name="get-resources-id-notes" href="#get-resources-id-notes" class="heading-anchor">🔗</a></h2>
<p>Gets the notes (IDs) associated with a resource.</p>
<h2>POST /resources<a name="post-resources" href="#post-resources" class="heading-anchor">🔗</a></h2>
<p>Creates a new resource</p>
<p>Creating a new resource is special because you also need to upload the file. Unlike other API calls, this one must have the &quot;multipart/form-data&quot; Content-Type. The file data must be passed to the &quot;data&quot; form field, and the other properties to the &quot;props&quot; form field. An example of a valid call with cURL would be:</p>

File diff suppressed because it is too large Load Diff

View File

@@ -512,7 +512,7 @@ https://github.com/laurent22/joplin/blob/dev/README.md
</tr>
<tr>
<td style="text-align:center"><img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br><a href="https://github.com/dbrandonjohnson">Brandon Johnson</a></td>
<td style="text-align:center"></td>
<td style="text-align:center"><img width="50" src="https://avatars1.githubusercontent.com/u/3061769?s=96&v=4"/></br><a href="https://github.com/c-nagy">@cnagy</a></td>
<td style="text-align:center"></td>
</tr>
</tbody>
@@ -682,6 +682,16 @@ Details:
<td>Current date and/or time formatted based on a supplied string (using <a href="https://momentjs.com/">moment.js</a> formatting)</td>
<td><code>{{#custom_datetime}}M d{{/custom_datetime}}</code></td>
</tr>
<tr>
<td><code>{{bowm}}</code></td>
<td>Date of the beginning of the week (when week starts on Monday) based on the settings format</td>
<td></td>
</tr>
<tr>
<td><code>{{bows}}</code></td>
<td>Date of the beginning of the week (when week starts on Sunday) based on the settings format</td>
<td></td>
</tr>
</tbody>
</table>
<h1>Searching<a name="searching" href="#searching" class="heading-anchor">🔗</a></h1>
@@ -854,7 +864,7 @@ Eg. <code>:search -- &quot;-tag:tag1&quot;</code>.</p>
<p>Joplin is currently available in the languages below. If you would like to contribute a <strong>new translation</strong>, it is quite straightforward, please follow these steps:</p>
<ul>
<li><a href="https://poedit.net/">Download Poedit</a>, the translation editor, and install it.</li>
<li><a href="https://raw.githubusercontent.com/laurent22/joplin/dev/packages/app-cli/locales/joplin.pot">Download the file to be translated</a>.</li>
<li><a href="https://raw.githubusercontent.com/laurent22/joplin/dev/packages/tools/locales/joplin.pot">Download the file to be translated</a>.</li>
<li>In Poedit, open this .pot file, go into the Catalog menu and click Configuration. Change &quot;Country&quot; and &quot;Language&quot; to your own country and language.</li>
<li>From then you can translate the file.</li>
<li>Once it is done, please <a href="https://github.com/laurent22/joplin/pulls">open a pull request</a> and add the file to it.</li>

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,11 @@
"files.exclude": {
"lerna-debug.log": true,
"_mydocs/mdtest/": true,
"./packages/lib/plugin_types": true,
"_releases/": true,
"_vieux/": true,
".gitignore": true,
".eslintignore": true,
"./packages/app-cli/**/*.*~": true,
"./packages/app-cli/**/*.mo": true,
"./packages/app-cli/**/build/": true,

View File

@@ -10,10 +10,10 @@
"buildApiDoc": "npm start --prefix=packages/app-cli -- apidoc ../../readme/api/references/rest_api.md",
"buildDoc": "./packages/tools/build-all.sh",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/api/references/plugin_api packages/lib/services/plugins/api/",
"buildTranslations": "node packages/tools/build-translation.js",
"buildTranslations": "npm run tsc && node packages/tools/build-translation.js",
"buildWebsite": "npm run buildApiDoc && node ./packages/tools/build-website.js && npm run buildPluginDoc",
"clean": "lerna clean -y && lerna run clean",
"generatePluginTypes": "rm -rf ./plugin_types && node node_modules/typescript/bin/tsc --declaration --declarationDir ./plugin_types --project tsconfig.json",
"generateDatabaseTypes": "node packages/tools/generate-database-types",
"linkChecker": "linkchecker https://joplinapp.org",
"linter-ci": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter-precommit": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
@@ -26,6 +26,8 @@
"releaseDesktop": "node packages/tools/release-electron.js",
"setupNewRelease": "node ./packages/tools/setupNewRelease",
"tsc": "lerna run tsc --stream --parallel",
"test": "lerna run test --stream",
"test-ci": "lerna run test-ci --stream",
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
"watch": "lerna run watch --stream --parallel"
},

View File

@@ -117,7 +117,12 @@ class Command extends BaseCommand {
lines.push('# Pagination');
lines.push('');
lines.push('All API calls that return multiple results will be paginated. The actual results will be under the `items` key, and if there are more results, there will also be a `cursor` key, which allows you to fetch the next results. If the `cursor` key is not present, it means you have reached the end of the data set.');
lines.push('All API calls that return multiple results will be paginated and will have the following structure:');
lines.push('');
lines.push('Key | Always present? | Description');
lines.push('--- | --- | ---');
lines.push('`items` | Yes | The array of items you have requested.');
lines.push('`has_more` | Yes | If `true`, there are more items after this page. If `false`, it means you have reached the end of the data set.');
lines.push('');
lines.push('You can specify how the results should be sorted using the `order_by` and `order_dir` query parameters, and you can specify the number of items to be returned using the `limit` parameter (the maximum being 100 items).');
lines.push('');
@@ -127,15 +132,13 @@ class Command extends BaseCommand {
lines.push('');
lines.push('This will return a result like this');
lines.push('');
lines.push('\t{ "items": [ /* 10 notes */ ], "cursor": "somecursor" }');
lines.push('\t{ "items": [ /* 10 notes */ ], "has_more": true }');
lines.push('');
lines.push('Then you will resume fetching the results using this query:');
lines.push('');
lines.push('\tcurl http://localhost:41184/notes?cursor=somecursor');
lines.push('\tcurl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10&page=2');
lines.push('');
lines.push('Note that you only need to pass the cursor to the next request, as it will continue the fetching process using the same parameters you initially provided.');
lines.push('');
lines.push('Eventually you will get some results that do not contain a "cursor" paramater, at which point you will have retrieved all the results');
lines.push('Eventually you will get some results that do not contain an "has_more" paramater, at which point you will have retrieved all the results');
lines.push('');
lines.push('As an example the pseudo-code below could be used to fetch all the notes:');
lines.push('');
@@ -146,15 +149,11 @@ async function fetchJson(url) {
}
async function fetchAllNotes() {
let query = '';
const url = 'http://localhost:41184/notes';
let pageNum = 1;
do {
const response = await fetchJson(url + query);
console.info('Printing notes:');
console.info(response.items);
query = '?cursor' + response.cursor;
} while (response.cursor)
const response = await fetchJson((http://localhost:41184/notes?page=' + pageNum++);
console.info('Printing notes:', response.items);
} while (response.has_more)
}`);
lines.push('```');
lines.push('');

View File

@@ -6,12 +6,12 @@ import executeSandboxCall from '@joplin/lib/services/plugins/utils/executeSandbo
import Global from '@joplin/lib/services/plugins/api/Global';
import mapEventHandlersToIds, { EventHandlers } from '@joplin/lib/services/plugins/utils/mapEventHandlersToIds';
function createConsoleWrapper(pluginId:string) {
const wrapper:any = {};
function createConsoleWrapper(pluginId: string) {
const wrapper: any = {};
for (const n in console) {
if (!console.hasOwnProperty(n)) continue;
wrapper[n] = (...args:any[]) => {
wrapper[n] = (...args: any[]) => {
const newArgs = args.slice();
newArgs.splice(0, 0, `Plugin "${pluginId}":`);
return (console as any)[n](...newArgs);
@@ -30,7 +30,7 @@ function createConsoleWrapper(pluginId:string) {
export default class PluginRunner extends BasePluginRunner {
private eventHandlers_:EventHandlers = {};
private eventHandlers_: EventHandlers = {};
constructor() {
super();
@@ -38,13 +38,13 @@ export default class PluginRunner extends BasePluginRunner {
this.eventHandler = this.eventHandler.bind(this);
}
private async eventHandler(eventHandlerId:string, args:any[]) {
private async eventHandler(eventHandlerId: string, args: any[]) {
const cb = this.eventHandlers_[eventHandlerId];
return cb(...args);
}
private newSandboxProxy(pluginId:string, sandbox:Global) {
const target = async (path:string, args:any[]) => {
private newSandboxProxy(pluginId: string, sandbox: Global) {
const target = async (path: string, args: any[]) => {
return executeSandboxCall(pluginId, sandbox, `joplin.${path}`, mapEventHandlersToIds(args, this.eventHandlers_), this.eventHandler);
};
@@ -54,8 +54,8 @@ export default class PluginRunner extends BasePluginRunner {
};
}
async run(plugin:Plugin, sandbox:Global):Promise<void> {
return new Promise((resolve:Function, reject:Function) => {
async run(plugin: Plugin, sandbox: Global): Promise<void> {
return new Promise((resolve: Function, reject: Function) => {
const onStarted = () => {
plugin.off('started', onStarted);
resolve();

View File

@@ -0,0 +1,47 @@
module.exports = {
testMatch: [
'**/tests/**/*.js',
// '**/tests/services_keychainService.js',
// '**/tests/urlUtils.js',
// '**/tests/models_BaseItem.js',
// '**/tests/markdownUtils.js',
// '**/tests/models_Resource.js',
],
testPathIgnorePatterns: [
'/node_modules/',
'/tests\\/support/',
'/build/',
'test-utils.js',
'file_api_driver.js',
],
testEnvironment: 'node',
setupFilesAfterEnv: ['./jest.setup.js'],
};
// PASS tests/services_rest_Api.js (14.705 s)
// PASS tests/services_SearchFilter.js (21.203 s)
// PASS tests/services_InteropService.js (10.807 s)
// PASS tests/reducer.js (17.398 s)
// PASS tests/services_Revision.js (17.642 s)
// PASS tests/feature_NoteHistory.js (31.448 s)
// PASS tests/services_KeymapService.js
// PASS tests/services_EncryptionService.js (9.258 s)
// PASS tests/models_Note.js (10.59 s)
// PASS tests/services_ResourceService.js (13.177 s)
// PASS tests/models_Folder.js (11.468 s)
// PASS tests/MdToHtml.js (5.242 s)
// PASS tests/services_PluginService.js (5.456 s)
// PASS tests/models_Note_CustomSortOrder.js (5.52 s)
// PASS tests/feature_ShowAllNotes.js (13.567 s)
// PASS tests/models_Setting.js
// PASS tests/models_Tag.js (6.324 s)
// PASS tests/services_keychainService.js
// PASS tests/urlUtils.js
// PASS tests/models_BaseItem.js
// PASS tests/markdownUtils.js
// (node:15679) UnhandledPromiseRejectionWarning: Error: {"title":"folder1"}: Fields have not been loaded yet
// (node:15679) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To termi$ate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 31)
// (node:15679) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
// FAIL tests/models_Resource.js

View File

@@ -0,0 +1,5 @@
const { afterEachCleanUp } = require('./tests/test-utils.js');
global.afterEach(async () => {
await afterEachCleanUp();
});

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,8 @@
"author": "Laurent Cozic",
"private": true,
"scripts": {
"test": "node node_modules/jasmine/bin/jasmine.js --fail-fast=true --config=tests/support/jasmine.json",
"test-ci": "node node_modules/jasmine/bin/jasmine.js --config=tests/support/jasmine.json",
"test": "jest --config=jest.config.js --runInBand --bail --forceExit",
"test-ci": "jest --config=jest.config.js --runInBand --forceExit",
"build": "gulp build",
"start": "gulp build -L && node \"build/main.js\" --stack-trace-enabled --log-level debug --env dev",
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
@@ -69,6 +69,7 @@
"@types/node": "^14.14.6",
"gulp": "^4.0.2",
"jasmine": "^3.5.0",
"jest": "^26.6.3",
"temp": "^0.9.1",
"typescript": "^4.0.5"
}

View File

@@ -5,7 +5,7 @@ const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('./tes
const shim = require('@joplin/lib/shim').default;
const { themeStyle } = require('@joplin/lib/theme');
function newTestMdToHtml(options:any = null) {
function newTestMdToHtml(options: any = null) {
options = {
ResourceModel: {
isResourceUrl: () => false,
@@ -19,7 +19,7 @@ function newTestMdToHtml(options:any = null) {
describe('MdToHtml', function() {
beforeEach(async (done:Function) => {
beforeEach(async (done: Function) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
@@ -39,7 +39,7 @@ describe('MdToHtml', function() {
// if (mdFilename !== 'sanitize_9.md') continue;
const mdToHtmlOptions:any = {
const mdToHtmlOptions: any = {
bodyOnly: true,
};
@@ -83,7 +83,7 @@ describe('MdToHtml', function() {
}));
it('should return enabled plugin assets', asyncTest(async () => {
const pluginOptions:any = {};
const pluginOptions: any = {};
const pluginNames = MdToHtml.pluginNames();
for (const n of pluginNames) pluginOptions[n] = { enabled: false };

View File

@@ -14,6 +14,11 @@ process.on('unhandledRejection', (reason, p) => {
const api = null;
// Adding empty test for Jest
it('will pass', () => {
expect(true).toBe(true);
});
// NOTE: These tests work with S3 and memory driver, but not
// with other targets like file system or Nextcloud.
// All this is tested in an indirect way in tests/synchronizer

View File

@@ -12,125 +12,125 @@ const makeTerm = (name, value, negated, quoted = false, wildcard = false) => {
describe('filterParser should be correct filter for keyword', () => {
it('title', () => {
const searchString = 'title: something';
expect(filterParser(searchString)).toContain(makeTerm('title', 'something', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'something', false));
});
it('negated title', () => {
const searchString = '-title: something';
expect(filterParser(searchString)).toContain(makeTerm('title', 'something', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'something', true));
});
it('body', () => {
const searchString = 'body:something';
expect(filterParser(searchString)).toContain(makeTerm('body', 'something', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'something', false));
});
it('negated body', () => {
const searchString = '-body:something';
expect(filterParser(searchString)).toContain(makeTerm('body', 'something', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'something', true));
});
it('title and body', () => {
const searchString = 'title:testTitle body:testBody';
expect(filterParser(searchString)).toContain(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'testBody', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'testBody', false));
});
it('title with multiple words', () => {
const searchString = 'title:"word1 word2" body:testBody';
expect(filterParser(searchString)).toContain(makeTerm('title', 'word1', false));
expect(filterParser(searchString)).toContain(makeTerm('title', 'word2', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'testBody', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'word1', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'word2', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'testBody', false));
});
it('body with multiple words', () => {
const searchString = 'title:testTitle body:"word1 word2"';
expect(filterParser(searchString)).toContain(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'word1', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'word2', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'word1', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'word2', false));
});
it('single word text', () => {
const searchString = 'joplin';
expect(filterParser(searchString)).toContain(makeTerm('text', '"joplin"', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"joplin"', false));
});
it('multi word text', () => {
const searchString = 'scott joplin';
expect(filterParser(searchString)).toContain(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContain(makeTerm('text', '"joplin"', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"joplin"', false));
});
it('negated word text', () => {
const searchString = 'scott -joplin';
expect(filterParser(searchString)).toContain(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContain(makeTerm('text', '"joplin"', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"joplin"', true));
});
it('phrase text search', () => {
const searchString = '"scott joplin"';
expect(filterParser(searchString)).toContain(makeTerm('text', '"scott joplin"', false, true));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"scott joplin"', false, true));
});
it('multi word body', () => {
const searchString = 'body:"foo bar"';
expect(filterParser(searchString)).toContain(makeTerm('body', 'foo', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'bar', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'foo', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'bar', false));
});
it('negated tag queries', () => {
const searchString = '-tag:mozart';
expect(filterParser(searchString)).toContain(makeTerm('tag', 'mozart', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', 'mozart', true));
});
it('created after', () => {
const searchString = 'created:20151218'; // YYYYMMDD
expect(filterParser(searchString)).toContain(makeTerm('created', '20151218', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('created', '20151218', false));
});
it('created before', () => {
const searchString = '-created:20151218'; // YYYYMMDD
expect(filterParser(searchString)).toContain(makeTerm('created', '20151218', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('created', '20151218', true));
});
it('any', () => {
const searchString = 'any:1 tag:123';
expect(filterParser(searchString)).toContain(makeTerm('any', '1', false));
expect(filterParser(searchString)).toContain(makeTerm('tag', '123', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('any', '1', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', '123', false));
});
it('wildcard tags', () => {
let searchString = 'tag:*';
expect(filterParser(searchString)).toContain(makeTerm('tag', '%', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', '%', false));
searchString = '-tag:*';
expect(filterParser(searchString)).toContain(makeTerm('tag', '%', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', '%', true));
searchString = 'tag:bl*sphemy';
expect(filterParser(searchString)).toContain(makeTerm('tag', 'bl%sphemy', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', 'bl%sphemy', false));
searchString = 'tag:"space travel"';
expect(filterParser(searchString)).toContain(makeTerm('tag', 'space travel', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', 'space travel', false));
});
it('wildcard notebooks', () => {
const searchString = 'notebook:my*notebook';
expect(filterParser(searchString)).toContain(makeTerm('notebook', 'my%notebook', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('notebook', 'my%notebook', false));
});
it('wildcard MIME types', () => {
const searchString = 'resource:image/*';
expect(filterParser(searchString)).toContain(makeTerm('resource', 'image/%', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('resource', 'image/%', false));
});
it('sourceurl', () => {
let searchString = 'sourceurl:https://www.google.com';
expect(filterParser(searchString)).toContain(makeTerm('sourceurl', 'https://www.google.com', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('sourceurl', 'https://www.google.com', false));
searchString = 'sourceurl:https://www.google.com -sourceurl:https://www.facebook.com';
expect(filterParser(searchString)).toContain(makeTerm('sourceurl', 'https://www.google.com', false));
expect(filterParser(searchString)).toContain(makeTerm('sourceurl', 'https://www.facebook.com', true));
expect(filterParser(searchString)).toContainEqual(makeTerm('sourceurl', 'https://www.google.com', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('sourceurl', 'https://www.facebook.com', true));
});
it('handle invalid filters', () => {

View File

@@ -4,7 +4,7 @@ const { expectThrow } = require('./test-utils.js');
// On Windows, path.resolve is going to convert a path such as
// /tmp/file.txt to c:\tmp\file.txt
function platformPath(path:string) {
function platformPath(path: string) {
if (shim.isWindows()) {
return `c:${path.replace(/\//g, '\\')}`;
} else {

View File

@@ -5,7 +5,7 @@ const { asyncTest } = require('./test-utils.js');
const markdownUtils = require('@joplin/lib/markdownUtils').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at markdownUtils: Promise', p, 'reason:', reason);
});
describe('markdownUtils', function() {

View File

@@ -11,7 +11,7 @@ const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at models_BaseItem: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -9,7 +9,7 @@ const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at models_Folder: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -11,7 +11,7 @@ const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at models_Note: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -11,7 +11,7 @@ const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at models_Note_CustomSortOrder: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -3,7 +3,7 @@ import Setting from '@joplin/lib/models/Setting';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at models_Setting: Promise', p, 'reason:', reason);
});
describe('models_Setting', function() {

View File

@@ -11,7 +11,7 @@ const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at models_Tag: Promise', p, 'reason:', reason);
});
describe('models_Tag', function() {

View File

@@ -1,16 +0,0 @@
// import Setting from '@joplin/lib/models/Setting';
// const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('../test-utils.js');
// describe('plugin_api_JoplinSetting', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// done();
// });
// it('should get and set plugin-specific values', asyncTest(async () => {
// await
// }));
// });

View File

@@ -4,7 +4,7 @@ const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('../..
describe('services_plugins_sandboxProxy', function() {
beforeEach(async (done:Function) => {
beforeEach(async (done: Function) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
@@ -12,13 +12,13 @@ describe('services_plugins_sandboxProxy', function() {
it('should create a new sandbox proxy', asyncTest(async () => {
interface Result {
path: string,
args: any[],
path: string;
args: any[];
}
const results:Result[] = [];
const results: Result[] = [];
const target:Target = (path:string, args:any[]) => {
const target: Target = (path: string, args: any[]) => {
results.push({ path, args });
};
@@ -35,13 +35,13 @@ describe('services_plugins_sandboxProxy', function() {
it('should allow importing a namespace', asyncTest(async () => {
interface Result {
path: string,
args: any[],
path: string;
args: any[];
}
const results:Result[] = [];
const results: Result[] = [];
const target:Target = (path:string, args:any[]) => {
const target: Target = (path: string, args: any[]) => {
results.push({ path, args });
};

View File

@@ -1,31 +1,33 @@
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils';
import CommandService, { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext';
import KeymapService from '@joplin/lib/services/KeymapService';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js');
interface TestCommand {
declaration: CommandDeclaration,
runtime: CommandRuntime,
declaration: CommandDeclaration;
runtime: CommandRuntime;
}
function newService():CommandService {
function newService(): CommandService {
const service = new CommandService();
const mockStore = {
getState: () => {
return {};
},
};
service.initialize(mockStore, true);
service.initialize(mockStore, true, stateToWhenClauseContext);
return service;
}
function createCommand(name:string, options:any):TestCommand {
const declaration:CommandDeclaration = {
function createCommand(name: string, options: any): TestCommand {
const declaration: CommandDeclaration = {
name: name,
};
const runtime:CommandRuntime = {
const runtime: CommandRuntime = {
execute: options.execute,
};
@@ -34,7 +36,7 @@ function createCommand(name:string, options:any):TestCommand {
return { declaration, runtime };
}
function registerCommand(service:CommandService, cmd:TestCommand) {
function registerCommand(service: CommandService, cmd: TestCommand) {
service.registerDeclaration(cmd.declaration);
service.registerRuntime(cmd.declaration.name, cmd.runtime);
}
@@ -42,6 +44,9 @@ function registerCommand(service:CommandService, cmd:TestCommand) {
describe('services_CommandService', function() {
beforeEach(async (done) => {
KeymapService.destroyInstance();
KeymapService.instance().initialize();
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
@@ -51,7 +56,7 @@ describe('services_CommandService', function() {
const service = newService();
const toolbarButtonUtils = new ToolbarButtonUtils(service);
const executedCommands:string[] = [];
const executedCommands: string[] = [];
registerCommand(service, createCommand('test1', {
execute: () => {
@@ -168,9 +173,9 @@ describe('services_CommandService', function() {
execute: () => {},
}));
const clickedCommands:string[] = [];
const clickedCommands: string[] = [];
const onClick = (commandName:string) => {
const onClick = (commandName: string) => {
clickedCommands.push(commandName);
};
@@ -230,7 +235,7 @@ describe('services_CommandService', function() {
let propValue = null;
registerCommand(service, createCommand('test1', {
execute: (_context:any, greeting:string) => {
execute: (_context: any, greeting: string) => {
propValue = greeting;
},
}));

View File

@@ -15,7 +15,7 @@ const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
const EncryptionService = require('@joplin/lib/services/EncryptionService.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at services_EncryptionService: Promise', p, 'reason:', reason);
});
let service = null;

View File

@@ -12,14 +12,14 @@ const fs = require('fs-extra');
const ArrayUtils = require('@joplin/lib/ArrayUtils');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at services_InteropService: Promise', p, 'reason:', reason);
});
function exportDir() {
return `${__dirname}/export`;
}
function fieldsEqual(model1:any, model2:any, fieldNames:string[]) {
function fieldsEqual(model1: any, model2: any, fieldNames: string[]) {
for (let i = 0; i < fieldNames.length; i++) {
const f = fieldNames[i];
expect(model1[f]).toBe(model2[f], `For key ${f}`);
@@ -86,7 +86,7 @@ describe('services_InteropService', function() {
await service.import({ path: filePath });
const allFolders = await Folder.all();
expect(allFolders.map((f:any) => f.title).sort().join(' - ')).toBe('folder - folder (1)');
expect(allFolders.map((f: any) => f.title).sort().join(' - ')).toBe('folder - folder (1)');
}));
it('should import folders, and only de-duplicate titles when needed', asyncTest(async () => {
@@ -447,14 +447,14 @@ describe('services_InteropService', function() {
sourcePath: '',
};
const module:Module = {
const module: Module = {
type: ModuleType.Importer,
description: 'Test Import Module',
format: 'testing',
fileExtensions: ['test'],
isCustom: true,
onExec: async (context:CustomImportContext) => {
onExec: async (context: CustomImportContext) => {
result.hasBeenExecuted = true;
result.sourcePath = context.sourcePath;
},
@@ -479,7 +479,7 @@ describe('services_InteropService', function() {
const filePath = `${exportDir()}/example.test`;
const result:any = {
const result: any = {
destPath: '',
itemTypes: [],
items: [],
@@ -488,28 +488,28 @@ describe('services_InteropService', function() {
closeCalled: false,
};
const module:Module = {
const module: Module = {
type: ModuleType.Exporter,
description: 'Test Export Module',
format: 'testing',
fileExtensions: ['test'],
isCustom: true,
onInit: async (context:CustomExportContext) => {
onInit: async (context: CustomExportContext) => {
result.destPath = context.destPath;
},
onProcessItem: async (_context:CustomExportContext, itemType:number, item:any) => {
onProcessItem: async (_context: CustomExportContext, itemType: number, item: any) => {
result.itemTypes.push(itemType);
result.items.push(item);
},
onProcessResource: async (_context:CustomExportContext, resource:any, filePath:string) => {
onProcessResource: async (_context: CustomExportContext, resource: any, filePath: string) => {
result.resources.push(resource);
result.filePaths.push(filePath);
},
onClose: async (_context:CustomExportContext) => {
onClose: async (_context: CustomExportContext) => {
result.closeCalled = true;
},
};
@@ -524,7 +524,7 @@ describe('services_InteropService', function() {
expect(result.destPath).toBe(filePath);
expect(result.itemTypes.sort().join('_')).toBe('1_1_2_4');
expect(result.items.length).toBe(4);
expect(result.items.map((o:any) => o.title).sort().join('_')).toBe('folder1_note1_note2_photo.jpg');
expect(result.items.map((o: any) => o.title).sort().join('_')).toBe('folder1_note1_note2_photo.jpg');
expect(result.resources.length).toBe(1);
expect(result.resources[0].title).toBe('photo.jpg');
expect(result.filePaths.length).toBe(1);

View File

@@ -9,7 +9,7 @@ const Note = require('@joplin/lib/models/Note');
const Folder = require('@joplin/lib/models/Folder');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at services_PluginService: Promise', p, 'reason:', reason);
});
const testPluginDir = `${__dirname}/../tests/support/plugins`;
@@ -44,6 +44,8 @@ describe('services_PluginService', function() {
const service = newPluginService();
await service.loadAndRunPlugins([`${testPluginDir}/simple`]);
expect(() => service.pluginById('simple')).not.toThrowError();
const allFolders = await Folder.all();
expect(allFolders.length).toBe(1);
expect(allFolders[0].title).toBe('my plugin folder');
@@ -54,6 +56,12 @@ describe('services_PluginService', function() {
expect(allNotes[0].parent_id).toBe(allFolders[0].id);
}));
it('should load and run a simple plugin and handle trailing slash', asyncTest(async () => {
const service = newPluginService();
await service.loadAndRunPlugins([`${testPluginDir}/simple/`]);
expect(() => service.pluginById('simple')).not.toThrowError();
}));
it('should load and run a plugin that uses external packages', asyncTest(async () => {
const service = newPluginService();
await service.loadAndRunPlugins([`${testPluginDir}/withExternalModules`]);
@@ -79,7 +87,7 @@ describe('services_PluginService', function() {
const allFolders = await Folder.all();
expect(allFolders.length).toBe(2);
expect(allFolders.map((f:any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
expect(allFolders.map((f: any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
}));
it('should load plugins from JS bundles', asyncTest(async () => {

View File

@@ -15,7 +15,7 @@ const RevisionService = require('@joplin/lib/services/RevisionService.js');
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at services_Revision: Promise', p, 'reason:', reason);
});
describe('services_Revision', function() {

View File

@@ -157,85 +157,88 @@ describe('services_SearchEngine', function() {
expect(rows[1].id).toBe(n2.id);
}));
it('should correctly weigh notes using BM25 and user_updated_time', asyncTest(async () => {
await mockDate(2020, 9, 30, 50);
const noteData = [
{
title: 'abc test2 test2',
updated_time: 1601425064756,
user_updated_time: 1601425064756,
created_time: 1601425064756,
user_created_time: 1601425064756,
},
{
title: 'foo foo',
updated_time: 1601425064758,
user_updated_time: 1601425064758,
created_time: 1601425064758,
user_created_time: 1601425064758,
},
{
title: 'dead beef',
updated_time: 1601425064760,
user_updated_time: 1601425064760,
created_time: 1601425064760,
user_created_time: 1601425064760,
},
{
title: 'test2 bar',
updated_time: 1601425064761,
user_updated_time: 1601425064761,
created_time: 1601425064761,
user_created_time: 1601425064761,
},
{
title: 'blah blah abc',
updated_time: 1601425064763,
user_updated_time: 1601425064763,
created_time: 1601425064763,
user_created_time: 1601425064763,
},
];
// TODO: Need to update and replace jasmine.mockDate() calls with Jest
// equivalent
const n0 = await Note.save(noteData[0], { autoTimestamp: false });
const n1 = await Note.save(noteData[1], { autoTimestamp: false });
const n2 = await Note.save(noteData[2], { autoTimestamp: false });
const n3 = await Note.save(noteData[3], { autoTimestamp: false });
const n4 = await Note.save(noteData[4], { autoTimestamp: false });
restoreDate();
await engine.syncTables();
await mockDate(2020, 9, 30, 50);
// it('should correctly weigh notes using BM25 and user_updated_time', asyncTest(async () => {
// await mockDate(2020, 9, 30, 50);
// const noteData = [
// {
// title: 'abc test2 test2',
// updated_time: 1601425064756,
// user_updated_time: 1601425064756,
// created_time: 1601425064756,
// user_created_time: 1601425064756,
// },
// {
// title: 'foo foo',
// updated_time: 1601425064758,
// user_updated_time: 1601425064758,
// created_time: 1601425064758,
// user_created_time: 1601425064758,
// },
// {
// title: 'dead beef',
// updated_time: 1601425064760,
// user_updated_time: 1601425064760,
// created_time: 1601425064760,
// user_created_time: 1601425064760,
// },
// {
// title: 'test2 bar',
// updated_time: 1601425064761,
// user_updated_time: 1601425064761,
// created_time: 1601425064761,
// user_created_time: 1601425064761,
// },
// {
// title: 'blah blah abc',
// updated_time: 1601425064763,
// user_updated_time: 1601425064763,
// created_time: 1601425064763,
// user_created_time: 1601425064763,
// },
// ];
let searchString = 'abc';
let scores = calculateScore(searchString, noteData);
let rows = await engine.search(searchString);
// const n0 = await Note.save(noteData[0], { autoTimestamp: false });
// const n1 = await Note.save(noteData[1], { autoTimestamp: false });
// const n2 = await Note.save(noteData[2], { autoTimestamp: false });
// const n3 = await Note.save(noteData[3], { autoTimestamp: false });
// const n4 = await Note.save(noteData[4], { autoTimestamp: false });
// restoreDate();
// await engine.syncTables();
// await mockDate(2020, 9, 30, 50);
expect(rows[0].weight).toEqual(scores[0]);
expect(rows[1].weight).toEqual(scores[1]);
// let searchString = 'abc';
// let scores = calculateScore(searchString, noteData);
// let rows = await engine.search(searchString);
// console.log(rows);
// console.log(scores);
// expect(rows[0].weight).toEqual(scores[0]);
// expect(rows[1].weight).toEqual(scores[1]);
searchString = 'test2';
scores = calculateScore(searchString, noteData);
rows = await engine.search(searchString);
// // console.log(rows);
// // console.log(scores);
// console.log(rows);
// console.log(scores);
// searchString = 'test2';
// scores = calculateScore(searchString, noteData);
// rows = await engine.search(searchString);
expect(rows[0].weight).toEqual(scores[0]);
expect(rows[1].weight).toEqual(scores[1]);
// // console.log(rows);
// // console.log(scores);
searchString = 'foo';
scores = calculateScore(searchString, noteData);
rows = await engine.search(searchString);
// expect(rows[0].weight).toEqual(scores[0]);
// expect(rows[1].weight).toEqual(scores[1]);
// console.log(rows);
// console.log(scores);
// searchString = 'foo';
// scores = calculateScore(searchString, noteData);
// rows = await engine.search(searchString);
expect(rows[0].weight).toEqual(scores[0]);
await restoreDate();
}));
// // console.log(rows);
// // console.log(scores);
// expect(rows[0].weight).toEqual(scores[0]);
// await restoreDate();
// }));
it('should tell where the results are found', asyncTest(async () => {
const notes = [

View File

@@ -16,7 +16,7 @@ const ResourceService = require('@joplin/lib/services/ResourceService').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at services_SearchFilter: Promise', p, 'reason:', reason);
});
let engine = null;

View File

@@ -1,161 +0,0 @@
/* eslint-disable no-unused-vars */
/* eslint prefer-const: 0*/
//
// const time = require('@joplin/lib/time').default;
// const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, createNTestNotes, switchClient, createNTestFolders } = require('./test-utils.js');
// const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine');
// const Note = require('@joplin/lib/models/Note');
// const Folder = require('@joplin/lib/models/Folder');
// const Tag = require('@joplin/lib/models/Tag');
// const ItemChange = require('@joplin/lib/models/ItemChange');
// const Setting = require('@joplin/lib/models/Setting');
// const Resource = require('@joplin/lib/models/Resource.js');
// const shim = require('@joplin/lib/shim').default;
// const ResourceService = require('@joplin/lib/services/ResourceService').default;
// process.on('unhandledRejection', (reason, p) => {
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
// });
// let engine = null;
// const ids = (array) => array.map(a => a.id);
// describe('services_SearchFuzzy', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// engine = new SearchEngine();
// engine.setDb(db());
// Setting.setValue('db.fuzzySearchEnabled', 1);
// done();
// });
// it('should return note almost matching title', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'If It Ain\'t Baroque, Don\'t Fix It' });
// const n2 = await Note.save({ title: 'Important note' });
// await engine.syncTables();
// rows = await engine.search('Broke', { fuzzy: false });
// expect(rows.length).toBe(0);
// rows = await engine.search('Broke', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('title:Broke', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('title:"Broke"', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('Imprtant', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n2.id);
// }));
// it('should order results by min fuzziness', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'I demand you take me to him' });
// const n2 = await Note.save({ title: 'He demanded an answer' });
// const n3 = await Note.save({ title: 'Don\'t you make demands of me' });
// const n4 = await Note.save({ title: 'No drama for me' });
// const n5 = await Note.save({ title: 'Just minding my own business' });
// await engine.syncTables();
// rows = await engine.search('demand', { fuzzy: false });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('demand', { fuzzy: true });
// expect(rows.length).toBe(3);
// expect(rows[0].id).toBe(n1.id);
// expect(rows[1].id).toBe(n3.id);
// expect(rows[2].id).toBe(n2.id);
// }));
// it('should consider any:1', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'cat' });
// const n2 = await Note.save({ title: 'cats' });
// const n3 = await Note.save({ title: 'cot' });
// const n4 = await Note.save({ title: 'defenestrate' });
// const n5 = await Note.save({ title: 'defenstrate' });
// const n6 = await Note.save({ title: 'defenestrated' });
// const n7 = await Note.save({ title: 'he defenestrated the cat' });
// await engine.syncTables();
// rows = await engine.search('defenestrated cat', { fuzzy: true });
// expect(rows.length).toBe(1);
// rows = await engine.search('any:1 defenestrated cat', { fuzzy: true });
// expect(rows.length).toBe(7);
// }));
// it('should leave phrase searches alone', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'abc def' });
// const n2 = await Note.save({ title: 'def ghi' });
// const n3 = await Note.save({ title: 'ghi jkl' });
// const n4 = await Note.save({ title: 'def abc' });
// const n5 = await Note.save({ title: 'mno pqr ghi jkl' });
// await engine.syncTables();
// rows = await engine.search('abc def', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// expect(rows.map(r=>r.id)).toContain(n4.id);
// rows = await engine.search('"abc def"', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// rows = await engine.search('"ghi jkl"', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// rows = await engine.search('"ghi jkl" mno', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// rows = await engine.search('any:1 "ghi jkl" mno', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// }));
// it('should leave wild card searches alone', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'abc def' });
// const n2 = await Note.save({ title: 'abcc ghi' });
// const n3 = await Note.save({ title: 'abccc ghi' });
// const n4 = await Note.save({ title: 'abcccc ghi' });
// const n5 = await Note.save({ title: 'wxy zzz' });
// await engine.syncTables();
// rows = await engine.search('abc*', { fuzzy: true });
// expect(rows.length).toBe(4);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// expect(rows.map(r=>r.id)).toContain(n2.id);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n4.id);
// }));
// });

View File

@@ -4,22 +4,24 @@ import Setting from '@joplin/lib/models/Setting';
const { db, asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
function describeIfCompatible(name:string, fn:any) {
function describeIfCompatible(name: string, fn: any, elseFn: any) {
if (['win32', 'darwin'].includes(shim.platformName())) {
return describe(name, fn);
} else {
elseFn();
}
}
describeIfCompatible('services_KeychainService', function() {
beforeEach(async (done:Function) => {
beforeEach(async (done: Function) => {
await setupDatabaseAndSynchronizer(1, { keychainEnabled: true });
await switchClient(1, { keychainEnabled: true });
await Setting.deleteKeychainPasswords();
done();
});
afterEach(async (done:Function) => {
afterEach(async (done: Function) => {
await Setting.deleteKeychainPasswords();
done();
});
@@ -91,4 +93,10 @@ describeIfCompatible('services_KeychainService', function() {
}
}));
}, () => {
it('will pass', () => {
expect(true).toBe(true);
});
});

View File

@@ -2,15 +2,16 @@ import { PaginationOrderDir } from '@joplin/lib/models/utils/types';
import Api, { RequestMethod } from '@joplin/lib/services/rest/Api';
import shim from '@joplin/lib/shim';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, db } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder');
const Resource = require('@joplin/lib/models/Resource');
const Note = require('@joplin/lib/models/Note');
const Tag = require('@joplin/lib/models/Tag');
const NoteTag = require('@joplin/lib/models/NoteTag');
const ResourceService = require('@joplin/lib/services/ResourceService').default;
const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine');
async function msleep(ms:number) {
async function msleep(ms: number) {
return new Promise((resolve) => {
shim.setTimeout(() => {
resolve();
@@ -18,7 +19,7 @@ async function msleep(ms:number) {
});
}
const createFolderForPagination = async (num:number, time:number) => {
const createFolderForPagination = async (num: number, time: number) => {
await Folder.save({
title: `folder${num}`,
updated_time: time,
@@ -26,7 +27,16 @@ const createFolderForPagination = async (num:number, time:number) => {
}, { autoTimestamp: false });
};
let api:Api = null;
const createNoteForPagination = async (num: number, time: number) => {
await Note.save({
title: `note${num}`,
body: `noteBody${num}`,
updated_time: time,
created_time: time,
}, { autoTimestamp: false });
};
let api: Api = null;
describe('services_rest_Api', function() {
@@ -148,7 +158,7 @@ describe('services_rest_Api', function() {
}));
it('should allow setting note properties', asyncTest(async () => {
let response:any = null;
let response: any = null;
const f = await Folder.save({ title: 'mon carnet' });
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
@@ -353,22 +363,6 @@ describe('services_rest_Api', function() {
expect(response.body).toBe('**Bold text**');
}));
// it('should filter fields', asyncTest(async () => {
// let f = api.fields_({ query: { fields: 'one,two' } } as any, []);
// expect(f.length).toBe(2);
// expect(f[0]).toBe('one');
// expect(f[1]).toBe('two');
// f = api.fields_({ query: { fields: 'one ,, two ' } } as any, []);
// expect(f.length).toBe(2);
// expect(f[0]).toBe('one');
// expect(f[1]).toBe('two');
// f = api.fields_({ query: { fields: ' ' } } as any, ['def']);
// expect(f.length).toBe(1);
// expect(f[0]).toBe('def');
// }));
it('should handle tokens', asyncTest(async () => {
api = new Api('mytoken');
@@ -513,82 +507,117 @@ describe('services_rest_Api', function() {
await createFolderForPagination(4, 1004);
{
const r1 = await api.route(RequestMethod.GET, 'folders', {
const baseQuery = {
fields: ['id', 'title', 'updated_time'],
limit: 2,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
});
};
const r1 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery });
expect(r1.has_more).toBe(true);
expect(r1.items.length).toBe(2);
expect(r1.items[0].title).toBe('folder1');
expect(r1.items[1].title).toBe('folder2');
const r2 = await api.route(RequestMethod.GET, 'folders', {
cursor: r1.cursor,
});
const r2 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 2 });
// The API currently doesn't check if there's effectively a
// page of data after the current one. If the number of
// returned items === limit, it sets `has_more` and the next
// result set will be empty
expect(r1.has_more).toBe(true);
expect(r2.items.length).toBe(2);
expect(r2.items[0].title).toBe('folder3');
expect(r2.items[1].title).toBe('folder4');
const r3 = await api.route(RequestMethod.GET, 'folders', {
cursor: r2.cursor,
});
const r3 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 3 });
expect(r3.items.length).toBe(0);
expect(r3.cursor).toBe(undefined);
expect(r3.has_more).toBe(undefined);
}
{
const r1 = await api.route(RequestMethod.GET, 'folders', {
const baseQuery = {
fields: ['id', 'title', 'updated_time'],
limit: 3,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
});
};
const r1 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery });
expect(r1.items.length).toBe(3);
expect(r1.items[0].title).toBe('folder1');
expect(r1.items[1].title).toBe('folder2');
expect(r1.items[2].title).toBe('folder3');
const r2 = await api.route(RequestMethod.GET, 'folders', {
cursor: r1.cursor,
});
const r2 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 2 });
expect(r2.items.length).toBe(1);
expect(r2.items[0].title).toBe('folder4');
expect(r2.cursor).toBe(undefined);
expect(r2.has_more).toBe(undefined);
}
}));
it('should paginate results and handle duplicate cursor field value', asyncTest(async () => {
it('should paginate results and handle duplicate field values', asyncTest(async () => {
// If, for example, ordering by updated_time, and two of the rows
// have the same updated_time, it should make sure that the sort
// order is stable and all results are correctly returned.
await createFolderForPagination(1, 1001);
await createFolderForPagination(2, 1002);
await createFolderForPagination(3, 1002);
await createFolderForPagination(4, 1003);
const r1 = await api.route(RequestMethod.GET, 'folders', {
const baseQuery = {
fields: ['id', 'title', 'updated_time'],
limit: 2,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
});
};
const r1 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery });
expect(r1.items.length).toBe(2);
expect(r1.items[0].title).toBe('folder1');
expect(['folder2', 'folder3'].includes(r1.items[1].title)).toBe(true);
const r2 = await api.route(RequestMethod.GET, 'folders', {
cursor: r1.cursor,
});
const r2 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 2 });
expect(r2.items.length).toBe(2);
expect(r2.items[0].title).toBe(r1.items[1].title === 'folder2' ? 'folder3' : 'folder2');
expect(r2.items[1].title).toBe('folder4');
}));
it('should paginate results and return the requested fields only', asyncTest(async () => {
await createNoteForPagination(1, 1001);
await createNoteForPagination(2, 1002);
await createNoteForPagination(3, 1003);
const baseQuery = {
fields: ['id', 'title', 'body'],
limit: 2,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
};
const r1 = await api.route(RequestMethod.GET, 'notes', { ...baseQuery });
expect(Object.keys(r1.items[0]).sort().join(',')).toBe('body,id,title');
expect(r1.items.length).toBe(2);
expect(r1.items[0].title).toBe('note1');
expect(r1.items[0].body).toBe('noteBody1');
expect(r1.items[1].title).toBe('note2');
expect(r1.items[1].body).toBe('noteBody2');
const r2 = await api.route(RequestMethod.GET, 'notes', { ...baseQuery, fields: ['id'], page: 2 });
expect(Object.keys(r2.items[0]).sort().join(',')).toBe('id');
expect(r2.items.length).toBe(1);
expect(!!r2.items[0].id).toBe(true);
}));
it('should paginate folder notes', asyncTest(async () => {
const folder = await Folder.save({});
const note1 = await Note.save({ parent_id: folder.id });
@@ -606,7 +635,8 @@ describe('services_rest_Api', function() {
expect(r1.items[1].id).toBe(note2.id);
const r2 = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`, {
cursor: r1.cursor,
limit: 2,
page: 2,
});
expect(r2.items.length).toBe(1);
@@ -666,6 +696,44 @@ describe('services_rest_Api', function() {
const r = await api.route(RequestMethod.GET, `resources/${resource.id}/notes`);
expect(r.items.length).toBe(1);
expect(r.items[0]).toBe(note.id);
expect(r.items[0].id).toBe(note.id);
}));
it('should return the resources associated with a note', asyncTest(async () => {
const note = await Note.save({});
await shim.attachFileToNote(note, `${__dirname}/../tests/support/photo.jpg`);
const resource = (await Resource.all())[0];
const r = await api.route(RequestMethod.GET, `notes/${note.id}/resources`);
expect(r.items.length).toBe(1);
expect(r.items[0].id).toBe(resource.id);
}));
it('should return search results', asyncTest(async () => {
SearchEngine.instance().setDb(db());
for (let i = 0; i < 10; i++) {
await Note.save({ title: 'a' });
}
await SearchEngine.instance().syncTables();
// Mostly testing pagination below
const r1 = await api.route(RequestMethod.GET, 'search', { query: 'a', limit: 4 });
expect(r1.items.length).toBe(4);
expect(r1.has_more).toBe(true);
const r2 = await api.route(RequestMethod.GET, 'search', { query: 'a', limit: 4, page: 2 });
expect(r2.items.length).toBe(4);
expect(r2.has_more).toBe(true);
const r3 = await api.route(RequestMethod.GET, 'search', { query: 'a', limit: 4, page: 3 });
expect(r3.items.length).toBe(2);
expect(!!r3.has_more).toBe(false);
await SearchEngine.instance().destroy();
}));
});

View File

@@ -1,11 +0,0 @@
{
"spec_dir": "tests",
"spec_files": [
"*.js",
"services/plugins/*.js",
"services/plugins/api/*.js",
"!test-utils.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}

View File

@@ -1,6 +1,6 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from 'lib/Logger';
import Logger from '../../../Logger';
/**
* @ignore
*/

View File

@@ -7,9 +7,21 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from 'lib/Logger';
import Logger from '../../../Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*
* **This is a beta API**
*
* Please note that the plugin API is relatively new and should be considered Beta state. Besides possible bugs, what it means is that there might be necessary breaking changes from one version to the next. Whenever such change is needed, best effort will be done to:
*
* - Maintain backward compatibility;
* - When possible, deprecate features instead of removing them;
* - Document breaking changes in the changelog;
*
* So if you are developing a plugin, please keep an eye on the changelog as everything will be in there with information about how to update your code. There won't be any major API rewrite or architecture changes, but possibly small tweaks like function signature change, type change, etc.
*
* Eventually, the plugin API will be versioned to make this process smoother.
*/
export default class Joplin {
private data_;

View File

@@ -1,25 +1,35 @@
import { Command } from './types';
/**
* This class allows executing or registering new Joplin commands. Commands can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or {@link JoplinViewsMenuItems | menu items}.
* This class allows executing or registering new Joplin commands. Commands
* can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or
* {@link JoplinViewsMenuItems | menu items}.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*
* ## Executing Joplin's internal commands
*
* It is also possible to execute internal Joplin's commands which, as of now, are not well documented.
* You can find the list directly on GitHub though at the following locations:
* It is also possible to execute internal Joplin's commands which, as of
* now, are not well documented. You can find the list directly on GitHub
* though at the following locations:
*
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/MainScreen/commands
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/commands
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.ts
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts)
*
* To view what arguments are supported, you can open any of these files and look at the `execute()` command.
* To view what arguments are supported, you can open any of these files
* and look at the `execute()` command.
*/
export default class JoplinCommands {
/**
* <span class="platform-desktop">desktop</span> Executes the given command.
* The `props` are the arguments passed to the command, and they vary based on the command
* <span class="platform-desktop">desktop</span> Executes the given
* command.
*
* The command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
*
* ```typescript
* // Create a new note in the current notebook:

View File

@@ -6,7 +6,7 @@ import { Path } from './types';
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/simple)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
*
* In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls.
* And each method takes these parameters:

View File

@@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.

View File

@@ -1,5 +1,5 @@
import Plugin from '../Plugin';
import Logger from 'lib/Logger';
import Logger from '../../../Logger';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
@@ -28,7 +28,7 @@ export default class JoplinPlugins {
* Note that registering a content script in itself will do nothing - it will only be loaded in specific cases by the relevant app modules
* (eg. the Markdown renderer or the code editor). So it is not a way to inject and run arbitrary code in the app, which for safety and performance reasons is not supported.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/content_script)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.

View File

@@ -7,7 +7,7 @@ import { SettingItem, SettingSection } from './types';
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/settings)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
@@ -37,7 +37,7 @@ export default class JoplinSettings {
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/ReactNativeClient/lib/models/Setting.ts#L142
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
}

View File

@@ -1,11 +1,12 @@
import Plugin from '../Plugin';
import { ButtonSpec, ViewHandle, ButtonId } from './types';
import { ButtonSpec, ViewHandle, DialogResult } from './types';
/**
* Allows creating and managing dialogs. A dialog is modal window that contains a webview and a row of buttons. You can update the update the webview using the `setHtml` method.
* Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return and provide the button ID that was
* clicked on. There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.
* Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return an object indicating what button was clicked
* on. If your HTML content included one or more form, a `formData` object will also be included with the key/value for each form.
* There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/dialog)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
@@ -16,7 +17,7 @@ export default class JoplinViewsDialogs {
/**
* Creates a new dialog
*/
create(): Promise<ViewHandle>;
create(id: string): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/
@@ -32,5 +33,5 @@ export default class JoplinViewsDialogs {
/**
* Opens the dialog
*/
open(handle: ViewHandle): Promise<ButtonId>;
open(handle: ViewHandle): Promise<DialogResult>;
}

View File

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

View File

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

View File

@@ -1,10 +1,13 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example
* it could be used to display a table of content for the active note, or display various metadata or graph.
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/toc)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
@@ -14,7 +17,7 @@ export default class JoplinViewsPanels {
/**
* Creates a new panel
*/
create(): Promise<ViewHandle>;
create(id: string): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/

View File

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

View File

@@ -2,7 +2,7 @@
* The workspace service provides access to all the parts of Joplin that are being worked on - i.e. the currently selected notes or notebooks as well
* as various related events, such as when a new note is selected, or when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;

View File

@@ -6,7 +6,7 @@ export interface Command {
/**
* Name of command - must be globally unique
*/
name: string
name: string;
/**
* Label to be displayed on menu items or keyboard shortcut editor for example.
@@ -14,17 +14,17 @@ export interface Command {
* In that case the command will not appear in the shortcut editor or command panel, and logically
* should not be used as a menu item.
*/
label?: string
label?: string;
/**
* Icon to be used on toolbar buttons for example
*/
iconName?: string,
iconName?: string;
/**
* Code to be ran when the command is executed. It may return a result.
*/
execute(...args:any[]):Promise<any | void>
execute(...args: any[]): Promise<any | void>;
/**
* Defines whether the command should be enabled or disabled, which in turns affects
@@ -40,13 +40,11 @@ export interface Command {
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
* And | && | "oneNoteSelected && !inConflictFolder"
*
* Currently the supported context variables aren't documented, but you can find the list there:
*
* https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/services/commands/stateToWhenClauseContext.ts
* Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/services/commands/stateToWhenClauseContext.ts).
*
* Note: Commands are enabled by default unless you use this property.
*/
enabledCondition?: string
enabledCondition?: string;
}
// =================================================================
@@ -64,7 +62,7 @@ export enum ImportModuleOutputFormat {
}
/**
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export) for an example.
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
*
* In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
*
@@ -74,113 +72,113 @@ export interface ExportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string,
format: string;
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string,
description: string;
/**
* Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
*/
target: FileSystemItem,
target: FileSystemItem;
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean,
isNoteArchive: boolean;
/**
* The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
*/
fileExtensions?: string[],
fileExtensions?: string[];
/**
* Called when the export process starts.
*/
onInit(context:ExportContext): Promise<void>;
onInit(context: ExportContext): Promise<void>;
/**
* Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
*/
onProcessItem(context:ExportContext, itemType:number, item:any):Promise<void>;
onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;
/**
* Called when a resource file needs to be exported.
*/
onProcessResource(context:ExportContext, resource:any, filePath:string):Promise<void>;
onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;
/**
* Called when the export process is done.
*/
onClose(context:ExportContext):Promise<void>;
onClose(context: ExportContext): Promise<void>;
}
export interface ImportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string,
format: string;
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string,
description: string;
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean,
isNoteArchive: boolean;
/**
* The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
*/
sources: FileSystemItem[],
sources: FileSystemItem[];
/**
* Tells the file extensions of the exported files.
*/
fileExtensions?: string[],
fileExtensions?: string[];
/**
* Tells the type of notes that will be generated, either HTML or Markdown (default).
*/
outputFormat?: ImportModuleOutputFormat,
outputFormat?: ImportModuleOutputFormat;
/**
* Called when the import process starts. There is only one event handler within which you should import the complete data.
*/
onExec(context:ImportContext): Promise<void>;
onExec(context: ImportContext): Promise<void>;
}
export interface ExportOptions {
format?: string,
path?:string,
sourceFolderIds?: string[],
sourceNoteIds?: string[],
modulePath?:string,
target?:FileSystemItem,
format?: string;
path?: string;
sourceFolderIds?: string[];
sourceNoteIds?: string[];
modulePath?: string;
target?: FileSystemItem;
}
export interface ExportContext {
destPath: string,
options: ExportOptions,
destPath: string;
options: ExportOptions;
/**
* You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next.
*/
userData?: any,
userData?: any;
}
export interface ImportContext {
sourcePath: string,
options: any,
warnings: string[],
sourcePath: string;
options: any;
warnings: string[];
}
// =================================================================
@@ -188,7 +186,7 @@ export interface ImportContext {
// =================================================================
export interface Script {
onStart?(event:any):Promise<void>,
onStart?(event: any): Promise<void>;
}
// =================================================================
@@ -196,7 +194,7 @@ export interface Script {
// =================================================================
export interface CreateMenuItemOptions {
accelerator: string,
accelerator: string;
}
export enum MenuItemLocation {
@@ -206,7 +204,12 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/*
* Deprecated - do not use - same as NoteListContext
*/
Context = 'context',
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
}
export interface MenuItem {
@@ -214,22 +217,22 @@ export interface MenuItem {
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string,
commandName?: string;
/**
* Accelerator associated with the menu item
*/
accelerator?: string,
accelerator?: string;
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[],
submenu?: MenuItem[];
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string,
label?: string;
}
// =================================================================
@@ -237,9 +240,9 @@ export interface MenuItem {
// =================================================================
export interface ButtonSpec {
id: ButtonId,
title?: string,
onClick?():void,
id: ButtonId;
title?: string;
onClick?(): void;
}
export type ButtonId = string;
@@ -263,6 +266,11 @@ export interface EditorCommand {
value?: any;
}
export interface DialogResult {
id: ButtonId;
formData?: any;
}
// =================================================================
// Settings types
// =================================================================
@@ -279,28 +287,28 @@ export enum SettingItemType {
// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
value: any,
type: SettingItemType,
public: boolean,
label:string,
value: any;
type: SettingItemType;
public: boolean;
label: string;
description?:string,
isEnum?: boolean,
section?: string,
options?:any,
appTypes?:string[],
secure?: boolean,
advanced?: boolean,
minimum?: number,
maximum?: number,
step?: number,
description?: string;
isEnum?: boolean;
section?: string;
options?: any;
appTypes?: string[];
secure?: boolean;
advanced?: boolean;
minimum?: number;
maximum?: number;
step?: number;
}
export interface SettingSection {
label: string,
iconName?: string,
description?: string,
name?: string,
label: string;
iconName?: string;
description?: string;
name?: string;
}
// =================================================================
@@ -322,36 +330,30 @@ export type Path = string[];
export enum ContentScriptType {
/**
* Registers a new Markdown-It plugin, which should follow this template:
* Registers a new Markdown-It plugin, which should follow the template below.
*
* ```javascript
* // The module should export an object as below:
*
* module.exports = {
*
* // The "context" variable is currently unused but could be used later on to provide
* // access to your own plugin so that the content script and plugin can communicate.
* default: function(context) {
* return {
*
* // This is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information
* // The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml.ts), which
* // contains a number of options, mostly useful for Joplin's internal code.
* plugin: function(markdownIt, options) {
* // ...
* },
*
* // You may also specify additional assets such as JS or CSS that should be loaded in the rendered HTML document.
* // Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to
* // see how the data should be structured.
* assets: {},
* assets: {
* // ...
* },
* }
* }
* }
* ```
*
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific feature, you
* would simply create a file such as this:
* - The `context` argument is currently unused but could be used later on to provide access to your own plugin so that the content script and plugin can communicate.
*
* - The **required** `plugin` key is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information. The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml.ts), which contains a number of options, mostly useful for Joplin's internal code.
*
* - Using the **optional** `assets` key you may specify assets such as JS or CSS that should be loaded in the rendered HTML document. Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to see how the data should be structured.
*
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific features, you would simply create a file such as this:
*
* ```javascript
* module.exports = {

View File

@@ -1,6 +1,6 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from 'lib/Logger';
import Logger from '../../../Logger';
/**
* @ignore
*/

View File

@@ -7,9 +7,21 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from 'lib/Logger';
import Logger from '../../../Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*
* **This is a beta API**
*
* Please note that the plugin API is relatively new and should be considered Beta state. Besides possible bugs, what it means is that there might be necessary breaking changes from one version to the next. Whenever such change is needed, best effort will be done to:
*
* - Maintain backward compatibility;
* - When possible, deprecate features instead of removing them;
* - Document breaking changes in the changelog;
*
* So if you are developing a plugin, please keep an eye on the changelog as everything will be in there with information about how to update your code. There won't be any major API rewrite or architecture changes, but possibly small tweaks like function signature change, type change, etc.
*
* Eventually, the plugin API will be versioned to make this process smoother.
*/
export default class Joplin {
private data_;

View File

@@ -1,25 +1,35 @@
import { Command } from './types';
/**
* This class allows executing or registering new Joplin commands. Commands can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or {@link JoplinViewsMenuItems | menu items}.
* This class allows executing or registering new Joplin commands. Commands
* can be executed or associated with
* {@link JoplinViewsToolbarButtons | toolbar buttons} or
* {@link JoplinViewsMenuItems | menu items}.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
*
* ## Executing Joplin's internal commands
*
* It is also possible to execute internal Joplin's commands which, as of now, are not well documented.
* You can find the list directly on GitHub though at the following locations:
* It is also possible to execute internal Joplin's commands which, as of
* now, are not well documented. You can find the list directly on GitHub
* though at the following locations:
*
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/MainScreen/commands
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/commands
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.ts
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts)
*
* To view what arguments are supported, you can open any of these files and look at the `execute()` command.
* To view what arguments are supported, you can open any of these files
* and look at the `execute()` command.
*/
export default class JoplinCommands {
/**
* <span class="platform-desktop">desktop</span> Executes the given command.
* The `props` are the arguments passed to the command, and they vary based on the command
* <span class="platform-desktop">desktop</span> Executes the given
* command.
*
* The command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
*
* ```typescript
* // Create a new note in the current notebook:

View File

@@ -6,7 +6,7 @@ import { Path } from './types';
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/simple)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
*
* In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls.
* And each method takes these parameters:

View File

@@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.

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