You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-02-01 07:49:31 +02:00
Compare commits
36 Commits
rn_63
...
command_en
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3b708ff64 | ||
|
|
fea85b39a0 | ||
|
|
a53955a03d | ||
|
|
8d61514f64 | ||
|
|
c8f496b733 | ||
|
|
c4414e3bee | ||
|
|
89e7a3a65e | ||
|
|
b3bce34e9a | ||
|
|
d059aeebab | ||
|
|
910a5ad09d | ||
|
|
259dadb650 | ||
|
|
4ce2bf492b | ||
|
|
e635ee967a | ||
|
|
6a43f3be66 | ||
|
|
a4562a0a75 | ||
|
|
410a0f5d14 | ||
|
|
f8f46db910 | ||
|
|
f529adac99 | ||
|
|
3a52b9764b | ||
|
|
7e78d7716b | ||
|
|
0488c5c4cb | ||
|
|
921cc3b6c6 | ||
|
|
2a7aa28d4c | ||
|
|
7b3440ac4c | ||
|
|
958e5a80b7 | ||
|
|
3179117c62 | ||
|
|
d895463167 | ||
|
|
7e0e513051 | ||
|
|
4309c4f8e2 | ||
|
|
20b1c90574 | ||
|
|
7ff6ceb585 | ||
|
|
3dc3f334ec | ||
|
|
3476211f8b | ||
|
|
48e299916f | ||
|
|
35aebc9f52 | ||
|
|
cb3e1cf1e9 |
@@ -114,7 +114,7 @@ ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleEditors.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
@@ -187,7 +187,9 @@ ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/InteropServiceHelper.js
|
||||
ElectronClient/plugins/GotoAnything.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/commands/types.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
@@ -235,9 +237,10 @@ ReactNativeClient/lib/services/AlarmServiceDriver.android.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
|
||||
ReactNativeClient/lib/services/BaseService.js
|
||||
ReactNativeClient/lib/services/BooleanExpression.js
|
||||
ReactNativeClient/lib/services/commands/commandsToMarkdownTable.js
|
||||
ReactNativeClient/lib/services/commands/MenuUtils.js
|
||||
ReactNativeClient/lib/services/commands/propsHaveChanged.js
|
||||
ReactNativeClient/lib/services/commands/stateToWhenClauseContext.js
|
||||
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/contextkey/contextkey.js
|
||||
@@ -309,6 +312,7 @@ ReactNativeClient/lib/services/synchronizer/migrations/1.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/2.js
|
||||
ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/services/WhenClause.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/shim.js
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -108,7 +108,7 @@ ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleEditors.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
@@ -181,7 +181,9 @@ ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/InteropServiceHelper.js
|
||||
ElectronClient/plugins/GotoAnything.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/commands/types.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
@@ -229,9 +231,10 @@ ReactNativeClient/lib/services/AlarmServiceDriver.android.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
|
||||
ReactNativeClient/lib/services/BaseService.js
|
||||
ReactNativeClient/lib/services/BooleanExpression.js
|
||||
ReactNativeClient/lib/services/commands/commandsToMarkdownTable.js
|
||||
ReactNativeClient/lib/services/commands/MenuUtils.js
|
||||
ReactNativeClient/lib/services/commands/propsHaveChanged.js
|
||||
ReactNativeClient/lib/services/commands/stateToWhenClauseContext.js
|
||||
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/contextkey/contextkey.js
|
||||
@@ -303,6 +306,7 @@ ReactNativeClient/lib/services/synchronizer/migrations/1.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/2.js
|
||||
ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/services/WhenClause.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/shim.js
|
||||
|
||||
277
.ignore
Normal file
277
.ignore
Normal file
@@ -0,0 +1,277 @@
|
||||
# This is used by VSCode to ignore patterns during search.
|
||||
# Before they were in joplin.code-workspace, under the `files.exclude` key
|
||||
# but it eventually reached the limit with ENAMETOOLONG error.
|
||||
#
|
||||
# https://github.com/microsoft/vscode/issues/94718
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/app/services/plugins/PluginRunner.js
|
||||
CliClient/tests/InMemoryCache.js
|
||||
CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services_rest_Api.js
|
||||
CliClient/tests/services/plugins/api/JoplinSetting.js
|
||||
CliClient/tests/services/plugins/sandboxProxy.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/app.js
|
||||
ElectronClient/bridge.js
|
||||
ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/commands/toggleExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
ElectronClient/gui/ConfigScreen/ConfigScreen.js
|
||||
ElectronClient/gui/ConfigScreen/SideBar.js
|
||||
ElectronClient/gui/DropboxLoginScreen.js
|
||||
ElectronClient/gui/ErrorBoundary.js
|
||||
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
|
||||
ElectronClient/gui/KeymapConfig/styles/index.js
|
||||
ElectronClient/gui/KeymapConfig/utils/getLabel.js
|
||||
ElectronClient/gui/KeymapConfig/utils/useCommandStatus.js
|
||||
ElectronClient/gui/KeymapConfig/utils/useKeymap.js
|
||||
ElectronClient/gui/MainScreen/commands/editAlarm.js
|
||||
ElectronClient/gui/MainScreen/commands/exportPdf.js
|
||||
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/renameTag.js
|
||||
ElectronClient/gui/MainScreen/commands/search.js
|
||||
ElectronClient/gui/MainScreen/commands/selectTemplate.js
|
||||
ElectronClient/gui/MainScreen/commands/setTags.js
|
||||
ElectronClient/gui/MainScreen/commands/showModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleEditors.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSideBar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
ElectronClient/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
ElectronClient/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
ElectronClient/gui/NoteEditor/commands/showLocalSearch.js
|
||||
ElectronClient/gui/NoteEditor/commands/showRevisions.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
ElectronClient/gui/NoteEditor/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/contextMenu.js
|
||||
ElectronClient/gui/NoteEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
|
||||
ElectronClient/gui/NoteEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFolder.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
ElectronClient/gui/NoteList/NoteList.js
|
||||
ElectronClient/gui/NoteListControls/commands/focusSearch.js
|
||||
ElectronClient/gui/NoteListControls/NoteListControls.js
|
||||
ElectronClient/gui/NoteListItem.js
|
||||
ElectronClient/gui/NoteTextViewer.js
|
||||
ElectronClient/gui/NoteToolbar/NoteToolbar.js
|
||||
ElectronClient/gui/OneDriveLoginScreen.js
|
||||
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js
|
||||
ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
|
||||
ElectronClient/gui/ResizableLayout/ResizableLayout.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/Root.js
|
||||
ElectronClient/gui/SearchBar/hooks/useSearch.js
|
||||
ElectronClient/gui/SearchBar/SearchBar.js
|
||||
ElectronClient/gui/SearchBar/styles/index.js
|
||||
ElectronClient/gui/ShareNoteDialog.js
|
||||
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
|
||||
ElectronClient/gui/SideBar/SideBar.js
|
||||
ElectronClient/gui/SideBar/styles/index.js
|
||||
ElectronClient/gui/StatusScreen/StatusScreen.js
|
||||
ElectronClient/gui/style/StyledInput.js
|
||||
ElectronClient/gui/style/StyledTextInput.js
|
||||
ElectronClient/gui/ToggleEditorsButton/styles/index.js
|
||||
ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
|
||||
ElectronClient/gui/ToolbarBase.js
|
||||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/InteropServiceHelper.js
|
||||
ElectronClient/plugins/GotoAnything.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/commands/types.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/BaseApplication.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
ReactNativeClient/lib/commands/synchronize.js
|
||||
ReactNativeClient/lib/components/BackButtonDialogBox.js
|
||||
ReactNativeClient/lib/components/CameraView.js
|
||||
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnMessage.js
|
||||
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js
|
||||
ReactNativeClient/lib/components/NoteBodyViewer/hooks/useSource.js
|
||||
ReactNativeClient/lib/components/NoteBodyViewer/NoteBodyViewer.js
|
||||
ReactNativeClient/lib/components/screens/Note.js
|
||||
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
|
||||
ReactNativeClient/lib/components/SelectDateTimeDialog.js
|
||||
ReactNativeClient/lib/errorUtils.js
|
||||
ReactNativeClient/lib/eventManager.js
|
||||
ReactNativeClient/lib/hooks/useEffectDebugger.js
|
||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||
ReactNativeClient/lib/hooks/usePrevious.js
|
||||
ReactNativeClient/lib/hooks/usePropsDebugger.js
|
||||
ReactNativeClient/lib/InMemoryCache.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/locale.js
|
||||
ReactNativeClient/lib/Logger.js
|
||||
ReactNativeClient/lib/markdownUtils.js
|
||||
ReactNativeClient/lib/models/Alarm.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/PoorManIntervals.js
|
||||
ReactNativeClient/lib/reducer.js
|
||||
ReactNativeClient/lib/services/AlarmService.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
|
||||
ReactNativeClient/lib/services/BaseService.js
|
||||
ReactNativeClient/lib/services/commands/commandsToMarkdownTable.js
|
||||
ReactNativeClient/lib/services/commands/MenuUtils.js
|
||||
ReactNativeClient/lib/services/commands/propsHaveChanged.js
|
||||
ReactNativeClient/lib/services/commands/stateToWhenClauseContext.js
|
||||
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/contextkey/contextkey.js
|
||||
ReactNativeClient/lib/services/debug/populateDatabase.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Base.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Custom.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Html.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Md.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Raw.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Base.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Custom.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToHtml.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToMd.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Jex.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Md.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Raw.js
|
||||
ReactNativeClient/lib/services/interop/InteropService.js
|
||||
ReactNativeClient/lib/services/interop/types.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/KeymapService.js
|
||||
ReactNativeClient/lib/services/plugins/api/Global.js
|
||||
ReactNativeClient/lib/services/plugins/api/Joplin.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinCommands.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinData.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinFilters.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinInterop.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinPlugins.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinSettings.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViews.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsDialogs.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsMenuItems.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsMenus.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsPanels.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsToolbarButtons.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js
|
||||
ReactNativeClient/lib/services/plugins/api/types.js
|
||||
ReactNativeClient/lib/services/plugins/BasePluginRunner.js
|
||||
ReactNativeClient/lib/services/plugins/MenuController.js
|
||||
ReactNativeClient/lib/services/plugins/MenuItemController.js
|
||||
ReactNativeClient/lib/services/plugins/Plugin.js
|
||||
ReactNativeClient/lib/services/plugins/PluginService.js
|
||||
ReactNativeClient/lib/services/plugins/reducer.js
|
||||
ReactNativeClient/lib/services/plugins/sandboxProxy.js
|
||||
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
|
||||
ReactNativeClient/lib/services/plugins/utils/createViewHandle.js
|
||||
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
|
||||
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
|
||||
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
ReactNativeClient/lib/services/plugins/utils/types.js
|
||||
ReactNativeClient/lib/services/plugins/ViewController.js
|
||||
ReactNativeClient/lib/services/plugins/WebviewController.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/Api.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
ReactNativeClient/lib/services/SettingUtils.js
|
||||
ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js
|
||||
ReactNativeClient/lib/services/synchronizer/LockHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/MigrationHandler.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/1.js
|
||||
ReactNativeClient/lib/services/synchronizer/migrations/2.js
|
||||
ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/services/WhenClause.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/shim.js
|
||||
ReactNativeClient/lib/Synchronizer.js
|
||||
ReactNativeClient/lib/theme.js
|
||||
ReactNativeClient/lib/themes/aritimDark.js
|
||||
ReactNativeClient/lib/themes/dark.js
|
||||
ReactNativeClient/lib/themes/dracula.js
|
||||
ReactNativeClient/lib/themes/light.js
|
||||
ReactNativeClient/lib/themes/nord.js
|
||||
ReactNativeClient/lib/themes/oledDark.js
|
||||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/uuid.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
28
CliClient/package-lock.json
generated
28
CliClient/package-lock.json
generated
@@ -5895,11 +5895,6 @@
|
||||
"is-fullwidth-code-point": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"slug": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/slug/-/slug-3.5.0.tgz",
|
||||
"integrity": "sha512-+pZLDhMtmAc+ZcojQSMlUKDZBYmvhZiZmK8Ffx/D3Q/MIMHPDBAMbWvWN8vJb9xl2MfbDdRWxFzrdOhBiyVpow=="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
@@ -6762,6 +6757,11 @@
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
||||
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
|
||||
},
|
||||
"unorm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
|
||||
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
|
||||
},
|
||||
"unpack-string": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unpack-string/-/unpack-string-0.0.2.tgz",
|
||||
@@ -6864,6 +6864,24 @@
|
||||
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
|
||||
},
|
||||
"uslug": {
|
||||
"version": "git+https://github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
|
||||
"from": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"requires": {
|
||||
"node-emoji": "^1.10.0",
|
||||
"unorm": ">= 1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-emoji": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
|
||||
"integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
|
||||
"requires": {
|
||||
"lodash.toarray": "^4.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
"sax": "^1.2.4",
|
||||
"server-destroy": "^1.0.1",
|
||||
"sharp": "^0.23.2",
|
||||
"slug": "^3.5.0",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"sqlite3": "^4.1.1",
|
||||
"string-padding": "^1.0.2",
|
||||
@@ -113,6 +112,7 @@
|
||||
"terminal-kit": "^1.30.0",
|
||||
"tkwidgets": "^0.5.26",
|
||||
"url-parse": "^1.4.7",
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"uuid": "^3.0.1",
|
||||
"valid-url": "^1.0.9",
|
||||
"word-wrap": "^1.2.3",
|
||||
|
||||
@@ -2,7 +2,7 @@ import MenuUtils from 'lib/services/commands/MenuUtils';
|
||||
import ToolbarButtonUtils from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('test-utils.js');
|
||||
|
||||
interface TestCommand {
|
||||
declaration: CommandDeclaration,
|
||||
@@ -11,7 +11,12 @@ interface TestCommand {
|
||||
|
||||
function newService():CommandService {
|
||||
const service = new CommandService();
|
||||
service.initialize({});
|
||||
const mockStore = {
|
||||
getState: () => {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
service.initialize(mockStore, true);
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -24,8 +29,7 @@ function createCommand(name:string, options:any):TestCommand {
|
||||
execute: options.execute,
|
||||
};
|
||||
|
||||
if (options.mapStateToProps) runtime.mapStateToProps = options.mapStateToProps;
|
||||
if (options.isEnabled) runtime.isEnabled = options.isEnabled;
|
||||
if (options.enabledCondition) runtime.enabledCondition = options.enabledCondition;
|
||||
|
||||
return { declaration, runtime };
|
||||
}
|
||||
@@ -61,7 +65,7 @@ describe('services_CommandService', function() {
|
||||
},
|
||||
}));
|
||||
|
||||
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons({}, ['test1', 'test2']);
|
||||
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons(['test1', 'test2'], {});
|
||||
|
||||
await toolbarInfos[0].onClick();
|
||||
await toolbarInfos[1].onClick();
|
||||
@@ -77,98 +81,78 @@ describe('services_CommandService', function() {
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedNoteId === 'abc';
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedNoteId === '123';
|
||||
},
|
||||
enabledCondition: 'multipleNotesSelected',
|
||||
}));
|
||||
|
||||
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: '123',
|
||||
selectedFolderId: 'aaa',
|
||||
}, ['test1', 'test2']);
|
||||
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons(['test1', 'test2'], {
|
||||
oneNoteSelected: false,
|
||||
multipleNotesSelected: true,
|
||||
});
|
||||
|
||||
expect(toolbarInfos[0].enabled).toBe(false);
|
||||
expect(toolbarInfos[1].enabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('should enable commands by default', asyncTest(async () => {
|
||||
const service = newService();
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
}));
|
||||
|
||||
expect(service.isEnabled('test1', {})).toBe(true);
|
||||
}));
|
||||
|
||||
it('should return the same toolbarButtons array if nothing has changed', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(service);
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedNoteId === 'ok';
|
||||
},
|
||||
enabledCondition: 'cond1',
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedFolderId === 'ok';
|
||||
},
|
||||
enabledCondition: 'cond2',
|
||||
}));
|
||||
|
||||
const toolbarInfos1 = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', 'test2']);
|
||||
const toolbarInfos1 = toolbarButtonUtils.commandsToToolbarButtons(['test1', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: false,
|
||||
});
|
||||
|
||||
const toolbarInfos2 = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', 'test2']);
|
||||
const toolbarInfos2 = toolbarButtonUtils.commandsToToolbarButtons(['test1', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: false,
|
||||
});
|
||||
|
||||
expect(toolbarInfos1).toBe(toolbarInfos2);
|
||||
expect(toolbarInfos1[0] === toolbarInfos2[0]).toBe(true);
|
||||
expect(toolbarInfos1[1] === toolbarInfos2[1]).toBe(true);
|
||||
|
||||
const toolbarInfos3 = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'ok',
|
||||
}, ['test1', 'test2']);
|
||||
const toolbarInfos3 = toolbarButtonUtils.commandsToToolbarButtons(['test1', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: true,
|
||||
});
|
||||
|
||||
expect(toolbarInfos2 === toolbarInfos3).toBe(false);
|
||||
expect(toolbarInfos2[0] === toolbarInfos3[0]).toBe(true);
|
||||
expect(toolbarInfos2[1] === toolbarInfos3[1]).toBe(false);
|
||||
|
||||
{
|
||||
expect(toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', '-', 'test2'])).toBe(toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', '-', 'test2']));
|
||||
expect(toolbarButtonUtils.commandsToToolbarButtons(['test1', '-', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: false,
|
||||
})).toBe(toolbarButtonUtils.commandsToToolbarButtons(['test1', '-', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: false,
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -206,50 +190,37 @@ describe('services_CommandService', function() {
|
||||
const utils = new MenuUtils(service);
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
isOk: state.test1 === 'ok',
|
||||
};
|
||||
},
|
||||
execute: () => {},
|
||||
enabledCondition: 'cond1',
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
isOk: state.test2 === 'ok',
|
||||
};
|
||||
},
|
||||
execute: () => {},
|
||||
enabledCondition: 'cond2',
|
||||
}));
|
||||
|
||||
{
|
||||
const menuItemProps = utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'notok',
|
||||
}, ['test1', 'test2']);
|
||||
const menuItemProps = utils.commandsToMenuItemProps(['test1', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: false,
|
||||
});
|
||||
|
||||
expect(menuItemProps.test1.isOk).toBe(true);
|
||||
expect(menuItemProps.test2.isOk).toBe(false);
|
||||
expect(menuItemProps.test1.enabled).toBe(true);
|
||||
expect(menuItemProps.test2.enabled).toBe(false);
|
||||
}
|
||||
|
||||
{
|
||||
const menuItemProps = utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'ok',
|
||||
}, ['test1', 'test2']);
|
||||
const menuItemProps = utils.commandsToMenuItemProps(['test1', 'test2'], {
|
||||
cond1: true,
|
||||
cond2: true,
|
||||
});
|
||||
|
||||
expect(menuItemProps.test1.isOk).toBe(true);
|
||||
expect(menuItemProps.test2.isOk).toBe(true);
|
||||
expect(menuItemProps.test1.enabled).toBe(true);
|
||||
expect(menuItemProps.test2.enabled).toBe(true);
|
||||
}
|
||||
|
||||
expect(utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'ok',
|
||||
}, ['test1', 'test2'])).toBe(utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'ok',
|
||||
}, ['test1', 'test2']));
|
||||
expect(utils.commandsToMenuItemProps(['test1', 'test2'], { cond1: true, cond2: true }))
|
||||
.toBe(utils.commandsToMenuItemProps(['test1', 'test2'], { cond1: true, cond2: true }));
|
||||
}));
|
||||
|
||||
it('should create stateful menu items', asyncTest(async () => {
|
||||
@@ -259,20 +230,30 @@ describe('services_CommandService', function() {
|
||||
let propValue = null;
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
isOk: state.test1 === 'ok',
|
||||
};
|
||||
},
|
||||
execute: (props:any) => {
|
||||
propValue = props.isOk;
|
||||
execute: (_context:any, greeting:string) => {
|
||||
propValue = greeting;
|
||||
},
|
||||
}));
|
||||
|
||||
const menuItem = utils.commandToStatefulMenuItem('test1', { isOk: 'hello' });
|
||||
const menuItem = utils.commandToStatefulMenuItem('test1', 'hello');
|
||||
menuItem.click();
|
||||
|
||||
expect(propValue).toBe('hello');
|
||||
}));
|
||||
|
||||
it('should throw an error for invalid when clause keys in dev mode', asyncTest(async () => {
|
||||
const service = newService();
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
enabledCondition: 'cond1 && cond2',
|
||||
}));
|
||||
|
||||
await expectThrow(async () => service.isEnabled('test1', {}));
|
||||
await expectThrow(async () => service.isEnabled('test1', { cond1: true }));
|
||||
await expectNotThrow(async () => service.isEnabled('test1', { cond1: true, cond2: true }));
|
||||
await expectNotThrow(async () => service.isEnabled('test1', { cond1: true, cond2: false }));
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ joplin.plugins.register({
|
||||
newLines.push(newCells.join(' | '));
|
||||
}
|
||||
|
||||
await joplin.commands.execute('replaceSelection', { value: newLines.join('\n') });
|
||||
await joplin.commands.execute('replaceSelection', newLines.join('\n'));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -2420,6 +2420,11 @@
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
@@ -2741,6 +2746,14 @@
|
||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node-emoji": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
|
||||
"integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
|
||||
"requires": {
|
||||
"lodash.toarray": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node-libs-browser": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
|
||||
@@ -3429,11 +3442,6 @@
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"slug": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/slug/-/slug-3.3.4.tgz",
|
||||
"integrity": "sha512-VpHbtRCEWmgaZsrZcTsVl/Dhw98lcrOYDO17DNmJCNpppI6s3qJvnNu2Q3D4L84/2bi6vkW40mjNQI9oGQsflg=="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
@@ -3981,6 +3989,11 @@
|
||||
"imurmurhash": "^0.1.4"
|
||||
}
|
||||
},
|
||||
"unorm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
|
||||
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
|
||||
},
|
||||
"unset-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
||||
@@ -4067,6 +4080,14 @@
|
||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
||||
"dev": true
|
||||
},
|
||||
"uslug": {
|
||||
"version": "git+https://github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
|
||||
"from": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"requires": {
|
||||
"node-emoji": "^1.10.0",
|
||||
"unorm": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"util": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||
|
||||
@@ -20,6 +20,6 @@
|
||||
"webpack-cli": "^3.3.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"slug": "^3.3.4"
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import joplin from 'api';
|
||||
|
||||
const nodeSlug = require('slug');
|
||||
const uslug = require('uslug');
|
||||
|
||||
// From https://stackoverflow.com/a/6234804/561309
|
||||
function escapeHtml(unsafe:string) {
|
||||
@@ -29,7 +29,7 @@ function noteHeaders(noteBody:string) {
|
||||
let slugs:any = {};
|
||||
|
||||
function headerSlug(headerText:string) {
|
||||
const s = nodeSlug(headerText);
|
||||
const s = uslug(headerText);
|
||||
let num = slugs[s] ? slugs[s] : 1;
|
||||
const output = [s];
|
||||
if (num > 1) output.push(num);
|
||||
@@ -49,9 +49,7 @@ joplin.plugins.register({
|
||||
|
||||
panels.onMessage(view, (message:any) => {
|
||||
if (message.name === 'scrollToHash') {
|
||||
joplin.commands.execute('scrollToHash', {
|
||||
hash: message.hash,
|
||||
})
|
||||
joplin.commands.execute('scrollToHash', message.hash)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ export default class InteropServiceHelper {
|
||||
|
||||
if (Array.isArray(path)) path = path[0];
|
||||
|
||||
CommandService.instance().execute('showModalMessage', { message: _('Exporting to "%s" as "%s" format. Please wait...', path, module.format) });
|
||||
CommandService.instance().execute('showModalMessage', _('Exporting to "%s" as "%s" format. Please wait...', path, module.format));
|
||||
|
||||
const exportOptions:ExportOptions = {};
|
||||
exportOptions.path = path;
|
||||
|
||||
@@ -58,7 +58,7 @@ const commands = [
|
||||
require('./gui/MainScreen/commands/showNoteProperties'),
|
||||
require('./gui/MainScreen/commands/showShareNoteDialog'),
|
||||
require('./gui/MainScreen/commands/toggleNoteList'),
|
||||
require('./gui/MainScreen/commands/toggleSidebar'),
|
||||
require('./gui/MainScreen/commands/toggleSideBar'),
|
||||
require('./gui/MainScreen/commands/toggleVisiblePanes'),
|
||||
require('./gui/MainScreen/commands/toggleEditors'),
|
||||
require('./gui/NoteEditor/commands/focusElementNoteBody'),
|
||||
@@ -85,7 +85,7 @@ const globalCommands = [
|
||||
const editorCommandDeclarations = require('./gui/NoteEditor/commands/editorCommandDeclarations').default;
|
||||
|
||||
const pluginClasses = [
|
||||
require('./plugins/GotoAnything.min'),
|
||||
require('./plugins/GotoAnything').default,
|
||||
];
|
||||
|
||||
interface AppStateRoute {
|
||||
@@ -513,7 +513,7 @@ class Application extends BaseApplication {
|
||||
|
||||
this.initRedux();
|
||||
|
||||
CommandService.instance().initialize(this.store());
|
||||
CommandService.instance().initialize(this.store(), Setting.value('env') == 'dev');
|
||||
|
||||
for (const command of commands) {
|
||||
CommandService.instance().registerDeclaration(command.declaration);
|
||||
|
||||
@@ -6,7 +6,7 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ target }:any) => {
|
||||
execute: async (_context:any, target:string) => {
|
||||
if (target === 'noteBody') return CommandService.instance().execute('focusElementNoteBody');
|
||||
if (target === 'noteList') return CommandService.instance().execute('focusElementNoteList');
|
||||
if (target === 'sideBar') return CommandService.instance().execute('focusElementSideBar');
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
const Note = require('lib/models/Note');
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
interface Props {
|
||||
noteId: string
|
||||
}
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'startExternalEditing',
|
||||
label: () => _('Edit in external editor'),
|
||||
@@ -16,21 +13,16 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
try {
|
||||
const note = await Note.load(props.noteId);
|
||||
const note = await Note.load(noteId);
|
||||
ExternalEditWatcher.instance().openAndWatch(note);
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(_('Error opening note in editor: %s', error.message));
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null,
|
||||
};
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
|
||||
interface Props {
|
||||
noteId: string
|
||||
}
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'stopExternalEditing',
|
||||
label: () => _('Stop external editing'),
|
||||
@@ -14,14 +11,10 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
ExternalEditWatcher.instance().stopWatching(props.noteId);
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null };
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
ExternalEditWatcher.instance().stopWatching(noteId);
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../lib/services/CommandService';
|
||||
import CommandService, { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { AppState } from '../app';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
|
||||
interface Props {
|
||||
noteId: string
|
||||
noteIsBeingWatched: boolean
|
||||
}
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
import { DesktopCommandContext } from '../services/commands/types';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'toggleExternalEditing',
|
||||
@@ -16,27 +11,21 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
if (!props.noteId) return;
|
||||
execute: async (context:DesktopCommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
if (props.noteIsBeingWatched) {
|
||||
CommandService.instance().execute('stopExternalEditing', { noteId: props.noteId });
|
||||
if (!noteId) return;
|
||||
|
||||
if (context.state.watchedNoteFiles.includes(noteId)) {
|
||||
CommandService.instance().execute('stopExternalEditing', noteId);
|
||||
} else {
|
||||
CommandService.instance().execute('startExternalEditing', { noteId: props.noteId });
|
||||
CommandService.instance().execute('startExternalEditing', noteId);
|
||||
}
|
||||
},
|
||||
isEnabled: (props:Props) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:AppState):Props => {
|
||||
const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null;
|
||||
return {
|
||||
noteId: noteId,
|
||||
noteIsBeingWatched: noteId ? state.watchedNoteFiles.includes(noteId) : false,
|
||||
};
|
||||
},
|
||||
title: (props:Props) => {
|
||||
return props.noteIsBeingWatched ? _('Stop') : '';
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
mapStateToTitle: (state:any) => {
|
||||
const noteId = stateUtils.selectedNoteId(state);
|
||||
return state.watchedNoteFiles.includes(noteId) ? _('Stop') : '';
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -26,6 +26,8 @@ const getLabel = (commandName: string) => {
|
||||
return _('Hide Joplin');
|
||||
case 'closeWindow':
|
||||
return _('Close Window');
|
||||
case 'commandPalette':
|
||||
return _('Command palette');
|
||||
case 'config':
|
||||
return shim.isMac() ? _('Preferences') : _('Options');
|
||||
default:
|
||||
|
||||
@@ -61,7 +61,7 @@ const commands = [
|
||||
require('./commands/showShareNoteDialog'),
|
||||
require('./commands/toggleEditors'),
|
||||
require('./commands/toggleNoteList'),
|
||||
require('./commands/toggleSidebar'),
|
||||
require('./commands/toggleSideBar'),
|
||||
require('./commands/toggleVisiblePanes'),
|
||||
];
|
||||
|
||||
@@ -320,7 +320,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
||||
window.removeEventListener('resize', this.window_resize);
|
||||
}
|
||||
|
||||
toggleSidebar() {
|
||||
toggleSideBar() {
|
||||
this.props.dispatch({
|
||||
type: 'SIDEBAR_VISIBILITY_TOGGLE',
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import eventManager from 'lib/eventManager';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
const Note = require('lib/models/Note');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const { time } = require('lib/time-utils');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -13,7 +13,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteId }:any) => {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
const note = await Note.load(noteId);
|
||||
|
||||
const defaultDate = new Date(Date.now() + 2 * 3600 * 1000);
|
||||
@@ -51,25 +53,12 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
title: (props:any):string => {
|
||||
if (!props.noteId) return null;
|
||||
if (!props.noteTodoDue) return null;
|
||||
return time.formatMsToLocal(props.noteTodoDue);
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
if (!props.noteId) return false;
|
||||
return !!props.noteIsTodo && !props.noteTodoCompleted;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null;
|
||||
const note = noteId ? BaseModel.byId(state.notes, noteId) : null;
|
||||
|
||||
return {
|
||||
noteId: note ? noteId : null,
|
||||
noteIsTodo: note ? note.is_todo : false,
|
||||
noteTodoCompleted: note ? note.todo_completed : false,
|
||||
noteTodoDue: note ? note.todo_due : null,
|
||||
};
|
||||
enabledCondition: 'oneNoteSelected && noteIsTodo && !noteTodoCompleted',
|
||||
|
||||
mapStateToTitle: (state:any) => {
|
||||
const note = stateUtils.selectedNote(state);
|
||||
return note && note.todo_due ? time.formatMsToLocal(note.todo_due) : null;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import shim from 'lib/shim';
|
||||
import InteropServiceHelper from '../../../InteropServiceHelper';
|
||||
import { _ } from 'lib/locale';
|
||||
@@ -12,8 +12,10 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteIds }:any) => {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
try {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
if (!noteIds.length) throw new Error('No notes selected for pdf export');
|
||||
|
||||
let path = null;
|
||||
@@ -22,7 +24,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
filters: [{ name: _('PDF File'), extensions: ['pdf'] }],
|
||||
defaultPath: await InteropServiceHelper.defaultFilename(noteIds[0], 'pdf'),
|
||||
});
|
||||
|
||||
} else {
|
||||
path = bridge().showOpenDialog({
|
||||
properties: ['openDirectory', 'createDirectory'],
|
||||
@@ -50,13 +51,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return !!props.noteIds.length;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
noteIds: state.selectedNoteIds,
|
||||
};
|
||||
},
|
||||
|
||||
enabledCondition: 'someNotesSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'hideModalMessage',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const Folder = require('lib/models/Folder');
|
||||
const Note = require('lib/models/Note');
|
||||
@@ -10,7 +10,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteIds }:any) => {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
const folders:any[] = await Folder.sortFolderTree();
|
||||
const startFolders:any[] = [];
|
||||
const maxDepth = 15;
|
||||
@@ -42,13 +44,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return !!props.noteIds.length;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
noteIds: state.selectedNoteIds,
|
||||
};
|
||||
},
|
||||
enabledCondition: 'someNotesSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandContext, CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const Folder = require('lib/models/Folder');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
@@ -11,7 +11,7 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ parentId }:any) => {
|
||||
execute: async (_context:CommandContext, parentId:string = null) => {
|
||||
comp.setState({
|
||||
promptOptions: {
|
||||
label: _('Notebook title:'),
|
||||
@@ -39,8 +39,5 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
title: () => {
|
||||
return _('New notebook');
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { utils, CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { utils, CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const TemplateUtils = require('lib/TemplateUtils');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -13,7 +12,7 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ template, isTodo }:any) => {
|
||||
execute: async (_context:CommandContext, template:string = null, isTodo:boolean = false) => {
|
||||
const folderId = Setting.value('activeFolderId');
|
||||
if (!folderId) return;
|
||||
|
||||
@@ -34,12 +33,6 @@ export const runtime = ():CommandRuntime => {
|
||||
id: newNote.id,
|
||||
});
|
||||
},
|
||||
isEnabled: () => {
|
||||
const { folders, selectedFolderId } = utils.store.getState();
|
||||
return !!folders.length && selectedFolderId !== Folder.conflictFolderId();
|
||||
},
|
||||
title: () => {
|
||||
return _('New note');
|
||||
},
|
||||
enabledCondition: 'oneFolderSelected && !inConflictFolder',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CommandService, { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import CommandService, { CommandContext, CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -9,14 +9,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ template }:any) => {
|
||||
return CommandService.instance().execute('newNote', { template: template, isTodo: true });
|
||||
},
|
||||
isEnabled: () => {
|
||||
return CommandService.instance().isEnabled('newNote', {});
|
||||
},
|
||||
title: () => {
|
||||
return _('New to-do');
|
||||
execute: async (_context:CommandContext, template:string = null) => {
|
||||
return CommandService.instance().execute('newNote', template, true);
|
||||
},
|
||||
enabledCondition: 'oneFolderSelected && !inConflictFolder',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
|
||||
@@ -10,8 +10,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteIds }:any) => {
|
||||
// TODO: test
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
try {
|
||||
if (noteIds.length !== 1) throw new Error(_('Only one note can be printed at a time.'));
|
||||
await comp.printTo_('printer', { noteId: noteIds[0] });
|
||||
@@ -19,13 +20,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return !!props.noteIds.length;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
noteIds: state.selectedNoteIds,
|
||||
};
|
||||
},
|
||||
enabledCondition: 'someNotesSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const Folder = require('lib/models/Folder');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
@@ -10,7 +10,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ folderId }:any) => {
|
||||
execute: async (context:CommandContext, folderId:string = null) => {
|
||||
folderId = folderId || context.state.selectedFolderId;
|
||||
|
||||
const folder = await Folder.load(folderId);
|
||||
|
||||
if (folder) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const Tag = require('lib/models/Tag');
|
||||
const bridge = require('electron').remote.require('./bridge').default;
|
||||
@@ -10,7 +10,10 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ tagId }:any) => {
|
||||
execute: async (context:CommandContext, tagId:string = null) => {
|
||||
tagId = tagId || context.state.selectedTagId;
|
||||
if (!tagId) return;
|
||||
|
||||
const tag = await Tag.load(tagId);
|
||||
if (tag) {
|
||||
comp.setState({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const uuid = require('lib/uuid').default;
|
||||
|
||||
@@ -9,7 +9,7 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ query }:any) => {
|
||||
execute: async (_context:CommandContext, query:string) => {
|
||||
if (!comp.searchId_) comp.searchId_ = uuid.create();
|
||||
|
||||
comp.props.dispatch({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CommandService, { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import CommandService, { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const TemplateUtils = require('lib/TemplateUtils');
|
||||
|
||||
@@ -8,7 +8,7 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteType }:any) => {
|
||||
execute: async (_context:CommandContext, noteType:string) => {
|
||||
comp.setState({
|
||||
promptOptions: {
|
||||
label: _('Template file:'),
|
||||
@@ -18,9 +18,9 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
onClose: async (answer:any) => {
|
||||
if (answer) {
|
||||
if (noteType === 'note' || noteType === 'todo') {
|
||||
CommandService.instance().execute('newNote', { template: answer.value, isTodo: noteType === 'todo' });
|
||||
CommandService.instance().execute('newNote', answer.value, noteType === 'todo');
|
||||
} else {
|
||||
CommandService.instance().execute('insertText', { value: TemplateUtils.render(answer.value) });
|
||||
CommandService.instance().execute('insertText', TemplateUtils.render(answer.value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const Tag = require('lib/models/Tag');
|
||||
|
||||
@@ -10,7 +10,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteIds }:any) => {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
const tags = await Tag.commonTagsByNoteIds(noteIds);
|
||||
const startTags = tags
|
||||
.map((a:any) => {
|
||||
@@ -64,11 +66,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !!props.noteIds.length;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { noteIds: state.selectedNoteIds };
|
||||
},
|
||||
enabledCondition: 'someNotesSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandDeclaration, CommandRuntime, CommandContext } from 'lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'showModalMessage',
|
||||
@@ -7,7 +7,7 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ message }:any) => {
|
||||
execute: async (_context:CommandContext, message:string) => {
|
||||
comp.setState({
|
||||
modalLayer: {
|
||||
visible: true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
const Note = require('lib/models/Note');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -9,7 +10,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteId }:any) => {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
const note = await Note.load(noteId);
|
||||
if (note) {
|
||||
comp.setState({
|
||||
@@ -21,11 +24,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
});
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null };
|
||||
},
|
||||
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import CommandService, { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import CommandService, { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'showNoteProperties',
|
||||
@@ -9,7 +10,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteId }:any) => {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
comp.setState({
|
||||
notePropertiesDialogOptions: {
|
||||
noteId: noteId,
|
||||
@@ -20,11 +23,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
},
|
||||
});
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null };
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -8,7 +8,9 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteIds }:any) => {
|
||||
execute: async (context:CommandContext, noteIds:string[] = null) => {
|
||||
noteIds = noteIds || context.state.selectedNoteIds;
|
||||
|
||||
comp.setState({
|
||||
shareNoteDialogOptions: {
|
||||
noteIds: noteIds,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandDeclaration, CommandRuntime, CommandContext } from 'lib/services/CommandService';
|
||||
import Setting from 'lib/models/Setting';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
import { _ } from 'lib/locale';
|
||||
@@ -11,22 +11,14 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:any) => {
|
||||
execute: async (context:CommandContext) => {
|
||||
// A bit of a hack, but for now don't allow changing code view
|
||||
// while a note is being saved as it will cause a problem with
|
||||
// TinyMCE because it won't have time to send its content before
|
||||
// being switch to Ace Editor.
|
||||
if (props.hasNotesBeingSaved) return;
|
||||
if (stateUtils.hasNotesBeingSaved(context.state)) return;
|
||||
Setting.toggle('editor.codeView');
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return !props.hasNotesBeingSaved && props.hasOneSelectedNote;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
hasNotesBeingSaved: stateUtils.hasNotesBeingSaved(state),
|
||||
hasOneSelectedNote: state.selectedNoteIds.length === 1,
|
||||
};
|
||||
},
|
||||
enabledCondition: '!notesAreBeingSaved && oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'toggleSidebar',
|
||||
name: 'toggleSideBar',
|
||||
label: () => _('Toggle sidebar'),
|
||||
iconName: 'fas fa-bars',
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -14,14 +14,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
type: 'NOTE_VISIBLE_PANES_TOGGLE',
|
||||
});
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return props.settingEditorCodeView && props.selectedNoteIds.length === 1;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
settingEditorCodeView: state.settings['editor.codeView'],
|
||||
};
|
||||
},
|
||||
|
||||
enabledCondition: 'markdownEditorVisible && oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Module } from 'lib/services/interop/types';
|
||||
import InteropServiceHelper from '../InteropServiceHelper';
|
||||
import { _ } from 'lib/locale';
|
||||
import { MenuItem, MenuItemLocation } from 'lib/services/plugins/api/types';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { reg } = require('lib/registry.js');
|
||||
@@ -108,7 +109,7 @@ const commandNames:string[] = [
|
||||
'attachFile',
|
||||
'focusSearch',
|
||||
'showLocalSearch',
|
||||
'toggleSidebar',
|
||||
'toggleSideBar',
|
||||
'toggleNoteList',
|
||||
'toggleVisiblePanes',
|
||||
'toggleExternalEditing',
|
||||
@@ -157,12 +158,12 @@ function useMenu(props:Props) {
|
||||
|
||||
if (Array.isArray(path)) path = path[0];
|
||||
|
||||
CommandService.instance().execute('showModalMessage', { message: _('Importing from "%s" as "%s" format. Please wait...', path, module.format) });
|
||||
CommandService.instance().execute('showModalMessage', _('Importing from "%s" as "%s" format. Please wait...', path, module.format));
|
||||
|
||||
const importOptions = {
|
||||
path,
|
||||
format: module.format,
|
||||
modulePath: module.path,
|
||||
outputFormat: module.outputFormat,
|
||||
onError: console.warn,
|
||||
destinationFolderId: !module.isNoteArchive && moduleSource === 'file' ? props.selectedFolderId : null,
|
||||
};
|
||||
@@ -251,7 +252,7 @@ function useMenu(props:Props) {
|
||||
exportItems.push({
|
||||
label: module.fullLabel(),
|
||||
click: async () => {
|
||||
await InteropServiceHelper.export(props.dispatch.bind(this), module);
|
||||
await InteropServiceHelper.export((action:any) => props.dispatch(action), module);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -299,12 +300,12 @@ function useMenu(props:Props) {
|
||||
templateItems.push({
|
||||
label: _('Create note from template'),
|
||||
click: () => {
|
||||
CommandService.instance().execute('selectTemplate', { noteType: 'note' });
|
||||
CommandService.instance().execute('selectTemplate', 'note');
|
||||
},
|
||||
}, {
|
||||
label: _('Create to-do from template'),
|
||||
click: () => {
|
||||
CommandService.instance().execute('selectTemplate', { noteType: 'todo' });
|
||||
CommandService.instance().execute('selectTemplate', 'todo');
|
||||
},
|
||||
}, {
|
||||
label: _('Insert template'),
|
||||
@@ -322,7 +323,7 @@ function useMenu(props:Props) {
|
||||
click: async () => {
|
||||
const templates = await TemplateUtils.loadTemplates(Setting.value('templateDir'));
|
||||
|
||||
this.store().dispatch({
|
||||
props.dispatch({
|
||||
type: 'TEMPLATE_UPDATE_ALL',
|
||||
templates: templates,
|
||||
});
|
||||
@@ -358,8 +359,8 @@ function useMenu(props:Props) {
|
||||
}
|
||||
toolsItems = toolsItems.concat(toolsItemsAll);
|
||||
|
||||
function _checkForUpdates(ctx:any) {
|
||||
bridge().checkForUpdates(false, bridge().window(), ctx.checkForUpdateLoggerPath(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||
function _checkForUpdates() {
|
||||
bridge().checkForUpdates(false, bridge().window(), `${Setting.value('profileDir')}/log-autoupdater.txt`, { includePreReleases: Setting.value('autoUpdate.includePreReleases') });
|
||||
}
|
||||
|
||||
function _showAbout() {
|
||||
@@ -405,7 +406,7 @@ function useMenu(props:Props) {
|
||||
}, {
|
||||
label: _('Check for updates...'),
|
||||
visible: shim.isMac() ? true : false,
|
||||
click: () => _checkForUpdates(this),
|
||||
click: () => _checkForUpdates(),
|
||||
}, {
|
||||
type: 'separator',
|
||||
visible: shim.isMac() ? true : false,
|
||||
@@ -532,7 +533,7 @@ function useMenu(props:Props) {
|
||||
view: {
|
||||
label: _('&View'),
|
||||
submenu: [
|
||||
menuItemDic.toggleSidebar,
|
||||
menuItemDic.toggleSideBar,
|
||||
menuItemDic.toggleNoteList,
|
||||
menuItemDic.toggleVisiblePanes,
|
||||
{
|
||||
@@ -550,7 +551,6 @@ function useMenu(props:Props) {
|
||||
id: 'showNoteCounts',
|
||||
label: Setting.settingMetadata('showNoteCounts').label(),
|
||||
type: 'checkbox',
|
||||
// checked: Setting.value('showNoteCounts'),
|
||||
click: () => {
|
||||
Setting.setValue('showNoteCounts', !Setting.value('showNoteCounts'));
|
||||
},
|
||||
@@ -558,7 +558,6 @@ function useMenu(props:Props) {
|
||||
id: 'uncompletedTodosOnTop',
|
||||
label: Setting.settingMetadata('uncompletedTodosOnTop').label(),
|
||||
type: 'checkbox',
|
||||
// checked: Setting.value('uncompletedTodosOnTop'),
|
||||
click: () => {
|
||||
Setting.setValue('uncompletedTodosOnTop', !Setting.value('uncompletedTodosOnTop'));
|
||||
},
|
||||
@@ -566,7 +565,6 @@ function useMenu(props:Props) {
|
||||
id: 'showCompletedTodos',
|
||||
label: Setting.settingMetadata('showCompletedTodos').label(),
|
||||
type: 'checkbox',
|
||||
// checked: Setting.value('showCompletedTodos'),
|
||||
click: () => {
|
||||
Setting.setValue('showCompletedTodos', !Setting.value('showCompletedTodos'));
|
||||
},
|
||||
@@ -638,7 +636,7 @@ function useMenu(props:Props) {
|
||||
}, {
|
||||
label: _('Check for updates...'),
|
||||
visible: shim.isMac() ? false : true,
|
||||
click: () => _checkForUpdates(this),
|
||||
click: () => _checkForUpdates(),
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
@@ -786,9 +784,13 @@ function useMenu(props:Props) {
|
||||
}, [props.routeName, props.pluginMenuItems, props.pluginMenus, keymapLastChangeTime, modulesLastChangeTime]);
|
||||
|
||||
useEffect(() => {
|
||||
const whenClauseContext = CommandService.instance().currentWhenClauseContext();
|
||||
|
||||
for (const commandName in props.menuItemProps) {
|
||||
if (!props.menuItemProps[commandName]) continue;
|
||||
menuItemSetEnabled(commandName, CommandService.instance().isEnabled(commandName, props.menuItemProps[commandName]));
|
||||
const p = props.menuItemProps[commandName];
|
||||
if (!p) continue;
|
||||
const enabled = 'enabled' in p ? p.enabled : CommandService.instance().isEnabled(commandName, whenClauseContext);
|
||||
menuItemSetEnabled(commandName, enabled);
|
||||
}
|
||||
|
||||
const layoutButtonSequenceOptions = Setting.enumOptions('layoutButtonSequence');
|
||||
@@ -858,8 +860,10 @@ function MenuBar(props:Props):JSX.Element {
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:AppState) => {
|
||||
const whenClauseContext = stateToWhenClauseContext(state);
|
||||
|
||||
return {
|
||||
menuItemProps: menuUtils.commandsToMenuItemProps(state, commandNames.concat(pluginCommandNames(state.pluginService.plugins))),
|
||||
menuItemProps: menuUtils.commandsToMenuItemProps(commandNames.concat(pluginCommandNames(state.pluginService.plugins)), whenClauseContext),
|
||||
routeName: state.route.routeName,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
layoutButtonSequence: state.settings.layoutButtonSequence,
|
||||
|
||||
@@ -46,8 +46,6 @@ function formatReadTime(readTimeMinutes: number) {
|
||||
}
|
||||
|
||||
export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) {
|
||||
|
||||
console.info('MMMMMMMMMMMM', props.markupLanguage);
|
||||
const theme = themeStyle(props.themeId);
|
||||
const tableBodyComps: JSX.Element[] = [];
|
||||
// For the source Markdown
|
||||
|
||||
@@ -5,6 +5,7 @@ import { utils as pluginUtils } from 'lib/services/plugins/reducer';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from '../../../../app';
|
||||
import ToolbarButtonUtils, { ToolbarButtonInfo } from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
interface ToolbarProps {
|
||||
@@ -31,6 +32,8 @@ function Toolbar(props:ToolbarProps) {
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const whenClauseContext = stateToWhenClauseContext(state);
|
||||
|
||||
const commandNames = [
|
||||
'historyBackward',
|
||||
'historyForward',
|
||||
@@ -53,7 +56,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
].concat(pluginUtils.commandNamesFromViews(state.pluginService.plugins, 'editorToolbar'));
|
||||
|
||||
return {
|
||||
toolbarButtonInfos: toolbarButtonUtils.commandsToToolbarButtons(state, commandNames),
|
||||
toolbarButtonInfos: toolbarButtonUtils.commandsToToolbarButtons(commandNames, whenClauseContext),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import eventManager from 'lib/eventManager';
|
||||
import { AppState } from '../../app';
|
||||
import ToolbarButtonUtils from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import { _ } from 'lib/locale';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { substrWithEllipsis } = require('lib/string-utils');
|
||||
@@ -247,9 +248,9 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteList' });
|
||||
CommandService.instance().execute('focusElement', 'noteList');
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteBody' });
|
||||
CommandService.instance().execute('focusElement', 'noteBody');
|
||||
}
|
||||
}
|
||||
}, [props.dispatch]);
|
||||
@@ -364,7 +365,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
function renderTagBar() {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const noteIds = [formNote.id];
|
||||
const instructions = <span onClick={() => { CommandService.instance().execute('setTags', { noteIds }); }} style={{ ...theme.clickableTextStyle, whiteSpace: 'nowrap' }}>Click to add tags...</span>;
|
||||
const instructions = <span onClick={() => { CommandService.instance().execute('setTags', noteIds); }} style={{ ...theme.clickableTextStyle, whiteSpace: 'nowrap' }}>Click to add tags...</span>;
|
||||
const tagList = props.selectedNoteTags.length ? <TagList items={props.selectedNoteTags} /> : null;
|
||||
|
||||
return (
|
||||
@@ -435,7 +436,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
const wysiwygBanner = props.bodyEditor !== 'TinyMCE' ? null : (
|
||||
<div style={{ ...styles.warningBanner }}>
|
||||
This is an experimental WYSIWYG editor for evaluation only. Please do not use with important notes as you may lose some data! See the <a style={styles.urlColor} onClick={introductionPostLinkClick} href="#">introduction post</a> for more information. TO SWITCH TO THE MARKDOWN EDITOR PLEASE PRESS "Code View".
|
||||
This is an experimental Rich Text editor for evaluation only. Please do not use with important notes as you may lose some data! See the <a style={styles.urlColor} onClick={introductionPostLinkClick} href="#">introduction post</a> for more information. To switch to the Markdown Editor please press the "Toggle editors" in the top right-hand corner.
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -565,6 +566,7 @@ export {
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null;
|
||||
const whenClauseContext = stateToWhenClauseContext(state);
|
||||
|
||||
return {
|
||||
noteId: noteId,
|
||||
@@ -587,15 +589,15 @@ const mapStateToProps = (state: AppState) => {
|
||||
watchedResources: state.watchedResources,
|
||||
highlightedWords: state.highlightedWords,
|
||||
plugins: state.pluginService.plugins,
|
||||
toolbarButtonInfos: toolbarButtonUtils.commandsToToolbarButtons(state, [
|
||||
toolbarButtonInfos: toolbarButtonUtils.commandsToToolbarButtons([
|
||||
'historyBackward',
|
||||
'historyForward',
|
||||
'toggleEditors',
|
||||
'toggleExternalEditing',
|
||||
]),
|
||||
setTagsToolbarButtonInfo: toolbarButtonUtils.commandsToToolbarButtons(state, [
|
||||
], whenClauseContext),
|
||||
setTagsToolbarButtonInfo: toolbarButtonUtils.commandsToToolbarButtons([
|
||||
'setTags',
|
||||
])[0],
|
||||
], whenClauseContext)[0],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -12,13 +12,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
execute: async () => {
|
||||
comp.editorRef.current.execCommand({ name: 'focus' });
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return props.hasOneNoteSelected;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
hasOneNoteSelected: state.selectedNoteIds.length === 1,
|
||||
};
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -13,14 +13,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
if (!comp.titleInputRef.current) return;
|
||||
comp.titleInputRef.current.focus();
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return props.hasOneNoteSelected;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
hasOneNoteSelected: state.selectedNoteIds.length === 1,
|
||||
};
|
||||
},
|
||||
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
@@ -16,11 +16,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
if (comp.noteSearchBarRef.current) comp.noteSearchBarRef.current.wrappedInstance.focus();
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null };
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'showRevisions',
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import { FormNote, ScrollOptionTypes } from './types';
|
||||
import editorCommandDeclarations from '../commands/editorCommandDeclarations';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext } from 'lib/services/CommandService';
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
|
||||
const commandsWithDependencies = [
|
||||
require('../commands/showLocalSearch'),
|
||||
@@ -25,49 +23,30 @@ interface HookDependencies {
|
||||
|
||||
function editorCommandRuntime(declaration:CommandDeclaration, editorRef:any):CommandRuntime {
|
||||
return {
|
||||
execute: async (props:any) => {
|
||||
// console.info('Running editor command:', declaration.name, props);
|
||||
execute: async (_context:CommandContext, ...args:any[]) => {
|
||||
if (!editorRef.current.execCommand) {
|
||||
reg.logger().warn('Received command, but editor cannot execute commands', declaration.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (declaration.name === 'insertDateTime') {
|
||||
return editorRef.current.execCommand({
|
||||
name: 'insertText',
|
||||
value: time.formatMsToLocal(new Date().getTime()),
|
||||
});
|
||||
} else if (declaration.name === 'scrollToHash') {
|
||||
return editorRef.current.scrollTo({
|
||||
type: ScrollOptionTypes.Hash,
|
||||
value: args[0],
|
||||
});
|
||||
} else {
|
||||
if (declaration.name === 'insertDateTime') {
|
||||
return editorRef.current.execCommand({
|
||||
name: 'insertText',
|
||||
value: time.formatMsToLocal(new Date().getTime()),
|
||||
});
|
||||
} else if (declaration.name === 'scrollToHash') {
|
||||
return editorRef.current.scrollTo({
|
||||
type: ScrollOptionTypes.Hash,
|
||||
value: props.hash,
|
||||
});
|
||||
} else {
|
||||
return editorRef.current.execCommand({
|
||||
name: declaration.name,
|
||||
value: props.value,
|
||||
});
|
||||
}
|
||||
return editorRef.current.execCommand({
|
||||
name: declaration.name,
|
||||
value: args[0],
|
||||
});
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
if (props.isDialogVisible) return false;
|
||||
if (props.markdownEditorViewerOnly) return false;
|
||||
if (!props.hasSelectedNote) return false;
|
||||
return props.isMarkdownNote;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null;
|
||||
const note = noteId ? BaseModel.byId(state.notes, noteId) : null;
|
||||
const isMarkdownNote = note ? note.markup_language === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN : false;
|
||||
|
||||
return {
|
||||
// True when the Markdown editor is active, and only the viewer pane is visible
|
||||
// In this case, all editor-related shortcuts are disabled.
|
||||
markdownEditorViewerOnly: state.settings['editor.codeView'] && state.noteVisiblePanes.length === 1 && state.noteVisiblePanes[0] === 'viewer',
|
||||
hasSelectedNote: !!note,
|
||||
isDialogVisible: !!Object.keys(state.visibleDialogs).length,
|
||||
isMarkdownNote: isMarkdownNote,
|
||||
};
|
||||
},
|
||||
enabledCondition: '!modalDialogVisible && markdownEditorPaneVisible && oneNoteSelected && noteIsMarkdown',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const StyledRoot = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
border-right: 1px solid ${(props:any) => props.theme.dividerColor},
|
||||
border-right: 1px solid ${(props:any) => props.theme.dividerColor};
|
||||
`;
|
||||
|
||||
class NoteListComponent extends React.Component {
|
||||
@@ -386,9 +386,9 @@ class NoteListComponent extends React.Component {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', { target: 'sideBar' });
|
||||
CommandService.instance().execute('focusElement', 'sideBar');
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteTitle' });
|
||||
CommandService.instance().execute('focusElement', 'noteTitle');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'focusElementNoteList',
|
||||
@@ -9,19 +10,14 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ noteId }:any) => {
|
||||
execute: async (context:CommandContext, noteId:string = null) => {
|
||||
noteId = noteId || stateUtils.selectedNoteId(context.state);
|
||||
|
||||
if (noteId) {
|
||||
const ref = comp.itemAnchorRef(noteId);
|
||||
if (ref) ref.focus();
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
};
|
||||
},
|
||||
enabledCondition: 'noteListHasNotes',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -52,13 +52,13 @@ export default function NoteListControls(props:Props) {
|
||||
return (
|
||||
<ButtonContainer>
|
||||
<StyledButton
|
||||
tooltip={CommandService.instance().title('newTodo', {})}
|
||||
tooltip={CommandService.instance().label('newTodo')}
|
||||
iconName="far fa-check-square"
|
||||
level={ButtonLevel.Primary}
|
||||
onClick={onNewTodoButtonClick}
|
||||
/>
|
||||
<StyledButton
|
||||
tooltip={CommandService.instance().title('newNote', {})}
|
||||
tooltip={CommandService.instance().label('newNote')}
|
||||
iconName="icon-note"
|
||||
level={ButtonLevel.Primary}
|
||||
onClick={onNewNoteButtonClick}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
|
||||
@@ -3,6 +3,7 @@ import CommandService from 'lib/services/CommandService';
|
||||
import ToolbarBase from '../ToolbarBase';
|
||||
import { utils as pluginUtils } from 'lib/services/plugins/reducer';
|
||||
import ToolbarButtonUtils, { ToolbarButtonInfo } from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import stateToWhenClauseContext from 'lib/services/commands/stateToWhenClauseContext';
|
||||
const { connect } = require('react-redux');
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
@@ -32,12 +33,14 @@ function NoteToolbar(props:NoteToolbarProps) {
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
const whenClauseContext = stateToWhenClauseContext(state);
|
||||
|
||||
return {
|
||||
toolbarButtonInfos: toolbarButtonUtils.commandsToToolbarButtons(state, [
|
||||
toolbarButtonInfos: toolbarButtonUtils.commandsToToolbarButtons([
|
||||
'editAlarm',
|
||||
'toggleVisiblePanes',
|
||||
'showNoteProperties',
|
||||
].concat(pluginUtils.commandNamesFromViews(state.pluginService.plugins, 'noteToolbar'))),
|
||||
].concat(pluginUtils.commandNamesFromViews(state.pluginService.plugins, 'noteToolbar')), whenClauseContext),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const debounce = require('debounce');
|
||||
export default function useSearch(query:string) {
|
||||
useEffect(() => {
|
||||
const search = debounce((query:string) => {
|
||||
CommandService.instance().execute('search', { query });
|
||||
CommandService.instance().execute('search', query);
|
||||
}, 500);
|
||||
|
||||
search(query);
|
||||
|
||||
@@ -230,7 +230,7 @@ class SideBarComponent extends React.Component<Props, State> {
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('newFolder', { parentId: itemId }))
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('newFolder', itemId))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ class SideBarComponent extends React.Component<Props, State> {
|
||||
);
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem('renameFolder', { folderId: itemId })));
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem('renameFolder', itemId)));
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
|
||||
@@ -290,7 +290,7 @@ class SideBarComponent extends React.Component<Props, State> {
|
||||
|
||||
if (itemType === BaseModel.TYPE_TAG) {
|
||||
menu.append(new MenuItem(
|
||||
menuUtils.commandToStatefulMenuItem('renameTag', { tagId: itemId })
|
||||
menuUtils.commandToStatefulMenuItem('renameTag', itemId)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -510,9 +510,9 @@ class SideBarComponent extends React.Component<Props, State> {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteBody' });
|
||||
CommandService.instance().execute('focusElement', 'noteBody');
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteList' });
|
||||
CommandService.instance().execute('focusElement', 'noteList');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,7 +559,7 @@ class SideBarComponent extends React.Component<Props, State> {
|
||||
iconAnimation={iconAnimation}
|
||||
title={label}
|
||||
onClick={() => {
|
||||
CommandService.instance().execute('synchronize', { syncStarted: type !== 'sync' });
|
||||
CommandService.instance().execute('synchronize', type !== 'sync');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../../../lib/services/CommandService';
|
||||
import { CommandRuntime, CommandDeclaration } from 'lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { DesktopCommandContext } from 'ElectronClient/services/commands/types';
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'focusElementSideBar',
|
||||
@@ -9,8 +10,10 @@ export const declaration:CommandDeclaration = {
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ sidebarVisibility }:any) => {
|
||||
if (sidebarVisibility) {
|
||||
execute: async (context:DesktopCommandContext) => {
|
||||
const sideBarVisible = !!context.state.sidebarVisibility;
|
||||
|
||||
if (sideBarVisible) {
|
||||
const item = comp.selectedItem();
|
||||
if (item) {
|
||||
const anchorRef = comp.anchorItemRefs[item.type][item.id];
|
||||
@@ -21,13 +24,7 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
}
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return props.sidebarVisibility;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
sidebarVisibility: state.sidebarVisibility,
|
||||
};
|
||||
},
|
||||
|
||||
enabledCondition: 'sideBarVisible',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -68,7 +68,7 @@ function listItemTextColor(props:any) {
|
||||
|
||||
export const StyledListItemAnchor = styled.a`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.0833333)}px;
|
||||
font-weight: 500;
|
||||
// font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: ${(props:any) => listItemTextColor(props)};
|
||||
cursor: default;
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function ToggleEditorsButton(props:Props) {
|
||||
const style = styles_(props);
|
||||
|
||||
return (
|
||||
<button style={style.button} disabled={!props.toolbarButtonInfo.enabled} aria-label={props.toolbarButtonInfo.title} title={props.toolbarButtonInfo.title} type="button" className="tox-tbtn" aria-pressed="false" onClick={props.toolbarButtonInfo.onClick}>
|
||||
<button style={style.button} disabled={!props.toolbarButtonInfo.enabled} aria-label={props.toolbarButtonInfo.tooltip} title={props.toolbarButtonInfo.tooltip} type="button" className="tox-tbtn" aria-pressed="false" onClick={props.toolbarButtonInfo.onClick}>
|
||||
<div style={style.leftInnerButton}>
|
||||
<i style={style.leftIcon} className="fab fa-markdown"></i>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ export default function styles(props:Props) {
|
||||
return buildStyle(['ToggleEditorsButton', props.value], props.themeId, (theme: any) => {
|
||||
const iconSize = 15;
|
||||
const mdIconWidth = iconSize * 1.25;
|
||||
const buttonHeight = theme.toolbarHeight - 8;
|
||||
const buttonHeight = theme.toolbarHeight - 7;
|
||||
const mdIconPadding = Math.round((buttonHeight - iconSize) / 2) + 3;
|
||||
|
||||
const innerButton:any = {
|
||||
@@ -41,14 +41,14 @@ export default function styles(props:Props) {
|
||||
leftIcon: {
|
||||
fontSize: iconSize,
|
||||
position: 'relative',
|
||||
top: 1,
|
||||
top: 2,
|
||||
color: theme.color3,
|
||||
},
|
||||
rightIcon: {
|
||||
fontSize: iconSize - 1,
|
||||
borderLeft: 'none',
|
||||
position: 'relative',
|
||||
top: 1,
|
||||
top: 2,
|
||||
color: theme.color3,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -40,11 +40,11 @@ export default class NoteListUtils {
|
||||
|
||||
if (!hasEncrypted) {
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('setTags', { noteIds }))
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('setTags', noteIds))
|
||||
);
|
||||
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('moveToFolder'))
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('moveToFolder', noteIds))
|
||||
);
|
||||
|
||||
menu.append(
|
||||
@@ -63,7 +63,7 @@ export default class NoteListUtils {
|
||||
|
||||
if (singleNoteId) {
|
||||
const cmd = props.watchedNoteFiles.includes(singleNoteId) ? 'stopExternalEditing' : 'startExternalEditing';
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem(cmd, { noteId: singleNoteId })));
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem(cmd, singleNoteId)));
|
||||
}
|
||||
|
||||
if (noteIds.length <= 1) {
|
||||
@@ -132,7 +132,7 @@ export default class NoteListUtils {
|
||||
|
||||
menu.append(
|
||||
new MenuItem(
|
||||
menuUtils.commandToStatefulMenuItem('showShareNoteDialog', { noteIds: noteIds.slice() })
|
||||
menuUtils.commandToStatefulMenuItem('showShareNoteDialog', noteIds.slice())
|
||||
)
|
||||
);
|
||||
|
||||
@@ -157,7 +157,7 @@ export default class NoteListUtils {
|
||||
|
||||
exportMenu.append(
|
||||
new MenuItem(
|
||||
menuUtils.commandToStatefulMenuItem('exportPdf', { noteIds: noteIds })
|
||||
menuUtils.commandToStatefulMenuItem('exportPdf', noteIds)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
61
ElectronClient/package-lock.json
generated
61
ElectronClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.5",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1535,7 +1535,8 @@
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -1700,7 +1701,8 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
@@ -1815,7 +1817,8 @@
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.8",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
|
||||
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -5946,8 +5949,22 @@
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
@@ -6117,7 +6134,8 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
@@ -7762,6 +7780,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
"integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg="
|
||||
},
|
||||
"lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
@@ -8490,6 +8513,14 @@
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
},
|
||||
"node-emoji": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
|
||||
"integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
|
||||
"requires": {
|
||||
"lodash.toarray": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
@@ -10668,11 +10699,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"slug": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/slug/-/slug-3.5.0.tgz",
|
||||
"integrity": "sha512-+pZLDhMtmAc+ZcojQSMlUKDZBYmvhZiZmK8Ffx/D3Q/MIMHPDBAMbWvWN8vJb9xl2MfbDdRWxFzrdOhBiyVpow=="
|
||||
},
|
||||
"smalltalk": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/smalltalk/-/smalltalk-2.5.1.tgz",
|
||||
@@ -11735,6 +11761,11 @@
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
||||
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
|
||||
},
|
||||
"unorm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
|
||||
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
|
||||
},
|
||||
"unset-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
||||
@@ -11926,6 +11957,14 @@
|
||||
"integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
|
||||
"dev": true
|
||||
},
|
||||
"uslug": {
|
||||
"version": "git+https://github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
|
||||
"from": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"requires": {
|
||||
"node-emoji": "^1.10.0",
|
||||
"unorm": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"utf8-byte-length": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.5",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -195,7 +195,6 @@
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"sax": "^1.2.4",
|
||||
"server-destroy": "^1.0.1",
|
||||
"slug": "^3.5.0",
|
||||
"smalltalk": "^2.5.1",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"sqlite3": "^4.1.1",
|
||||
@@ -211,6 +210,7 @@
|
||||
"tinymce": "^5.2.0",
|
||||
"uglifycss": "0.0.29",
|
||||
"url-parse": "^1.4.3",
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"uuid": "^3.2.1",
|
||||
"valid-url": "^1.0.9",
|
||||
"xml2js": "^0.4.19"
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
const React = require('react');
|
||||
import * as React from 'react';
|
||||
import { AppState } from '../app';
|
||||
import CommandService, { SearchResult as CommandSearchResult } from 'lib/services/CommandService';
|
||||
import KeymapService from 'lib/services/KeymapService';
|
||||
import shim from 'lib/shim';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const CommandService = require('lib/services/CommandService').default;
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const Tag = require('lib/models/Tag');
|
||||
@@ -12,32 +16,73 @@ const { ItemList } = require('../gui/ItemList.min');
|
||||
const HelpButton = require('../gui/HelpButton.min');
|
||||
const { surroundKeywords, nextWhitespaceIndex, removeDiacritics } = require('lib/string-utils.js');
|
||||
const { mergeOverlappingIntervals } = require('lib/ArrayUtils.js');
|
||||
const PLUGIN_NAME = 'gotoAnything';
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils');
|
||||
const KeymapService = require('lib/services/KeymapService.js').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
const PLUGIN_NAME = 'gotoAnything';
|
||||
|
||||
interface SearchResult {
|
||||
id: string,
|
||||
title: string,
|
||||
parent_id: string,
|
||||
fields: string[],
|
||||
fragments?: string,
|
||||
path?: string,
|
||||
type?: number,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
themeId: number,
|
||||
dispatch: Function,
|
||||
folders: any[],
|
||||
showCompletedTodos: boolean,
|
||||
userData: any,
|
||||
}
|
||||
|
||||
interface State {
|
||||
query: string,
|
||||
results: SearchResult[],
|
||||
selectedItemId: string,
|
||||
keywords: string[],
|
||||
listType: number,
|
||||
showHelp: boolean,
|
||||
resultsInBody: boolean,
|
||||
}
|
||||
|
||||
class GotoAnything {
|
||||
|
||||
onTrigger() {
|
||||
public dispatch:Function;
|
||||
public static Dialog:any;
|
||||
public static manifest:any;
|
||||
|
||||
onTrigger(event:any) {
|
||||
this.dispatch({
|
||||
type: 'PLUGINLEGACY_DIALOG_SET',
|
||||
open: true,
|
||||
pluginName: PLUGIN_NAME,
|
||||
userData: event.userData,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Dialog extends React.PureComponent {
|
||||
class Dialog extends React.PureComponent<Props, State> {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
private fuzzy_:boolean;
|
||||
private styles_:any;
|
||||
private inputRef:any;
|
||||
private itemListRef:any;
|
||||
private listUpdateIID_:any;
|
||||
private markupToHtml_:any;
|
||||
|
||||
constructor(props:Props) {
|
||||
super(props);
|
||||
|
||||
this.fuzzy_ = false;
|
||||
|
||||
const startString = props?.userData?.startString ? props?.userData?.startString : '';
|
||||
|
||||
this.state = {
|
||||
query: '',
|
||||
query: startString,
|
||||
results: [],
|
||||
selectedItemId: null,
|
||||
keywords: [],
|
||||
@@ -55,19 +100,25 @@ class Dialog extends React.PureComponent {
|
||||
this.input_onChange = this.input_onChange.bind(this);
|
||||
this.input_onKeyDown = this.input_onKeyDown.bind(this);
|
||||
this.modalLayer_onClick = this.modalLayer_onClick.bind(this);
|
||||
this.listItemRenderer = this.listItemRenderer.bind(this);
|
||||
this.renderItem = this.renderItem.bind(this);
|
||||
this.listItem_onClick = this.listItem_onClick.bind(this);
|
||||
this.helpButton_onClick = this.helpButton_onClick.bind(this);
|
||||
|
||||
if (startString) this.scheduleListUpdate();
|
||||
}
|
||||
|
||||
style() {
|
||||
const styleKey = [this.props.themeId, this.state.resultsInBody ? '1' : '0'].join('-');
|
||||
const styleKey = [this.props.themeId, this.state.listType, this.state.resultsInBody ? '1' : '0'].join('-');
|
||||
|
||||
if (this.styles_[styleKey]) return this.styles_[styleKey];
|
||||
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const itemHeight = this.state.resultsInBody ? 84 : 64;
|
||||
let itemHeight = this.state.resultsInBody ? 84 : 64;
|
||||
|
||||
if (this.state.listType === BaseModel.TYPE_COMMAND) {
|
||||
itemHeight = 40;
|
||||
}
|
||||
|
||||
this.styles_[styleKey] = {
|
||||
dialogBox: Object.assign({}, theme.dialogBox, { minWidth: '50%', maxWidth: '50%' }),
|
||||
@@ -138,7 +189,7 @@ class Dialog extends React.PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
onKeyDown(event:any) {
|
||||
if (event.keyCode === 27) { // ESCAPE
|
||||
this.props.dispatch({
|
||||
pluginName: PLUGIN_NAME,
|
||||
@@ -148,7 +199,7 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
modalLayer_onClick(event) {
|
||||
modalLayer_onClick(event:any) {
|
||||
if (event.currentTarget == event.target) {
|
||||
this.props.dispatch({
|
||||
pluginName: PLUGIN_NAME,
|
||||
@@ -162,7 +213,7 @@ class Dialog extends React.PureComponent {
|
||||
this.setState({ showHelp: !this.state.showHelp });
|
||||
}
|
||||
|
||||
input_onChange(event) {
|
||||
input_onChange(event:any) {
|
||||
this.setState({ query: event.target.value });
|
||||
|
||||
this.scheduleListUpdate();
|
||||
@@ -177,7 +228,7 @@ class Dialog extends React.PureComponent {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
makeSearchQuery(query) {
|
||||
makeSearchQuery(query:string) {
|
||||
const output = [];
|
||||
const splitted = query.split(' ');
|
||||
|
||||
@@ -190,7 +241,7 @@ class Dialog extends React.PureComponent {
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
async keywords(searchQuery) {
|
||||
async keywords(searchQuery:string) {
|
||||
const parsedQuery = await SearchEngine.instance().parseQuery(searchQuery, this.fuzzy_);
|
||||
return SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
}
|
||||
@@ -207,11 +258,28 @@ class Dialog extends React.PureComponent {
|
||||
if (!this.state.query) {
|
||||
this.setState({ results: [], keywords: [] });
|
||||
} else {
|
||||
let results = [];
|
||||
let results:SearchResult[] = [];
|
||||
let listType = null;
|
||||
let searchQuery = '';
|
||||
let keywords = null;
|
||||
|
||||
if (this.state.query.indexOf('#') === 0) { // TAGS
|
||||
if (this.state.query.indexOf(':') === 0) { // COMMANDS
|
||||
const query = this.state.query.substr(1);
|
||||
listType = BaseModel.TYPE_COMMAND;
|
||||
keywords = [query];
|
||||
|
||||
const commandResults = CommandService.instance().searchCommands(query, true);
|
||||
|
||||
results = commandResults.map((result:CommandSearchResult) => {
|
||||
return {
|
||||
id: result.commandName,
|
||||
title: result.title,
|
||||
parent_id: null,
|
||||
fields: [],
|
||||
type: BaseModel.TYPE_COMMAND,
|
||||
};
|
||||
});
|
||||
} else if (this.state.query.indexOf('#') === 0) { // TAGS
|
||||
listType = BaseModel.TYPE_TAG;
|
||||
searchQuery = `*${this.state.query.split(' ')[0].substr(1).trim()}*`;
|
||||
results = await Tag.searchAllWithNotes({ titlePattern: searchQuery });
|
||||
@@ -230,7 +298,7 @@ class Dialog extends React.PureComponent {
|
||||
searchQuery = this.makeSearchQuery(this.state.query);
|
||||
results = await SearchEngine.instance().search(searchQuery, { fuzzy: this.fuzzy_ });
|
||||
|
||||
resultsInBody = !!results.find(row => row.fields.includes('body'));
|
||||
resultsInBody = !!results.find((row:any) => row.fields.includes('body'));
|
||||
|
||||
if (!resultsInBody || this.state.query.length <= 1) {
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
@@ -241,7 +309,9 @@ class Dialog extends React.PureComponent {
|
||||
} else {
|
||||
const limit = 20;
|
||||
const searchKeywords = await this.keywords(searchQuery);
|
||||
const notes = await Note.byIds(results.map(result => result.id).slice(0, limit), { fields: ['id', 'body', 'markup_language', 'is_todo', 'todo_completed'] });
|
||||
const notes = await Note.byIds(results.map((result:any) => result.id).slice(0, limit), { fields: ['id', 'body', 'markup_language', 'is_todo', 'todo_completed'] });
|
||||
// Can't make any sense of this code so...
|
||||
// @ts-ignore
|
||||
const notesById = notes.reduce((obj, { id, body, markup_language }) => ((obj[[id]] = { id, body, markup_language }), obj), {});
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
@@ -272,7 +342,7 @@ class Dialog extends React.PureComponent {
|
||||
// e.g. 'Joplin is a free, open source' and 'open source note taking application'
|
||||
// will result in 'Joplin is a free, open source note taking application'
|
||||
const mergedIndices = mergeOverlappingIntervals(indices, 3);
|
||||
fragments = mergedIndices.map(f => body.slice(f[0], f[1])).join(' ... ');
|
||||
fragments = mergedIndices.map((f:any) => body.slice(f[0], f[1])).join(' ... ');
|
||||
// Add trailing ellipsis if the final fragment doesn't end where the note is ending
|
||||
if (mergedIndices.length && mergedIndices[mergedIndices.length - 1][1] !== body.length) fragments += ' ...';
|
||||
|
||||
@@ -285,7 +355,7 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
|
||||
if (!this.props.showCompletedTodos) {
|
||||
results = results.filter((row) => !row.is_todo || !row.todo_completed);
|
||||
results = results.filter((row:any) => !row.is_todo || !row.todo_completed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,20 +366,25 @@ class Dialog extends React.PureComponent {
|
||||
this.setState({
|
||||
listType: listType,
|
||||
results: results,
|
||||
keywords: await this.keywords(searchQuery),
|
||||
keywords: keywords ? keywords : await this.keywords(searchQuery),
|
||||
selectedItemId: results.length === 0 ? null : results[0].id,
|
||||
resultsInBody: resultsInBody,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async gotoItem(item) {
|
||||
async gotoItem(item:any) {
|
||||
this.props.dispatch({
|
||||
pluginName: PLUGIN_NAME,
|
||||
type: 'PLUGINLEGACY_DIALOG_SET',
|
||||
open: false,
|
||||
});
|
||||
|
||||
if (item.type === BaseModel.TYPE_COMMAND) {
|
||||
CommandService.instance().execute(item.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.listType === BaseModel.TYPE_NOTE || this.state.listType === BaseModel.TYPE_FOLDER) {
|
||||
const folderPath = await Folder.folderPath(this.props.folders, item.parent_id);
|
||||
|
||||
@@ -329,7 +404,7 @@ class Dialog extends React.PureComponent {
|
||||
noteId: item.id,
|
||||
});
|
||||
|
||||
CommandService.instance().scheduleExecute('focusElement', { target: 'noteBody' });
|
||||
CommandService.instance().scheduleExecute('focusElement', 'noteBody');
|
||||
} else if (this.state.listType === BaseModel.TYPE_TAG) {
|
||||
this.props.dispatch({
|
||||
type: 'TAG_SELECT',
|
||||
@@ -343,17 +418,19 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
listItem_onClick(event) {
|
||||
listItem_onClick(event:any) {
|
||||
const itemId = event.currentTarget.getAttribute('data-id');
|
||||
const parentId = event.currentTarget.getAttribute('data-parent-id');
|
||||
const itemType = event.currentTarget.getAttribute('data-type');
|
||||
|
||||
this.gotoItem({
|
||||
id: itemId,
|
||||
parent_id: parentId,
|
||||
type: itemType,
|
||||
});
|
||||
}
|
||||
|
||||
listItemRenderer(item) {
|
||||
renderItem(item:SearchResult) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
const rowStyle = item.id === this.state.selectedItemId ? style.rowSelected : style.row;
|
||||
@@ -368,7 +445,7 @@ class Dialog extends React.PureComponent {
|
||||
const fragmentComp = !fragmentsHtml ? null : <div style={style.rowFragments} dangerouslySetInnerHTML={{ __html: (fragmentsHtml) }}></div>;
|
||||
|
||||
return (
|
||||
<div key={item.id} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id}>
|
||||
<div key={item.id} style={rowStyle} onClick={this.listItem_onClick} data-id={item.id} data-parent-id={item.parent_id} data-type={item.type}>
|
||||
<div style={style.rowTitle} dangerouslySetInnerHTML={{ __html: titleHtml }}></div>
|
||||
{fragmentComp}
|
||||
{pathComp}
|
||||
@@ -376,7 +453,7 @@ class Dialog extends React.PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
selectedItemIndex(results, itemId) {
|
||||
selectedItemIndex(results:any[] = undefined, itemId:string = undefined) {
|
||||
if (typeof results === 'undefined') results = this.state.results;
|
||||
if (typeof itemId === 'undefined') itemId = this.state.selectedItemId;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
@@ -392,7 +469,7 @@ class Dialog extends React.PureComponent {
|
||||
return this.state.results[index];
|
||||
}
|
||||
|
||||
input_onKeyDown(event) {
|
||||
input_onKeyDown(event:any) {
|
||||
const keyCode = event.keyCode;
|
||||
|
||||
if (this.state.results.length > 0 && (keyCode === 40 || keyCode === 38)) { // DOWN / UP
|
||||
@@ -428,7 +505,7 @@ class Dialog extends React.PureComponent {
|
||||
|
||||
const itemListStyle = {
|
||||
marginTop: 5,
|
||||
height: Math.min(style.itemHeight * this.state.results.length, 7 * style.itemHeight),
|
||||
height: Math.min(style.itemHeight * this.state.results.length, 10 * style.itemHeight),
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -437,7 +514,7 @@ class Dialog extends React.PureComponent {
|
||||
itemHeight={style.itemHeight}
|
||||
items={this.state.results}
|
||||
style={itemListStyle}
|
||||
itemRenderer={this.listItemRenderer}
|
||||
itemRenderer={this.renderItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -445,7 +522,7 @@ class Dialog extends React.PureComponent {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
const helpComp = !this.state.showHelp ? null : <div style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name.')}</div>;
|
||||
const helpComp = !this.state.showHelp ? null : <div style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name. Or type : to search for commands.')}</div>;
|
||||
|
||||
return (
|
||||
<div onClick={this.modalLayer_onClick} style={theme.dialogModalLayer}>
|
||||
@@ -463,7 +540,7 @@ class Dialog extends React.PureComponent {
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const mapStateToProps = (state:AppState) => {
|
||||
return {
|
||||
folders: state.folders,
|
||||
themeId: state.settings.theme,
|
||||
@@ -485,8 +562,18 @@ GotoAnything.manifest = {
|
||||
accelerator: () => KeymapService.instance().getAccelerator('gotoAnything'),
|
||||
screens: ['Main'],
|
||||
},
|
||||
{
|
||||
name: 'main',
|
||||
parent: 'tools',
|
||||
label: _('Command palette'),
|
||||
accelerator: () => KeymapService.instance().getAccelerator('commandPalette'),
|
||||
screens: ['Main'],
|
||||
userData: {
|
||||
startString: ':',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
module.exports = GotoAnything;
|
||||
export default GotoAnything;
|
||||
5
ElectronClient/services/commands/types.ts
Normal file
5
ElectronClient/services/commands/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { AppState } from '../../app';
|
||||
|
||||
export interface DesktopCommandContext {
|
||||
state: AppState,
|
||||
}
|
||||
@@ -132,8 +132,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097583
|
||||
versionName "1.3.0"
|
||||
versionCode 2097595
|
||||
versionName "1.3.7"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
}
|
||||
@@ -175,7 +175,7 @@ android {
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
signingConfig signingConfigs.release
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ const syswidecas = require('syswide-cas');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
|
||||
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
||||
const SyncTargetOneDriveDev = require('lib/SyncTargetOneDriveDev.js');
|
||||
const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js');
|
||||
const SyncTargetDropbox = require('lib/SyncTargetDropbox.js');
|
||||
@@ -673,7 +672,6 @@ export default class BaseApplication {
|
||||
|
||||
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
||||
SyncTargetRegistry.addClass(SyncTargetOneDrive);
|
||||
if (Setting.value('env') === 'dev') SyncTargetRegistry.addClass(SyncTargetOneDriveDev);
|
||||
SyncTargetRegistry.addClass(SyncTargetNextcloud);
|
||||
SyncTargetRegistry.addClass(SyncTargetWebDAV);
|
||||
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||
|
||||
@@ -590,7 +590,24 @@ class BaseModel {
|
||||
}
|
||||
}
|
||||
|
||||
BaseModel.typeEnum_ = [['TYPE_NOTE', 1], ['TYPE_FOLDER', 2], ['TYPE_SETTING', 3], ['TYPE_RESOURCE', 4], ['TYPE_TAG', 5], ['TYPE_NOTE_TAG', 6], ['TYPE_SEARCH', 7], ['TYPE_ALARM', 8], ['TYPE_MASTER_KEY', 9], ['TYPE_ITEM_CHANGE', 10], ['TYPE_NOTE_RESOURCE', 11], ['TYPE_RESOURCE_LOCAL_STATE', 12], ['TYPE_REVISION', 13], ['TYPE_MIGRATION', 14], ['TYPE_SMART_FILTER', 15]];
|
||||
BaseModel.typeEnum_ = [
|
||||
['TYPE_NOTE', 1],
|
||||
['TYPE_FOLDER', 2],
|
||||
['TYPE_SETTING', 3],
|
||||
['TYPE_RESOURCE', 4],
|
||||
['TYPE_TAG', 5],
|
||||
['TYPE_NOTE_TAG', 6],
|
||||
['TYPE_SEARCH', 7],
|
||||
['TYPE_ALARM', 8],
|
||||
['TYPE_MASTER_KEY', 9],
|
||||
['TYPE_ITEM_CHANGE', 10],
|
||||
['TYPE_NOTE_RESOURCE', 11],
|
||||
['TYPE_RESOURCE_LOCAL_STATE', 12],
|
||||
['TYPE_REVISION', 13],
|
||||
['TYPE_MIGRATION', 14],
|
||||
['TYPE_SMART_FILTER', 15],
|
||||
['TYPE_COMMAND', 16],
|
||||
];
|
||||
|
||||
for (let i = 0; i < BaseModel.typeEnum_.length; i++) {
|
||||
const e = BaseModel.typeEnum_[i];
|
||||
|
||||
@@ -4,27 +4,16 @@ import { _ } from 'lib/locale';
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'historyBackward',
|
||||
label: () => _('Back'),
|
||||
// iconName: 'fa-arrow-left',
|
||||
iconName: 'icon-back',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
hasBackwardNotes: boolean,
|
||||
}
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
if (!props.hasBackwardNotes) return;
|
||||
execute: async () => {
|
||||
utils.store.dispatch({
|
||||
type: 'HISTORY_BACKWARD',
|
||||
});
|
||||
},
|
||||
isEnabled: (props:Props) => {
|
||||
return props.hasBackwardNotes;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { hasBackwardNotes: state.backwardHistoryNotes.length > 0 };
|
||||
},
|
||||
enabledCondition: 'historyhasBackwardNotes',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,23 +7,13 @@ export const declaration:CommandDeclaration = {
|
||||
iconName: 'icon-forward',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
hasForwardNotes: boolean,
|
||||
}
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
if (!props.hasForwardNotes) return;
|
||||
execute: async () => {
|
||||
utils.store.dispatch({
|
||||
type: 'HISTORY_FORWARD',
|
||||
});
|
||||
},
|
||||
isEnabled: (props:Props) => {
|
||||
return props.hasForwardNotes;
|
||||
},
|
||||
mapStateToProps: (state:any) => {
|
||||
return { hasForwardNotes: state.forwardHistoryNotes.length > 0 };
|
||||
},
|
||||
enabledCondition: 'historyhasForwardNotes',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { utils, CommandRuntime, CommandDeclaration } from '../services/CommandService';
|
||||
import { utils, CommandRuntime, CommandDeclaration, CommandContext } from '../services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
@@ -8,9 +8,11 @@ export const declaration:CommandDeclaration = {
|
||||
iconName: 'fa-sync-alt',
|
||||
};
|
||||
|
||||
// Note that this command actually acts as a toggle - it starts or cancels
|
||||
// synchronisation depending on the "syncStarted" parameter
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ syncStarted }:any) => {
|
||||
execute: async (_context:CommandContext, syncStarted:boolean = false) => {
|
||||
const action = syncStarted ? 'cancel' : 'start';
|
||||
|
||||
if (!(await reg.syncTarget().isAuthenticated())) {
|
||||
@@ -43,13 +45,5 @@ export const runtime = ():CommandRuntime => {
|
||||
return 'sync';
|
||||
}
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return !props.syncStarted;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
syncStarted: state.syncStarted,
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -237,7 +237,7 @@ class SideMenuContentComponent extends Component {
|
||||
|
||||
let iconWrapper = null;
|
||||
|
||||
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'md-arrow-down' : 'md-arrow-up';
|
||||
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'chevron-down' : 'chevron-up';
|
||||
const iconComp = <Icon name={iconName} style={this.styles().folderIcon} />;
|
||||
|
||||
iconWrapper = !hasChildren ? null : (
|
||||
|
||||
@@ -22,7 +22,7 @@ const rules = {
|
||||
// const eventManager = require('lib/eventManager').default;
|
||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
||||
const hljs = require('highlight.js');
|
||||
const nodeSlug = require('slug');
|
||||
const uslug = require('uslug');
|
||||
const markdownItAnchor = require('markdown-it-anchor');
|
||||
// The keys must match the corresponding entry in Setting.js
|
||||
const plugins = {
|
||||
@@ -41,7 +41,7 @@ const plugins = {
|
||||
const defaultNoteStyle = require('./defaultNoteStyle');
|
||||
|
||||
function slugify(s) {
|
||||
return nodeSlug(s);
|
||||
return uslug(s);
|
||||
}
|
||||
|
||||
// Share across all instances of MdToHtml
|
||||
|
||||
@@ -25,9 +25,9 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
const longPressHandler = `${ruleOptions.postMessageSyntax}('longclick:${id}')`;
|
||||
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${longPressHandler};}, ${longPressDelay});`;
|
||||
const cancel = 'if (!!t) clearTimeout(t); t=null';
|
||||
const touchEnd = 'if (!!t) clearTimeout(t); t=null';
|
||||
|
||||
js = ` ontouchstart="${touchStart}" ontouchend="${cancel}" ontouchcancel="${cancel}" ontouchmove="${cancel}"`;
|
||||
js = ` ontouchstart="${touchStart}" ontouchend="${touchEnd}"`;
|
||||
}
|
||||
|
||||
return `<img data-from-md ${htmlUtils.attributesHtml(Object.assign({}, r, { title: title }))}${js}/>`;
|
||||
|
||||
@@ -71,10 +71,11 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
|
||||
const onLongClick = `${ruleOptions.postMessageSyntax}("longclick:${resourceId}")`;
|
||||
|
||||
const touchStart = `t=setTimeout(()=>{t=null; ${onLongClick};}, ${longPressDelay});`;
|
||||
const cancel = 'if (!!t) {clearTimeout(t); t=null;';
|
||||
const touchEnd = `${cancel} ${onClick};}`;
|
||||
const touchEnd = `if (!!t) {clearTimeout(t); t=null; ${onClick};}`;
|
||||
|
||||
js = `ontouchstart='${touchStart}' ontouchend='${touchEnd}' ontouchcancel='${cancel} ontouchmove="${cancel}'`;
|
||||
js = `ontouchstart='${touchStart}' ontouchend='${touchEnd}'`;
|
||||
} else {
|
||||
js = `onclick='${js}'`;
|
||||
}
|
||||
|
||||
if (hrefAttr.indexOf('#') === 0 && href.indexOf('#') === 0) js = ''; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place
|
||||
|
||||
@@ -738,6 +738,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz",
|
||||
"integrity": "sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ="
|
||||
},
|
||||
"lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
|
||||
},
|
||||
"lower-case": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
|
||||
@@ -915,6 +920,14 @@
|
||||
"lower-case": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node-emoji": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
|
||||
"integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
|
||||
"requires": {
|
||||
"lodash.toarray": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -966,11 +979,6 @@
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"slug": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/slug/-/slug-3.5.1.tgz",
|
||||
"integrity": "sha512-ei0JnJzg8HKhLunZy+vpNlILRRradfaAQ+p2YEI4b4r8yX/5TlFi1JSwcYQCg7INZxdTC43BT68rHMkRxzn7Xg=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
@@ -1057,11 +1065,24 @@
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
},
|
||||
"unorm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
|
||||
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
|
||||
},
|
||||
"upper-case": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
|
||||
"integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
|
||||
},
|
||||
"uslug": {
|
||||
"version": "git+https://github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
|
||||
"from": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"requires": {
|
||||
"node-emoji": "^1.10.0",
|
||||
"unorm": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
||||
@@ -37,6 +37,6 @@
|
||||
"markdown-it-toc-done-right": "^4.1.0",
|
||||
"md5": "^2.2.1",
|
||||
"mermaid": "^8.8.1",
|
||||
"slug": "^3.5.0"
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import produce, { Draft } from 'immer';
|
||||
import pluginServiceReducer, { stateRootKey as pluginServiceStateRootKey, defaultState as pluginServiceDefaultState, State as PluginServiceState } from 'lib/services/plugins/reducer';
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids');
|
||||
const { createSelectorCreator, defaultMemoize } = require('reselect');
|
||||
@@ -160,8 +161,6 @@ for (const additionalReducer of additionalReducers) {
|
||||
|
||||
export const MAX_HISTORY = 200;
|
||||
|
||||
export const stateUtils:any = {};
|
||||
|
||||
const derivedStateCache_:any = {};
|
||||
|
||||
// Allows, for a given state, to return the same derived
|
||||
@@ -185,9 +184,7 @@ const createShallowArrayEqualSelector = createSelectorCreator(
|
||||
}
|
||||
);
|
||||
|
||||
// Given an input array, this selector ensures that the same array is returned
|
||||
// if its content hasn't changed.
|
||||
stateUtils.selectArrayShallow = createCachedSelector(
|
||||
const selectArrayShallow = createCachedSelector(
|
||||
(state:any) => state.array,
|
||||
(array:any[]) => array
|
||||
)({
|
||||
@@ -197,85 +194,85 @@ stateUtils.selectArrayShallow = createCachedSelector(
|
||||
selectorCreator: createShallowArrayEqualSelector,
|
||||
});
|
||||
|
||||
stateUtils.hasOneSelectedNote = function(state:State):boolean {
|
||||
return state.selectedNoteIds.length === 1;
|
||||
};
|
||||
class StateUtils {
|
||||
|
||||
stateUtils.notesOrder = function(stateSettings:any) {
|
||||
if (stateSettings['notes.sortOrder.field'] === 'order') {
|
||||
return cacheEnabledOutput('notesOrder', [
|
||||
{
|
||||
by: 'order',
|
||||
dir: 'DESC',
|
||||
},
|
||||
{
|
||||
by: 'user_created_time',
|
||||
dir: 'DESC',
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
return cacheEnabledOutput('notesOrder', [
|
||||
{
|
||||
by: stateSettings['notes.sortOrder.field'],
|
||||
dir: stateSettings['notes.sortOrder.reverse'] ? 'DESC' : 'ASC',
|
||||
},
|
||||
]);
|
||||
// Given an input array, this selector ensures that the same array is returned
|
||||
// if its content hasn't changed.
|
||||
public selectArrayShallow(props:any, cacheKey:any) {
|
||||
return selectArrayShallow(props, cacheKey);
|
||||
}
|
||||
};
|
||||
|
||||
stateUtils.foldersOrder = function(stateSettings:any) {
|
||||
return cacheEnabledOutput('foldersOrder', [
|
||||
{
|
||||
by: stateSettings['folders.sortOrder.field'],
|
||||
dir: stateSettings['folders.sortOrder.reverse'] ? 'DESC' : 'ASC',
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
stateUtils.hasNotesBeingSaved = function(state:State):boolean {
|
||||
for (const id in state.editorNoteStatuses) {
|
||||
if (state.editorNoteStatuses[id] === 'saving') return true;
|
||||
public oneNoteSelected(state:State):boolean {
|
||||
return state.selectedNoteIds.length === 1;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
stateUtils.parentItem = function(state:State) {
|
||||
const t = state.notesParentType;
|
||||
let id = null;
|
||||
if (t === 'Folder') id = state.selectedFolderId;
|
||||
if (t === 'Tag') id = state.selectedTagId;
|
||||
if (t === 'Search') id = state.selectedSearchId;
|
||||
if (!t || !id) return null;
|
||||
return { type: t, id: id };
|
||||
};
|
||||
|
||||
stateUtils.lastSelectedNoteIds = function(state:State):string[] {
|
||||
const parent = stateUtils.parentItem(state);
|
||||
if (!parent) return [];
|
||||
const output = (state.lastSelectedNotesIds as any)[parent.type][parent.id];
|
||||
return output ? output : [];
|
||||
};
|
||||
|
||||
stateUtils.getCurrentNote = function(state:State) {
|
||||
const selectedNoteIds = state.selectedNoteIds;
|
||||
const notes = state.notes;
|
||||
if (selectedNoteIds != null && selectedNoteIds.length > 0) {
|
||||
const currNote = notes.find(note => note.id === selectedNoteIds[0]);
|
||||
if (currNote != null) {
|
||||
return {
|
||||
id: currNote.id,
|
||||
parent_id: currNote.parent_id,
|
||||
notesParentType: state.notesParentType,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
searches: state.searches,
|
||||
selectedSmartFilterId: state.selectedSmartFilterId,
|
||||
};
|
||||
public notesOrder(stateSettings:any) {
|
||||
if (stateSettings['notes.sortOrder.field'] === 'order') {
|
||||
return cacheEnabledOutput('notesOrder', [
|
||||
{
|
||||
by: 'order',
|
||||
dir: 'DESC',
|
||||
},
|
||||
{
|
||||
by: 'user_created_time',
|
||||
dir: 'DESC',
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
return cacheEnabledOutput('notesOrder', [
|
||||
{
|
||||
by: stateSettings['notes.sortOrder.field'],
|
||||
dir: stateSettings['notes.sortOrder.reverse'] ? 'DESC' : 'ASC',
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
public foldersOrder(stateSettings:any) {
|
||||
return cacheEnabledOutput('foldersOrder', [
|
||||
{
|
||||
by: stateSettings['folders.sortOrder.field'],
|
||||
dir: stateSettings['folders.sortOrder.reverse'] ? 'DESC' : 'ASC',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public hasNotesBeingSaved(state:State):boolean {
|
||||
for (const id in state.editorNoteStatuses) {
|
||||
if (state.editorNoteStatuses[id] === 'saving') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public parentItem(state:State) {
|
||||
const t = state.notesParentType;
|
||||
let id = null;
|
||||
if (t === 'Folder') id = state.selectedFolderId;
|
||||
if (t === 'Tag') id = state.selectedTagId;
|
||||
if (t === 'Search') id = state.selectedSearchId;
|
||||
if (!t || !id) return null;
|
||||
return { type: t, id: id };
|
||||
}
|
||||
|
||||
public lastSelectedNoteIds(state:State):string[] {
|
||||
const parent = this.parentItem(state);
|
||||
if (!parent) return [];
|
||||
const output = (state.lastSelectedNotesIds as any)[parent.type][parent.id];
|
||||
return output ? output : [];
|
||||
}
|
||||
|
||||
public selectedNote(state:State):any {
|
||||
const noteId = this.selectedNoteId(state);
|
||||
return noteId ? BaseModel.byId(state.notes, noteId) : null;
|
||||
}
|
||||
|
||||
public selectedNoteId(state:State):any {
|
||||
return state.selectedNoteIds.length ? state.selectedNoteIds[0] : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const stateUtils:StateUtils = new StateUtils();
|
||||
|
||||
function arrayHasEncryptedItems(array:any[]) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
@@ -526,8 +523,29 @@ const getContextFromHistory = (ctx:any) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
function getNoteHistoryInfo(state:State) {
|
||||
const selectedNoteIds = state.selectedNoteIds;
|
||||
const notes = state.notes;
|
||||
if (selectedNoteIds != null && selectedNoteIds.length > 0) {
|
||||
const currNote = notes.find(note => note.id === selectedNoteIds[0]);
|
||||
if (currNote != null) {
|
||||
return {
|
||||
id: currNote.id,
|
||||
parent_id: currNote.parent_id,
|
||||
notesParentType: state.notesParentType,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
searches: state.searches,
|
||||
selectedSmartFilterId: state.selectedSmartFilterId,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function handleHistory(draft:Draft<State>, action:any) {
|
||||
const currentNote = stateUtils.getCurrentNote(draft);
|
||||
const currentNote = getNoteHistoryInfo(draft);
|
||||
switch (action.type) {
|
||||
case 'HISTORY_BACKWARD': {
|
||||
const note = draft.backwardHistoryNotes[draft.backwardHistoryNotes.length - 1];
|
||||
@@ -1086,6 +1104,7 @@ const reducer = produce((draft: Draft<State> = defaultState, action:any) => {
|
||||
const newPluginsLegacy = Object.assign({}, draft.pluginsLegacy);
|
||||
const newPlugin = draft.pluginsLegacy[action.pluginName] ? Object.assign({}, draft.pluginsLegacy[action.pluginName]) : {};
|
||||
if ('open' in action) newPlugin.dialogOpen = action.open;
|
||||
if ('userData' in action) newPlugin.userData = action.userData;
|
||||
newPluginsLegacy[action.pluginName] = newPlugin;
|
||||
draft.pluginsLegacy = newPluginsLegacy;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
import { State } from 'lib/reducer';
|
||||
import eventManager from 'lib/eventManager';
|
||||
import markdownUtils, { MarkdownTableHeader, MarkdownTableRow } from 'lib/markdownUtils';
|
||||
import BaseService from 'lib/services/BaseService';
|
||||
import shim from 'lib/shim';
|
||||
import WhenClause from './WhenClause';
|
||||
import stateToWhenClauseContext from './commands/stateToWhenClauseContext';
|
||||
|
||||
type LabelFunction = () => string;
|
||||
type EnabledCondition = string;
|
||||
|
||||
export interface CommandContext {
|
||||
// The state may also be of type "AppState" (used by the desktop app), which inherits from "State" (used by all apps)
|
||||
state: State,
|
||||
}
|
||||
|
||||
export interface CommandRuntime {
|
||||
execute(props:any):Promise<any>
|
||||
isEnabled?(props:any):boolean
|
||||
|
||||
// "state" type is "AppState" but in order not to introduce a
|
||||
// dependency to the desktop app (so that the service can
|
||||
// potentially be used by the mobile app too), we keep it as "any".
|
||||
// Individual commands can define it as state:AppState when relevant.
|
||||
//
|
||||
// In general this method should reduce the provided state to only
|
||||
// what's absolutely necessary. For example, if the property of a
|
||||
// note is needed, return only that particular property and not the
|
||||
// whole note object. This will ensure that components that depends
|
||||
// on this command are not uncessarily re-rendered. A note object for
|
||||
// example might change frequently but its markdown_language property
|
||||
// will almost never change.
|
||||
mapStateToProps?(state:any):any
|
||||
|
||||
execute(context:CommandContext, ...args:any[]):Promise<any>
|
||||
enabledCondition?: EnabledCondition;
|
||||
// Used for the (optional) toolbar button title
|
||||
title?(props:any):string,
|
||||
mapStateToTitle?(state:any):string,
|
||||
}
|
||||
|
||||
export interface CommandDeclaration {
|
||||
@@ -33,6 +26,9 @@ export interface CommandDeclaration {
|
||||
// Used for the menu item label, and toolbar button tooltip
|
||||
label?: LabelFunction | string,
|
||||
|
||||
// Command description - if none is provided, the label will be used as description
|
||||
description?: string,
|
||||
|
||||
// This is a bit of a hack because some labels don't make much sense in isolation. For example,
|
||||
// the commmand to focus the note list is called just "Note list". This makes sense within the menu
|
||||
// but not so much within the keymap config screen, where the parent item is not displayed. Because
|
||||
@@ -88,30 +84,29 @@ interface CommandByNameOptions {
|
||||
runtimeMustBeRegistered?:boolean,
|
||||
}
|
||||
|
||||
interface CommandState {
|
||||
export interface SearchResult {
|
||||
commandName: string,
|
||||
title: string,
|
||||
enabled: boolean,
|
||||
}
|
||||
|
||||
interface CommandStates {
|
||||
[key:string]: CommandState
|
||||
}
|
||||
|
||||
export default class CommandService extends BaseService {
|
||||
|
||||
private static instance_:CommandService;
|
||||
|
||||
static instance():CommandService {
|
||||
public static instance():CommandService {
|
||||
if (this.instance_) return this.instance_;
|
||||
this.instance_ = new CommandService();
|
||||
return this.instance_;
|
||||
}
|
||||
|
||||
private commands_:Commands = {};
|
||||
private commandPreviousStates_:CommandStates = {};
|
||||
private store_:any;
|
||||
private devMode_:boolean;
|
||||
|
||||
initialize(store:any) {
|
||||
public initialize(store:any, devMode:boolean) {
|
||||
utils.store = store;
|
||||
this.store_ = store;
|
||||
this.devMode_ = devMode;
|
||||
}
|
||||
|
||||
public on(eventName:string, callback:Function) {
|
||||
@@ -122,6 +117,36 @@ export default class CommandService extends BaseService {
|
||||
eventManager.off(eventName, callback);
|
||||
}
|
||||
|
||||
public searchCommands(query:string, returnAllWhenEmpty:boolean, excludeWithoutLabel:boolean = true):SearchResult[] {
|
||||
query = query.toLowerCase();
|
||||
|
||||
const output = [];
|
||||
|
||||
for (const commandName of this.commandNames()) {
|
||||
const label = this.label(commandName, true);
|
||||
if (!label && excludeWithoutLabel) continue;
|
||||
|
||||
const title = label ? `${label} (${commandName})` : commandName;
|
||||
|
||||
if ((returnAllWhenEmpty && !query) || title.toLowerCase().includes(query)) {
|
||||
output.push({
|
||||
commandName: commandName,
|
||||
title: title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
output.sort((a:SearchResult, b:SearchResult) => {
|
||||
return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : +1;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public commandNames() {
|
||||
return Object.keys(this.commands_);
|
||||
}
|
||||
|
||||
public commandByName(name:string, options:CommandByNameOptions = null):Command {
|
||||
options = {
|
||||
mustExist: true,
|
||||
@@ -140,7 +165,7 @@ export default class CommandService extends BaseService {
|
||||
return command;
|
||||
}
|
||||
|
||||
registerDeclaration(declaration:CommandDeclaration) {
|
||||
public registerDeclaration(declaration:CommandDeclaration) {
|
||||
declaration = { ...declaration };
|
||||
if (!declaration.label) declaration.label = '';
|
||||
if (!declaration.iconName) declaration.iconName = '';
|
||||
@@ -148,83 +173,89 @@ export default class CommandService extends BaseService {
|
||||
this.commands_[declaration.name] = {
|
||||
declaration: declaration,
|
||||
};
|
||||
|
||||
delete this.commandPreviousStates_[declaration.name];
|
||||
}
|
||||
|
||||
registerRuntime(commandName:string, runtime:CommandRuntime) {
|
||||
public registerRuntime(commandName:string, runtime:CommandRuntime) {
|
||||
if (typeof commandName !== 'string') throw new Error(`Command name must be a string. Got: ${JSON.stringify(commandName)}`);
|
||||
|
||||
const command = this.commandByName(commandName);
|
||||
|
||||
runtime = Object.assign({}, runtime);
|
||||
if (!runtime.isEnabled) runtime.isEnabled = () => true;
|
||||
if (!runtime.title) runtime.title = () => null;
|
||||
if (!runtime.enabledCondition) runtime.enabledCondition = 'true';
|
||||
command.runtime = runtime;
|
||||
|
||||
delete this.commandPreviousStates_[commandName];
|
||||
}
|
||||
|
||||
componentRegisterCommands(component:any, commands:any[]) {
|
||||
public componentRegisterCommands(component:any, commands:any[]) {
|
||||
for (const command of commands) {
|
||||
CommandService.instance().registerRuntime(command.declaration.name, command.runtime(component));
|
||||
}
|
||||
}
|
||||
|
||||
componentUnregisterCommands(commands:any[]) {
|
||||
public componentUnregisterCommands(commands:any[]) {
|
||||
for (const command of commands) {
|
||||
CommandService.instance().unregisterRuntime(command.declaration.name);
|
||||
}
|
||||
}
|
||||
|
||||
unregisterRuntime(commandName:string) {
|
||||
public unregisterRuntime(commandName:string) {
|
||||
const command = this.commandByName(commandName, { mustExist: false });
|
||||
if (!command || !command.runtime) return;
|
||||
delete command.runtime;
|
||||
|
||||
delete this.commandPreviousStates_[commandName];
|
||||
}
|
||||
|
||||
async execute(commandName:string, props:any = null):Promise<any> {
|
||||
public async execute(commandName:string, ...args:any[]):Promise<any> {
|
||||
const command = this.commandByName(commandName);
|
||||
this.logger().info('CommandService::execute:', commandName, props);
|
||||
return command.runtime.execute(props ? props : {});
|
||||
this.logger().info('CommandService::execute:', commandName, args);
|
||||
return command.runtime.execute({ state: this.store_.getState() }, ...args);
|
||||
}
|
||||
|
||||
scheduleExecute(commandName:string, args:any) {
|
||||
public scheduleExecute(commandName:string, args:any) {
|
||||
shim.setTimeout(() => {
|
||||
this.execute(commandName, args);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
isEnabled(commandName:string, props:any):boolean {
|
||||
public currentWhenClauseContext() {
|
||||
return stateToWhenClauseContext(this.store_.getState());
|
||||
}
|
||||
|
||||
// When looping on commands and checking their enabled state, the whenClauseContext
|
||||
// should be specified (created using currentWhenClauseContext) to avoid having
|
||||
// to re-create it on each call.
|
||||
public isEnabled(commandName:string, whenClauseContext:any = null):boolean {
|
||||
const command = this.commandByName(commandName);
|
||||
if (!command || !command.runtime) return false;
|
||||
// if (!command.runtime.props) return false;
|
||||
return command.runtime.isEnabled(props);
|
||||
|
||||
if (!whenClauseContext) whenClauseContext = this.currentWhenClauseContext();
|
||||
|
||||
const exp = new WhenClause(command.runtime.enabledCondition, this.devMode_);
|
||||
return exp.evaluate(whenClauseContext);
|
||||
}
|
||||
|
||||
commandMapStateToProps(commandName:string, state:any):any {
|
||||
const command = this.commandByName(commandName);
|
||||
if (!command.runtime) return null;
|
||||
if (!command.runtime.mapStateToProps) return {};
|
||||
return command.runtime.mapStateToProps(state);
|
||||
}
|
||||
|
||||
title(commandName:string, props:any):string {
|
||||
// The title is dynamic and derived from the state, which is why the state is passed
|
||||
// as an argument. Title can be used for example to display the alarm date on the
|
||||
// "set alarm" toolbar button.
|
||||
public title(commandName:string, state:any = null):string {
|
||||
const command = this.commandByName(commandName);
|
||||
if (!command || !command.runtime) return null;
|
||||
return command.runtime.title(props);
|
||||
|
||||
state = state || this.store_.getState();
|
||||
|
||||
if (command.runtime.mapStateToTitle) {
|
||||
return command.runtime.mapStateToTitle(state);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
iconName(commandName:string, variant:string = null):string {
|
||||
public iconName(commandName:string, variant:string = null):string {
|
||||
const command = this.commandByName(commandName);
|
||||
if (!command) throw new Error(`No such command: ${commandName}`);
|
||||
if (variant === 'tinymce') return command.declaration.tinymceIconName ? command.declaration.tinymceIconName : 'preferences';
|
||||
return command.declaration.iconName;
|
||||
}
|
||||
|
||||
label(commandName:string, fullLabel:boolean = false):string {
|
||||
public label(commandName:string, fullLabel:boolean = false):string {
|
||||
const command = this.commandByName(commandName);
|
||||
if (!command) throw new Error(`Command: ${commandName} is not declared`);
|
||||
const output = [];
|
||||
@@ -240,42 +271,15 @@ export default class CommandService extends BaseService {
|
||||
return output.join(': ');
|
||||
}
|
||||
|
||||
exists(commandName:string):boolean {
|
||||
public description(commandName:string):string {
|
||||
const command = this.commandByName(commandName);
|
||||
if (command.declaration.description) return command.declaration.description;
|
||||
return this.label(commandName, true);
|
||||
}
|
||||
|
||||
public exists(commandName:string):boolean {
|
||||
const command = this.commandByName(commandName, { mustExist: false });
|
||||
return !!command;
|
||||
}
|
||||
|
||||
public commandsToMarkdownTable(state:any):string {
|
||||
const headers:MarkdownTableHeader[] = [
|
||||
{
|
||||
name: 'commandName',
|
||||
label: 'Name',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
},
|
||||
{
|
||||
name: 'props',
|
||||
label: 'Props',
|
||||
},
|
||||
];
|
||||
|
||||
const rows:MarkdownTableRow[] = [];
|
||||
|
||||
for (const commandName in this.commands_) {
|
||||
const props = this.commandMapStateToProps(commandName, state);
|
||||
|
||||
const row:MarkdownTableRow = {
|
||||
commandName: commandName,
|
||||
description: this.label(commandName),
|
||||
props: JSON.stringify(props),
|
||||
};
|
||||
|
||||
rows.push(row);
|
||||
}
|
||||
|
||||
return markdownUtils.createMarkdownTable(headers, rows);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const defaultKeymapItems = {
|
||||
{ accelerator: 'Cmd+N', command: 'newNote' },
|
||||
{ accelerator: 'Cmd+T', command: 'newTodo' },
|
||||
{ accelerator: 'Cmd+S', command: 'synchronize' },
|
||||
{ accelerator: 'Cmd+P', command: 'print' },
|
||||
{ accelerator: '', command: 'print' },
|
||||
{ accelerator: 'Cmd+H', command: 'hideApp' },
|
||||
{ accelerator: 'Cmd+Q', command: 'quit' },
|
||||
{ accelerator: 'Cmd+,', command: 'config' },
|
||||
@@ -37,20 +37,21 @@ const defaultKeymapItems = {
|
||||
{ accelerator: 'Shift+Cmd+L', command: 'focusElementNoteList' },
|
||||
{ accelerator: 'Shift+Cmd+N', command: 'focusElementNoteTitle' },
|
||||
{ accelerator: 'Shift+Cmd+B', command: 'focusElementNoteBody' },
|
||||
{ accelerator: 'Option+Cmd+S', command: 'toggleSidebar' },
|
||||
{ accelerator: 'Option+Cmd+S', command: 'toggleSideBar' },
|
||||
{ accelerator: 'Option+Cmd+L', command: 'toggleNoteList' },
|
||||
{ accelerator: 'Cmd+L', command: 'toggleVisiblePanes' },
|
||||
{ accelerator: 'Cmd+0', command: 'zoomActualSize' },
|
||||
{ accelerator: 'Cmd+E', command: 'toggleExternalEditing' },
|
||||
{ accelerator: 'Option+Cmd+T', command: 'setTags' },
|
||||
{ accelerator: 'Cmd+G', command: 'gotoAnything' },
|
||||
{ accelerator: 'Cmd+P', command: 'gotoAnything' },
|
||||
{ accelerator: 'Shift+Cmd+P', command: 'commandPalette' },
|
||||
{ accelerator: 'F1', command: 'help' },
|
||||
],
|
||||
default: [
|
||||
{ accelerator: 'Ctrl+N', command: 'newNote' },
|
||||
{ accelerator: 'Ctrl+T', command: 'newTodo' },
|
||||
{ accelerator: 'Ctrl+S', command: 'synchronize' },
|
||||
{ accelerator: 'Ctrl+P', command: 'print' },
|
||||
{ accelerator: '', command: 'print' },
|
||||
{ accelerator: 'Ctrl+Q', command: 'quit' },
|
||||
{ accelerator: 'Ctrl+Alt+I', command: 'insertTemplate' },
|
||||
{ accelerator: 'Ctrl+C', command: 'textCopy' },
|
||||
@@ -68,14 +69,15 @@ const defaultKeymapItems = {
|
||||
{ accelerator: 'Ctrl+Shift+L', command: 'focusElementNoteList' },
|
||||
{ accelerator: 'Ctrl+Shift+N', command: 'focusElementNoteTitle' },
|
||||
{ accelerator: 'Ctrl+Shift+B', command: 'focusElementNoteBody' },
|
||||
{ accelerator: 'F10', command: 'toggleSidebar' },
|
||||
{ accelerator: 'F10', command: 'toggleSideBar' },
|
||||
{ accelerator: 'F11', command: 'toggleNoteList' },
|
||||
{ accelerator: 'Ctrl+L', command: 'toggleVisiblePanes' },
|
||||
{ accelerator: 'Ctrl+0', command: 'zoomActualSize' },
|
||||
{ accelerator: 'Ctrl+E', command: 'toggleExternalEditing' },
|
||||
{ accelerator: 'Ctrl+Alt+T', command: 'setTags' },
|
||||
{ accelerator: 'Ctrl+,', command: 'config' },
|
||||
{ accelerator: 'Ctrl+G', command: 'gotoAnything' },
|
||||
{ accelerator: 'Ctrl+P', command: 'gotoAnything' },
|
||||
{ accelerator: 'Ctrl+Shift+P', command: 'commandPalette' },
|
||||
{ accelerator: 'F1', command: 'help' },
|
||||
],
|
||||
};
|
||||
@@ -90,13 +92,14 @@ interface Keymap {
|
||||
}
|
||||
|
||||
export default class KeymapService extends BaseService {
|
||||
|
||||
private keymap: Keymap;
|
||||
private platform: string;
|
||||
private customKeymapPath: string;
|
||||
private defaultKeymapItems: KeymapItem[];
|
||||
private lastSaveTime_:number;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.lastSaveTime_ = Date.now();
|
||||
@@ -106,11 +109,11 @@ export default class KeymapService extends BaseService {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
get lastSaveTime():number {
|
||||
public get lastSaveTime():number {
|
||||
return this.lastSaveTime_;
|
||||
}
|
||||
|
||||
initialize(platform: string = shim.platformName()) {
|
||||
public initialize(platform: string = shim.platformName()) {
|
||||
this.platform = platform;
|
||||
|
||||
switch (platform) {
|
||||
@@ -131,7 +134,7 @@ export default class KeymapService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
async loadCustomKeymap(customKeymapPath: string) {
|
||||
public async loadCustomKeymap(customKeymapPath: string) {
|
||||
this.customKeymapPath = customKeymapPath; // Useful for saving the changes later
|
||||
|
||||
if (await shim.fsDriver().exists(customKeymapPath)) {
|
||||
@@ -143,7 +146,7 @@ export default class KeymapService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
async saveCustomKeymap(customKeymapPath: string = this.customKeymapPath) {
|
||||
public async saveCustomKeymap(customKeymapPath: string = this.customKeymapPath) {
|
||||
this.logger().info(`KeymapService: Saving keymap to file: ${customKeymapPath}`);
|
||||
|
||||
try {
|
||||
@@ -161,7 +164,7 @@ export default class KeymapService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
acceleratorExists(command: string) {
|
||||
public acceleratorExists(command: string) {
|
||||
return !!this.keymap[command];
|
||||
}
|
||||
|
||||
@@ -189,33 +192,33 @@ export default class KeymapService extends BaseService {
|
||||
};
|
||||
}
|
||||
|
||||
setAccelerator(command: string, accelerator: string) {
|
||||
public setAccelerator(command: string, accelerator: string) {
|
||||
this.keymap[command].accelerator = accelerator;
|
||||
}
|
||||
|
||||
getAccelerator(command: string) {
|
||||
public getAccelerator(command: string) {
|
||||
const item = this.keymap[command];
|
||||
if (!item) throw new Error(`KeymapService: "${command}" command does not exist!`);
|
||||
|
||||
return item.accelerator;
|
||||
}
|
||||
|
||||
getDefaultAccelerator(command: string) {
|
||||
public getDefaultAccelerator(command: string) {
|
||||
const defaultItem = this.defaultKeymapItems.find((item => item.command === command));
|
||||
if (!defaultItem) throw new Error(`KeymapService: "${command}" command does not exist!`);
|
||||
|
||||
return defaultItem.accelerator;
|
||||
}
|
||||
|
||||
getCommandNames() {
|
||||
public getCommandNames() {
|
||||
return Object.keys(this.keymap);
|
||||
}
|
||||
|
||||
getKeymapItems() {
|
||||
public getKeymapItems() {
|
||||
return Object.values(this.keymap);
|
||||
}
|
||||
|
||||
getCustomKeymapItems() {
|
||||
public getCustomKeymapItems() {
|
||||
const customkeymapItems: KeymapItem[] = [];
|
||||
this.defaultKeymapItems.forEach(({ command, accelerator }) => {
|
||||
const currentAccelerator = this.getAccelerator(command);
|
||||
@@ -236,11 +239,11 @@ export default class KeymapService extends BaseService {
|
||||
return customkeymapItems;
|
||||
}
|
||||
|
||||
getDefaultKeymapItems() {
|
||||
public getDefaultKeymapItems() {
|
||||
return [...this.defaultKeymapItems];
|
||||
}
|
||||
|
||||
overrideKeymap(customKeymapItems: KeymapItem[]) {
|
||||
public overrideKeymap(customKeymapItems: KeymapItem[]) {
|
||||
try {
|
||||
for (let i = 0; i < customKeymapItems.length; i++) {
|
||||
const item = customKeymapItems[i];
|
||||
@@ -284,7 +287,7 @@ export default class KeymapService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
validateKeymap(proposedKeymapItem: KeymapItem = null) {
|
||||
public validateKeymap(proposedKeymapItem: KeymapItem = null) {
|
||||
const usedAccelerators = new Set();
|
||||
|
||||
// Validate as if the proposed change is already present in the current keymap
|
||||
@@ -312,7 +315,7 @@ export default class KeymapService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
validateAccelerator(accelerator: string) {
|
||||
public validateAccelerator(accelerator: string) {
|
||||
let keyFound = false;
|
||||
|
||||
const parts = accelerator.split('+');
|
||||
@@ -334,7 +337,7 @@ export default class KeymapService extends BaseService {
|
||||
if (!isValid) throw new Error(_('Accelerator "%s" is not valid.', accelerator));
|
||||
}
|
||||
|
||||
domToElectronAccelerator(event: KeyboardEvent<HTMLDivElement>) {
|
||||
public domToElectronAccelerator(event: KeyboardEvent<HTMLDivElement>) {
|
||||
const parts = [];
|
||||
const { key, ctrlKey, metaKey, altKey, shiftKey } = event;
|
||||
|
||||
@@ -358,7 +361,7 @@ export default class KeymapService extends BaseService {
|
||||
return parts.join('+');
|
||||
}
|
||||
|
||||
static domToElectronKey(domKey: string) {
|
||||
private static domToElectronKey(domKey: string) {
|
||||
let electronKey;
|
||||
|
||||
if (/^([a-z])$/.test(domKey)) {
|
||||
@@ -398,7 +401,7 @@ export default class KeymapService extends BaseService {
|
||||
|
||||
private static instance_:KeymapService = null;
|
||||
|
||||
static instance():KeymapService {
|
||||
public static instance():KeymapService {
|
||||
if (this.instance_) return this.instance_;
|
||||
|
||||
this.instance_ = new KeymapService();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const Logger = require('lib/Logger').default;
|
||||
const KeymapService = require('lib/services/KeymapService').default;
|
||||
|
||||
class PluginManager {
|
||||
constructor() {
|
||||
@@ -52,6 +51,7 @@ class PluginManager {
|
||||
const p = this.pluginInstance_(event.pluginName);
|
||||
p.onTrigger({
|
||||
itemName: event.itemName,
|
||||
userData: event.userData,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class PluginManager {
|
||||
|
||||
return {
|
||||
Dialog: Class.Dialog,
|
||||
props: this.dialogProps_(name),
|
||||
props: Object.assign({}, this.dialogProps_(name), { userData: p.userData }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,20 +81,24 @@ class PluginManager {
|
||||
|
||||
menuItems() {
|
||||
let output = [];
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
for (const name in this.plugins_) {
|
||||
const menuItems = this.plugins_[name].Class.manifest.menuItems;
|
||||
const menuItems = this.plugins_[name].Class.manifest.menuItems.slice();
|
||||
if (!menuItems) continue;
|
||||
|
||||
for (const item of menuItems) {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
const item = Object.assign({}, menuItems[i]);
|
||||
|
||||
item.click = () => {
|
||||
this.onPluginMenuItemTrigger_({
|
||||
pluginName: name,
|
||||
itemName: item.name,
|
||||
userData: item.userData,
|
||||
});
|
||||
};
|
||||
item.accelerator = keymapService.getAccelerator(name);
|
||||
|
||||
item.accelerator = menuItems[i].accelerator();
|
||||
|
||||
menuItems[i] = item;
|
||||
}
|
||||
|
||||
output = output.concat(menuItems);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { ContextKeyExpr, ContextKeyExpression } from './contextkey/contextkey';
|
||||
|
||||
export default class BooleanExpression {
|
||||
export default class WhenClause {
|
||||
|
||||
private expression_:string;
|
||||
private validate_:boolean;
|
||||
private rules_:ContextKeyExpression = null;
|
||||
|
||||
constructor(expression:string) {
|
||||
constructor(expression:string, validate:boolean) {
|
||||
this.expression_ = expression;
|
||||
this.validate_ = validate;
|
||||
}
|
||||
|
||||
private createContext(ctx: any) {
|
||||
@@ -21,11 +23,20 @@ export default class BooleanExpression {
|
||||
if (!this.rules_) {
|
||||
this.rules_ = ContextKeyExpr.deserialize(this.expression_);
|
||||
}
|
||||
|
||||
return this.rules_;
|
||||
}
|
||||
|
||||
public evaluate(context:any):boolean {
|
||||
if (this.validate_) this.validate(context);
|
||||
return this.rules.evaluate(this.createContext(context));
|
||||
}
|
||||
|
||||
public validate(context:any) {
|
||||
const keys = this.rules.keys();
|
||||
for (const key of keys) {
|
||||
if (!(key in context)) throw new Error(`No such key: ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,9 +87,9 @@ export default class MenuUtils {
|
||||
return item;
|
||||
}
|
||||
|
||||
public commandToStatefulMenuItem(commandName:string, props:any = null):MenuItem {
|
||||
public commandToStatefulMenuItem(commandName:string, ...args:any[]):MenuItem {
|
||||
return this.commandToMenuItem(commandName, () => {
|
||||
return this.service.execute(commandName, props ? props : {});
|
||||
return this.service.execute(commandName, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,11 +108,14 @@ export default class MenuUtils {
|
||||
return output;
|
||||
}
|
||||
|
||||
public commandsToMenuItemProps(state:any, commandNames:string[]):MenuItemProps {
|
||||
public commandsToMenuItemProps(commandNames:string[], whenClauseContext:any):MenuItemProps {
|
||||
const output:MenuItemProps = {};
|
||||
|
||||
for (const commandName of commandNames) {
|
||||
const newProps = this.service.commandMapStateToProps(commandName, state);
|
||||
const newProps = {
|
||||
enabled: this.service.isEnabled(commandName, whenClauseContext),
|
||||
};
|
||||
|
||||
if (newProps === null || propsHaveChanged(this.menuItemPropsCache_[commandName], newProps)) {
|
||||
output[commandName] = newProps;
|
||||
this.menuItemPropsCache_[commandName] = newProps;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import CommandService from '../CommandService';
|
||||
import propsHaveChanged from './propsHaveChanged';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
|
||||
const separatorItem = { type: 'separator' };
|
||||
@@ -14,7 +13,6 @@ export interface ToolbarButtonInfo {
|
||||
}
|
||||
|
||||
interface ToolbarButtonCacheItem {
|
||||
props: any,
|
||||
info: ToolbarButtonInfo,
|
||||
}
|
||||
|
||||
@@ -35,8 +33,15 @@ export default class ToolbarButtonUtils {
|
||||
return this.service_;
|
||||
}
|
||||
|
||||
private commandToToolbarButton(commandName:string, props:any):ToolbarButtonInfo {
|
||||
if (this.toolbarButtonCache_[commandName] && !propsHaveChanged(this.toolbarButtonCache_[commandName].props, props)) {
|
||||
private commandToToolbarButton(commandName:string, whenClauseContext:any):ToolbarButtonInfo {
|
||||
const newEnabled = this.service.isEnabled(commandName, whenClauseContext);
|
||||
const newTitle = this.service.title(commandName);
|
||||
|
||||
if (
|
||||
this.toolbarButtonCache_[commandName] &&
|
||||
this.toolbarButtonCache_[commandName].info.enabled === newEnabled &&
|
||||
this.toolbarButtonCache_[commandName].info.title === newTitle
|
||||
) {
|
||||
return this.toolbarButtonCache_[commandName].info;
|
||||
}
|
||||
|
||||
@@ -46,15 +51,14 @@ export default class ToolbarButtonUtils {
|
||||
name: commandName,
|
||||
tooltip: this.service.label(commandName),
|
||||
iconName: command.declaration.iconName,
|
||||
enabled: this.service.isEnabled(commandName, props),
|
||||
enabled: newEnabled,
|
||||
onClick: async () => {
|
||||
this.service.execute(commandName, props);
|
||||
this.service.execute(commandName);
|
||||
},
|
||||
title: this.service.title(commandName, props),
|
||||
title: newTitle,
|
||||
};
|
||||
|
||||
this.toolbarButtonCache_[commandName] = {
|
||||
props: props,
|
||||
info: output,
|
||||
};
|
||||
|
||||
@@ -64,7 +68,7 @@ export default class ToolbarButtonUtils {
|
||||
// This method ensures that if the provided commandNames and state hasn't changed
|
||||
// the output also won't change. Invididual toolbarButtonInfo also won't changed
|
||||
// if the state they use hasn't changed. This is to avoid useless renders of the toolbars.
|
||||
public commandsToToolbarButtons(state:any, commandNames:string[]):ToolbarButtonInfo[] {
|
||||
public commandsToToolbarButtons(commandNames:string[], whenClauseContext:any):ToolbarButtonInfo[] {
|
||||
const output:ToolbarButtonInfo[] = [];
|
||||
|
||||
for (const commandName of commandNames) {
|
||||
@@ -73,8 +77,7 @@ export default class ToolbarButtonUtils {
|
||||
continue;
|
||||
}
|
||||
|
||||
const props = this.service.commandMapStateToProps(commandName, state);
|
||||
output.push(this.commandToToolbarButton(commandName, props));
|
||||
output.push(this.commandToToolbarButton(commandName, whenClauseContext));
|
||||
}
|
||||
|
||||
return stateUtils.selectArrayShallow({ array: output }, commandNames.join('_'));
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import markdownUtils, { MarkdownTableHeader, MarkdownTableRow } from 'lib/markdownUtils';
|
||||
|
||||
export default function commandsToMarkdownTable():string {
|
||||
const headers:MarkdownTableHeader[] = [
|
||||
{
|
||||
name: 'commandName',
|
||||
label: 'Name',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
},
|
||||
{
|
||||
name: 'props',
|
||||
label: 'Props',
|
||||
},
|
||||
];
|
||||
|
||||
const rows:MarkdownTableRow[] = [];
|
||||
|
||||
for (const commandName in this.commands_) {
|
||||
|
||||
const row:MarkdownTableRow = {
|
||||
commandName: commandName,
|
||||
description: this.label(commandName),
|
||||
};
|
||||
|
||||
rows.push(row);
|
||||
}
|
||||
|
||||
return markdownUtils.createMarkdownTable(headers, rows);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
export default function propsHaveChanged(previous:any, next:any):boolean {
|
||||
if (!previous && next) return true;
|
||||
if (previous && !next) return true;
|
||||
if (!previous && !next) return false;
|
||||
|
||||
if (Object.keys(previous).length !== Object.keys(next).length) return true;
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { stateUtils } from 'lib/reducer';
|
||||
|
||||
const BaseModel = require('lib/BaseModel');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const MarkupToHtml = require('lib/joplin-renderer/MarkupToHtml');
|
||||
|
||||
export default function stateToWhenClauseContext(state:any) {
|
||||
const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null;
|
||||
const note = noteId ? BaseModel.byId(state.notes, noteId) : null;
|
||||
|
||||
return {
|
||||
// UI elements
|
||||
markdownEditorVisible: !!state.settings['editor.codeView'],
|
||||
richTextEditorVisible: !state.settings['editor.codeView'],
|
||||
markdownEditorPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('editor'),
|
||||
markdownViewerPaneVisible: state.settings['editor.codeView'] && state.noteVisiblePanes.includes('viewer'),
|
||||
modalDialogVisible: !!Object.keys(state.visibleDialogs).length,
|
||||
sideBarVisible: !!state.sidebarVisibility,
|
||||
noteListHasNotes: !!state.notes.length,
|
||||
|
||||
// Application state
|
||||
notesAreBeingSaved: stateUtils.hasNotesBeingSaved(state),
|
||||
syncStarted: state.syncStarted,
|
||||
|
||||
// Current location
|
||||
inConflictFolder: state.selectedFolderId === Folder.conflictFolderId(),
|
||||
|
||||
// Note selection
|
||||
oneNoteSelected: !!note,
|
||||
someNotesSelected: state.selectedNoteIds.length > 0,
|
||||
multipleNotesSelected: state.selectedNoteIds.length > 1,
|
||||
noNotesSelected: !state.selectedNoteIds.length,
|
||||
|
||||
// Note history
|
||||
historyhasBackwardNotes: state.backwardHistoryNotes.length > 0,
|
||||
historyhasForwardNotes: state.forwardHistoryNotes.length > 0,
|
||||
|
||||
// Folder selection
|
||||
oneFolderSelected: !!state.selectedFolderId,
|
||||
|
||||
// Current note properties
|
||||
noteIsTodo: note ? !!note.is_todo : false,
|
||||
noteTodoCompleted: note ? !!note.todo_completed : false,
|
||||
noteIsMarkdown: note ? note.markup_language === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN : false,
|
||||
noteIsHtml: note ? note.markup_language === MarkupToHtml.MARKUP_LANGUAGE_HTML : false,
|
||||
};
|
||||
}
|
||||
@@ -282,6 +282,8 @@ export default class InteropService {
|
||||
|
||||
let importer = null;
|
||||
|
||||
// Not certain the "modulePath" property still has any use at this point. Modules should be looked up
|
||||
// based on their format and outputFormat.
|
||||
if (options.modulePath) {
|
||||
importer = this.newModuleFromPath_(ModuleType.Importer, options);
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,7 @@ const shim = require('lib/shim').default;
|
||||
const { extractImageUrls } = require('lib/markdownUtils').default;
|
||||
const { unique } = require('lib/ArrayUtils');
|
||||
const { pregQuote } = require('lib/string-utils-common');
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
|
||||
export default class InteropService_Importer_Md extends InteropService_Importer_Base {
|
||||
async exec(result:ImportExportResult) {
|
||||
@@ -101,6 +102,7 @@ export default class InteropService_Importer_Md extends InteropService_Importer_
|
||||
created_time: stat.birthtime.getTime(),
|
||||
user_updated_time: stat.mtime.getTime(),
|
||||
user_created_time: stat.birthtime.getTime(),
|
||||
markup_language: MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN,
|
||||
};
|
||||
|
||||
return Note.save(note, { autoTimestamp: false });
|
||||
|
||||
@@ -5,7 +5,7 @@ import BasePluginRunner from 'lib/services/plugins/BasePluginRunner';
|
||||
import BaseService from '../BaseService';
|
||||
import shim from 'lib/shim';
|
||||
const { filename, dirname } = require('lib/path-utils');
|
||||
const nodeSlug = require('slug');
|
||||
const uslug = require('uslug');
|
||||
|
||||
interface Plugins {
|
||||
[key:string]: Plugin
|
||||
@@ -13,7 +13,7 @@ interface Plugins {
|
||||
|
||||
function makePluginId(source:string):string {
|
||||
// https://www.npmjs.com/package/slug#options
|
||||
return nodeSlug(source, nodeSlug.defaults.modes['rfc3986']).substr(0,32);
|
||||
return uslug(source).substr(0,32);
|
||||
}
|
||||
|
||||
export default class PluginService extends BaseService {
|
||||
|
||||
@@ -30,11 +30,11 @@ export default class JoplinCommands {
|
||||
*
|
||||
* // Create a new sub-notebook under the provided notebook
|
||||
* // Note: internally, notebooks are called "folders".
|
||||
* await joplin.commands.execute('newFolder', { parent_id: "SOME_FOLDER_ID" });
|
||||
* await joplin.commands.execute('newFolder', "SOME_FOLDER_ID");
|
||||
* ```
|
||||
*/
|
||||
async execute(commandName: string, props: any = null):Promise<any> {
|
||||
return CommandService.instance().execute(commandName, props);
|
||||
async execute(commandName: string, ...args:any[]):Promise<any> {
|
||||
return CommandService.instance().execute(commandName, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,8 +65,7 @@ export default class JoplinCommands {
|
||||
execute: command.execute,
|
||||
};
|
||||
|
||||
if ('isEnabled' in command) runtime.isEnabled = command.isEnabled;
|
||||
if ('mapStateToProps' in command) runtime.mapStateToProps = command.mapStateToProps;
|
||||
if ('enabledCondition' in command) runtime.enabledCondition = command.enabledCondition;
|
||||
|
||||
CommandService.instance().registerDeclaration(declaration);
|
||||
CommandService.instance().registerRuntime(declaration.name, runtime);
|
||||
|
||||
@@ -3,12 +3,47 @@
|
||||
// =================================================================
|
||||
|
||||
export interface Command {
|
||||
/**
|
||||
* Name of command - must be globally unique
|
||||
*/
|
||||
name: string
|
||||
|
||||
/**
|
||||
* Label to be displayed on menu items or keyboard shortcut editor for example
|
||||
*/
|
||||
label: string
|
||||
|
||||
/**
|
||||
* Icon to be used on toolbar buttons for example
|
||||
*/
|
||||
iconName?: string,
|
||||
|
||||
/**
|
||||
* Code to be ran when the command is executed. It maybe return a result.
|
||||
*/
|
||||
execute(props:any):Promise<any>
|
||||
isEnabled?(props:any):boolean
|
||||
mapStateToProps?(state:any):any
|
||||
|
||||
/**
|
||||
* Defines whether the command should be enabled or disabled, which in turns affects
|
||||
* the enabled state of any associated button or menu item.
|
||||
*
|
||||
* The condition should be expressed as a "when-clause" (as in Visual Studio Code). It's a simple boolean expression that evaluates to
|
||||
* `true` or `false`. It supports the following operators:
|
||||
*
|
||||
* Operator | Symbol | Example
|
||||
* -- | -- | --
|
||||
* Equality | == | "editorType == markdown"
|
||||
* Inequality | != | "currentScreen != config"
|
||||
* 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
|
||||
*
|
||||
* Note: Commands are enabled by default unless you use this property.
|
||||
*/
|
||||
enabledCondition?: string
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
||||
31
ReactNativeClient/package-lock.json
generated
31
ReactNativeClient/package-lock.json
generated
@@ -5232,6 +5232,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
@@ -7125,6 +7130,14 @@
|
||||
"lower-case": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node-emoji": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
|
||||
"integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
|
||||
"requires": {
|
||||
"lodash.toarray": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
@@ -9196,11 +9209,6 @@
|
||||
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
|
||||
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
|
||||
},
|
||||
"slug": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/slug/-/slug-3.5.1.tgz",
|
||||
"integrity": "sha512-ei0JnJzg8HKhLunZy+vpNlILRRradfaAQ+p2YEI4b4r8yX/5TlFi1JSwcYQCg7INZxdTC43BT68rHMkRxzn7Xg=="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
|
||||
@@ -9855,6 +9863,11 @@
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||
},
|
||||
"unorm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
|
||||
"integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA=="
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
@@ -9950,6 +9963,14 @@
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"uslug": {
|
||||
"version": "git+https://github.com/laurent22/uslug.git#ba2834d79beb0435318709958b2f5e817d96674d",
|
||||
"from": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"requires": {
|
||||
"node-emoji": "^1.10.0",
|
||||
"unorm": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"utf8": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
|
||||
|
||||
@@ -81,13 +81,13 @@
|
||||
"redux": "4.0.0",
|
||||
"reselect": "^4.0.0",
|
||||
"rn-fetch-blob": "^0.12.0",
|
||||
"slug": "^3.5.0",
|
||||
"stream": "0.0.2",
|
||||
"string-natural-compare": "^2.0.2",
|
||||
"string-padding": "^1.0.2",
|
||||
"timers": "^0.1.1",
|
||||
"url": "^0.11.0",
|
||||
"url-parse": "^1.4.7",
|
||||
"uslug": "git+https://github.com/laurent22/uslug.git#emoji-support",
|
||||
"uuid": "^3.0.1",
|
||||
"valid-url": "^1.0.9",
|
||||
"word-wrap": "^1.2.3",
|
||||
|
||||
@@ -73,14 +73,12 @@ const KeychainServiceDriverMobile = require('lib/services/keychain/KeychainServi
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
||||
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
|
||||
const SyncTargetOneDriveDev = require('lib/SyncTargetOneDriveDev.js');
|
||||
const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js');
|
||||
const SyncTargetDropbox = require('lib/SyncTargetDropbox.js');
|
||||
const SyncTargetAmazonS3 = require('lib/SyncTargetAmazonS3.js');
|
||||
|
||||
SyncTargetRegistry.addClass(SyncTargetOneDrive);
|
||||
if (__DEV__) SyncTargetRegistry.addClass(SyncTargetOneDriveDev);
|
||||
SyncTargetRegistry.addClass(SyncTargetNextcloud);
|
||||
SyncTargetRegistry.addClass(SyncTargetWebDAV);
|
||||
SyncTargetRegistry.addClass(SyncTargetDropbox);
|
||||
@@ -465,7 +463,6 @@ async function initialize(dispatch) {
|
||||
let locale = NativeModules.I18nManager.localeIdentifier;
|
||||
if (!locale) locale = defaultLocale();
|
||||
Setting.setValue('locale', closestSupportedLocale(locale));
|
||||
if (Setting.value('env') === 'dev') Setting.setValue('sync.target', SyncTargetRegistry.nameToId('onedrive_dev'));
|
||||
Setting.setValue('firstStart', 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ function filterLogs(logs, platform) {
|
||||
if (platform === 'android' && prefix.indexOf('android') >= 0) addIt = true;
|
||||
if (platform === 'ios' && prefix.indexOf('ios') >= 0) addIt = true;
|
||||
if (platform === 'desktop' && prefix.indexOf('desktop') >= 0) addIt = true;
|
||||
if (platform === 'desktop' && (prefix.indexOf('desktop') >= 0 || prefix.indexOf('api') >= 0)) addIt = true;
|
||||
if (platform === 'desktop' && (prefix.indexOf('desktop') >= 0 || prefix.indexOf('api') >= 0 || prefix.indexOf('plugins') >= 0)) addIt = true;
|
||||
if (platform === 'cli' && prefix.indexOf('cli') >= 0) addIt = true;
|
||||
if (platform === 'clipper' && prefix.indexOf('clipper') >= 0) addIt = true;
|
||||
|
||||
@@ -121,7 +121,7 @@ function formatCommitMessage(msg, author, options) {
|
||||
const isPlatformPrefix = prefix => {
|
||||
prefix = prefix.split(',').map(p => p.trim().toLowerCase());
|
||||
for (const p of prefix) {
|
||||
if (['android', 'mobile', 'ios', 'desktop', 'cli', 'clipper', 'all', 'api'].indexOf(p) >= 0) return true;
|
||||
if (['android', 'mobile', 'ios', 'desktop', 'cli', 'clipper', 'all', 'api', 'plugins'].indexOf(p) >= 0) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -129,6 +129,7 @@ function formatCommitMessage(msg, author, options) {
|
||||
if (splitted.length) {
|
||||
const platform = splitted[0].trim().toLowerCase();
|
||||
if (platform === 'api') subModule = 'api';
|
||||
if (platform === 'plugins') subModule = 'plugins';
|
||||
if (isPlatformPrefix(platform)) {
|
||||
splitted.splice(0, 1);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ module.exports = {
|
||||
'**/Modules/TinyMCE/IconPack/**',
|
||||
'**/CliClient/tests/support/plugins/**',
|
||||
'**/plugin_types/**',
|
||||
'**/ReactNativeClient/android/**',
|
||||
'**/ReactNativeClient/ios/**',
|
||||
],
|
||||
}).map(f => f.substr(rootDir.length + 1));
|
||||
|
||||
@@ -32,6 +34,7 @@ module.exports = {
|
||||
await Promise.all([
|
||||
utils.replaceFileText(`${rootDir}/.gitignore`, regex, replacement),
|
||||
utils.replaceFileText(`${rootDir}/.eslintignore`, regex, replacement),
|
||||
utils.replaceFileText(`${rootDir}/.ignore`, regex, replacement),
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"_vieux/": true,
|
||||
"*.sublime-workspace": true,
|
||||
"**/_mydocs/EnexSamples/*.enex": true,
|
||||
"CliClient/locales/*.po": true,
|
||||
"**/*.base64": true,
|
||||
"**/*.bundle.js": true,
|
||||
"**/*.ttf": true,
|
||||
@@ -58,7 +59,6 @@
|
||||
"ElectronClient/dist/": true,
|
||||
"ElectronClient/fonts/": true,
|
||||
"ElectronClient/gui/note-viewer/highlight/styles/": true,
|
||||
"ElectronClient/lib/": true,
|
||||
"ElectronClient/locale/": true,
|
||||
"ElectronClient/build/": true,
|
||||
"node_modules/": true,
|
||||
@@ -233,183 +233,19 @@
|
||||
"**/joplin-webclipper-source.zip": true,
|
||||
"Tools/commit_hook.txt": true,
|
||||
".vscode/*": true,
|
||||
"CliClient/tests/support/plugins/codemirror_test/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/codemirror_test/src/index.js": true,
|
||||
"CliClient/tests/support/plugins/withExternalModules/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/withExternalModules/src/index.js": true,
|
||||
"ElectronClient/global.d.js": true,
|
||||
"ElectronClient/gui/MultiNoteActions.js": true,
|
||||
"ElectronClient/gui/NoteContentPropertiesDialog.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Toolbar.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteEditor.js": true,
|
||||
"ElectronClient/gui/NoteEditor/styles/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/contextMenu.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/resourceHandling.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/types.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useDropHandler.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useFormNote.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useMessageHandler.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js": true,
|
||||
"CliClient/tests/support/plugins/**/api": true,
|
||||
"CliClient/tests/support/plugins/**/README.md": true,
|
||||
"CliClient/tests/support/plugins/**/package.json": true,
|
||||
"CliClient/tests/support/plugins/**/webpack.config.js": true,
|
||||
"CliClient/tests/support/plugins/**/.gitignore": true,
|
||||
"CliClient/tests/support/plugins/**/tsconfig.json": true,
|
||||
"ElectronClient/gui/NoteListItem.js": true,
|
||||
"ElectronClient/gui/NoteToolbar/NoteToolbar.js": true,
|
||||
"ElectronClient/gui/plugins/UserWebview.js": true,
|
||||
"ElectronClient/gui/ResourceScreen.js": true,
|
||||
"ElectronClient/gui/ShareNoteDialog.js": true,
|
||||
"ReactNativeClient/lib/AsyncActionQueue.js": true,
|
||||
"ReactNativeClient/lib/checkPermissions.js": true,
|
||||
"ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js": true,
|
||||
"ReactNativeClient/lib/hooks/usePrevious.js": true,
|
||||
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js": true,
|
||||
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js": true,
|
||||
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js": true,
|
||||
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js": true,
|
||||
"ReactNativeClient/lib/JoplinServerApi.js": true,
|
||||
"ReactNativeClient/lib/services/keychain/KeychainService.js": true,
|
||||
"ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js": true,
|
||||
"ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js": true,
|
||||
"ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js": true,
|
||||
"ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Plugin.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/PluginService.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/SandboxService.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/utils/types.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/ViewController.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/WebviewController.js": true,
|
||||
"ReactNativeClient/lib/services/ResourceEditWatcher.js": true,
|
||||
"ReactNativeClient/lib/services/rest/actionApi.desktop.js": true,
|
||||
"ReactNativeClient/lib/services/rest/errors.js": true,
|
||||
"ReactNativeClient/lib/services/SettingUtils.js": true,
|
||||
"ReactNativeClient/lib/services/UndoRedoService.js": true,
|
||||
"ReactNativeClient/lib/ShareExtension.js": true,
|
||||
"ReactNativeClient/lib/shareHandler.js": true,
|
||||
"ReactNativeClient/PluginAssetsLoader.js": true,
|
||||
"ReactNativeClient/setUpQuickActions.js": true,
|
||||
"D:/Web/www/nextcloud/apps/joplin/Tools/**/github_oauth_token.txt": true,
|
||||
"D:/Web/www/nextcloud/apps/joplin/Tools/**/node_modules/": true,
|
||||
"D:/Web/www/nextcloud/apps/joplin/**/vendor/": true,
|
||||
"D:/Web/www/nextcloud/apps/joplin/**/dist/": true,
|
||||
"ReactNativeClient/lib/commands/newNote.js": true,
|
||||
"ReactNativeClient/lib/commands/newTodo.js": true,
|
||||
"ReactNativeClient/lib/services/CommandService.js": true,
|
||||
"ElectronClient/gui/ErrorBoundary.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/editAlarm.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/exportPdf.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/hideModalMessage.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/moveToFolder.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/newNote.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/newFolder.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/newTodo.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/print.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/renameFolder.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/renameTag.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/search.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/selectTemplate.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/setTags.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/showModalMessage.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/showNoteProperties.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/toggleNoteList.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/toggleSidebar.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js": true,
|
||||
"./ElectronClient/**/*.min.js": true,
|
||||
"ElectronClient/commands/focusElement.js": true,
|
||||
"ElectronClient/gui/Header/commands/focusSearch.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/focusElementNoteBody.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/focusElementNoteTitle.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/showLocalSearch.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/startExternalEditing.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/stopExternalEditing.js": true,
|
||||
"ElectronClient/gui/NoteList/commands/focusElementNoteList.js": true,
|
||||
"ElectronClient/gui/SideBar/commands/focusElementSideBar.js": true,
|
||||
"ReactNativeClient/lib/commands/synchronize.js": true,
|
||||
"ElectronClient/commands/startExternalEditing.js": true,
|
||||
"ElectronClient/commands/stopExternalEditing.js": true,
|
||||
"ElectronClient/gui/NoteEditor/commands/showRevisions.js": true,
|
||||
"ReactNativeClient/lib/commands/historyBackward.js": true,
|
||||
"ReactNativeClient/lib/commands/historyForward.js": true,
|
||||
"ReactNativeClient/lib/hooks/useEffectDebugger.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/newSandbox.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/reducer.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/Sandbox.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplin.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinApi.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinCommands.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinFilters.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinPlugins.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinUtils.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinViews.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinWorkspaces.js": true,
|
||||
"CliClient/tests/support/amazon-s3-auth.json": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js": true,
|
||||
"CliClient/tests/synchronizer_LockHandler.js": true,
|
||||
"CliClient/tests/synchronizer_MigrationHandler.js": true,
|
||||
"ElectronClient/gui/Root_UpgradeSyncTarget.js": true,
|
||||
"ReactNativeClient/lib/services/synchronizer/LockHandler.js": true,
|
||||
"ReactNativeClient/lib/services/synchronizer/MigrationHandler.js": true,
|
||||
"ReactNativeClient/lib/services/synchronizer/migrations/2.js": true,
|
||||
"ReactNativeClient/lib/services/synchronizer/utils/types.js": true,
|
||||
"ReactNativeClient/lib/services/synchronizer/gui/useSyncTargetUpgrade.js": true,
|
||||
"ReactNativeClient/lib/services/ResourceEditWatcher/index.js": true,
|
||||
"ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js": true,
|
||||
"./CliClient/tests/support/nextcloud-auth.json": true,
|
||||
"./CliClient/tests/support/onedrive-auth.txt": true,
|
||||
"CliClient/app/LinkSelector.js": true,
|
||||
"CliClient/build/LinkSelector.js": true,
|
||||
"ElectronClient/gui/MainScreen/commands/toggleEditors.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js": true,
|
||||
"ElectronClient/gui/ResizableLayout/ResizableLayout.js": true,
|
||||
"ElectronClient/gui/ToggleEditorsButton/styles/index.js": true,
|
||||
"ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js": true,
|
||||
"ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js": true,
|
||||
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/media.js": true,
|
||||
"ReactNativeClient/lib/services/KeymapService.js": true,
|
||||
"ReactNativeClient/lib/services/synchronizer/migrations/1.js": true,
|
||||
"ReactNativeClient/lib/versionInfo.js": true,
|
||||
"ElectronClient/gui/NoteList/NoteList.js": true,
|
||||
"ElectronClient/gui/NoteListControls/NoteListControls.js": true,
|
||||
"ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js": true,
|
||||
"ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js": true,
|
||||
"ElectronClient/gui/ToolbarButton/styles/index.js": true,
|
||||
"ElectronClient/gui/ToolbarButton/ToolbarButton.js": true,
|
||||
"ElectronClient/gui/Button/Button.js": true,
|
||||
"ElectronClient/gui/SearchBar/hooks/useSearch.js": true,
|
||||
"ElectronClient/gui/SearchBar/SearchBar.js": true,
|
||||
"ElectronClient/gui/style/StyledInput.js": true,
|
||||
"ElectronClient/gui/style/StyledTextInput.js": true,
|
||||
"ElectronClient/gui/NoteListControls/commands/focusSearch.js": true,
|
||||
"ElectronClient/gui/SearchBar/styles/index.js": true,
|
||||
"ElectronClient/**/node_modules/": true,
|
||||
"ElectronClient/**/packageInfo.js": true,
|
||||
"ElectronClient/**/dist/": true,
|
||||
@@ -418,10 +254,7 @@
|
||||
"ElectronClient/**/.DS_Store": true,
|
||||
"ElectronClient/**/gui/note-viewer/pluginAssets/": true,
|
||||
"ElectronClient/**/pluginAssets/": true,
|
||||
"ElectronClient/gui/SideBar/SideBar.js": true,
|
||||
"ElectronClient/gui/SideBar/styles/index.js": true,
|
||||
"ReactNativeClient/lib/services/searchengine/filterParser.js": true,
|
||||
"ReactNativeClient/lib/services/searchengine/queryBuilder.js": true,
|
||||
"ElectronClient/lib/": true,
|
||||
"Clipper/popup/**/node_modules": true,
|
||||
"Clipper/popup/**/coverage": true,
|
||||
"Clipper/popup/**/build": true,
|
||||
@@ -440,19 +273,11 @@
|
||||
"CliClient/**/tests-build/": true,
|
||||
"CliClient/tests/src": true,
|
||||
"CliClient/**/config.json": true,
|
||||
"CliClient/app/lib": true,
|
||||
"CliClient/tests/fuzzing/client0": true,
|
||||
"CliClient/tests/fuzzing/client1": true,
|
||||
"CliClient/tests/fuzzing/client2": true,
|
||||
"CliClient/tests/fuzzing/sync": true,
|
||||
"CliClient/tests/fuzzing.*": true,
|
||||
"CliClient/tests/fuzzing -*": true,
|
||||
"CliClient/tests/logs/*": true,
|
||||
"CliClient/**/tests/cli-integration/": true,
|
||||
"CliClient/**/tests/tmp/": true,
|
||||
"CliClient/**/*.mo": true,
|
||||
"CliClient/**/*.*~": true,
|
||||
"CliClient/tests/sync": true,
|
||||
"CliClient/**/out.txt": true,
|
||||
"CliClient/**/linkToLocal.sh": true,
|
||||
"CliClient/**/yarn-error.log": true,
|
||||
@@ -523,198 +348,21 @@
|
||||
"Modules/TinyMCE/JoplinLists/**/package-lock.json": true,
|
||||
"Modules/TinyMCE/JoplinLists/**/jenkins-plumbing": true,
|
||||
"**/ReactNativeClient/lib/sql-extensions/": true,
|
||||
"ReactNativeClient/lib/sql-extensions/spellfix.dll": false,
|
||||
"ElectronClient/gui/ConfigScreen/ConfigScreen.js": true,
|
||||
"ElectronClient/gui/ConfigScreen/SideBar.js": true,
|
||||
"ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js": true,
|
||||
"ElectronClient/gui/KeymapConfig/ShortcutRecorder.js": true,
|
||||
"ElectronClient/gui/KeymapConfig/styles/index.js": true,
|
||||
"ElectronClient/gui/KeymapConfig/utils/getLabel.js": true,
|
||||
"ElectronClient/gui/KeymapConfig/utils/useCommandStatus.js": true,
|
||||
"ElectronClient/gui/KeymapConfig/utils/useKeymap.js": true,
|
||||
"ElectronClient/gui/MainScreen/MainScreen.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useFolder.js": true,
|
||||
"ReactNativeClient/lib/ntpDate.js": true,
|
||||
"ElectronClient/gui/ConfigScreen/ButtonBar.js": true,
|
||||
"ElectronClient/gui/DropboxLoginScreen.js": true,
|
||||
"ElectronClient/gui/OneDriveLoginScreen.js": true,
|
||||
"ElectronClient/gui/StatusScreen/StatusScreen.js": true,
|
||||
"ReactNativeClient/lib/theme.js": true,
|
||||
"ReactNativeClient/lib/themes/aritimDark.js": true,
|
||||
"ReactNativeClient/lib/themes/dark.js": true,
|
||||
"ReactNativeClient/lib/themes/dracula.js": true,
|
||||
"ReactNativeClient/lib/themes/light.js": true,
|
||||
"ReactNativeClient/lib/themes/nord.js": true,
|
||||
"ReactNativeClient/lib/themes/oledDark.js": true,
|
||||
"ReactNativeClient/lib/themes/solarizedDark.js": true,
|
||||
"ReactNativeClient/lib/themes/solarizedLight.js": true,
|
||||
"ReactNativeClient/lib/themes/type.js": true,
|
||||
"CliClient/tests/support/plugins/toc/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/toc/src/index.js": true,
|
||||
"ElectronClient/commands/copyDevCommand.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinWorkspace.js": true,
|
||||
"CliClient/tests/support/plugins/toc/**/dist/": true,
|
||||
"CliClient/tests/services_InteropService.js": true,
|
||||
"ElectronClient/gui/plugins/hooks/useThemeCss.js": true,
|
||||
"ElectronClient/gui/plugins/hooks/useViewIsReady.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Exporter_Base.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_Base.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService.js": true,
|
||||
"ReactNativeClient/lib/services/interop/types.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinInterop.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Exporter_Custom.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_Custom.js": true,
|
||||
"CliClient/tests/support/plugins/json_export/dist/*": true,
|
||||
"CliClient/tests/support/plugins/json_export/**/node_modules/": true,
|
||||
"CliClient/tests/support/plugins/register_command/dist/*": true,
|
||||
"CliClient/tests/support/plugins/register_command/**/node_modules/": true,
|
||||
"ReactNativeClient/lib/sql-extensions/spellfix.so": true,
|
||||
"ReactNativeClient/lib/sql-extensions/spellfix.dylib": true,
|
||||
"CliClient/tests/support/plugins/json_export/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/json_export/src/index.js": true,
|
||||
"CliClient/tests/support/plugins/register_command/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/register_command/src/index.js": true,
|
||||
"ElectronClient/app.js": true,
|
||||
"ElectronClient/gui/Root.js": true,
|
||||
"ElectronClient/gui/ToolbarBase.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/MenuItemController.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/ToolbarButtonController.js": true,
|
||||
"CliClient/tests/support/plugins/dialog/dist/*": true,
|
||||
"CliClient/tests/support/plugins/dialog/**/node_modules/": true,
|
||||
"CliClient/tests/support/plugins/settings/dist/*": true,
|
||||
"CliClient/tests/support/plugins/settings/**/node_modules/": true,
|
||||
"CliClient/tests/support/plugins/selected_text/dist/*": true,
|
||||
"CliClient/tests/support/plugins/selected_text/**/node_modules/": true,
|
||||
"CliClient/tests/models_Setting.js": true,
|
||||
"CliClient/tests/support/plugins/dialog/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/dialog/src/index.js": true,
|
||||
"CliClient/tests/support/plugins/selected_text/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/selected_text/src/index.js": true,
|
||||
"CliClient/tests/support/plugins/settings/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/settings/src/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/useSandboxRegistration.js": true,
|
||||
"ElectronClient/gui/plugins/UserWebviewDialog.js": true,
|
||||
"ElectronClient/gui/plugins/UserWebviewDialogButtonBar.js": true,
|
||||
"ElectronClient/plugins/SandboxImplementation.js": true,
|
||||
"ReactNativeClient/lib/eventManager.js": true,
|
||||
"ReactNativeClient/lib/hooks/usePropsDebugger.js": true,
|
||||
"ReactNativeClient/lib/models/Setting.js": true,
|
||||
"ReactNativeClient/lib/services/debug/populateDatabase.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/Sandbox/SandboxJoplinSettings.js": true,
|
||||
"CliClient/tests/support/plugins/multi_selection/dist/*": true,
|
||||
"CliClient/tests/support/plugins/multi_selection/**/node_modules/": true,
|
||||
"CliClient/tests/support/plugins/markdown_plugin/dist/*": true,
|
||||
"CliClient/tests/support/plugins/markdown_plugin/**/node_modules/": true,
|
||||
"CliClient/tests/support/plugins/markdown_plugin/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/markdown_plugin/src/index.js": true,
|
||||
"CliClient/tests/support/plugins/multi_selection/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/multi_selection/src/index.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js": true,
|
||||
"ElectronClient/gui/NoteTextViewer.js": true,
|
||||
"ElectronClient/gui/utils/NoteListUtils.js": true,
|
||||
"ReactNativeClient/lib/reducer.js": true,
|
||||
"**/CliClient/build/": true,
|
||||
"CliClient/app/services/plugins/PluginRunner.js": true,
|
||||
"CliClient/tests/services_PluginService.js": true,
|
||||
"CliClient/tests/services/plugins/sandboxProxy.js": true,
|
||||
"ElectronClient/services/plugins/hooks/useThemeCss.js": true,
|
||||
"ElectronClient/services/plugins/hooks/useViewIsReady.js": true,
|
||||
"ElectronClient/services/plugins/SandboxImplementation.js": true,
|
||||
"ElectronClient/services/plugins/UserWebview.js": true,
|
||||
"ElectronClient/services/plugins/UserWebviewDialog.js": true,
|
||||
"ElectronClient/services/plugins/UserWebviewDialogButtonBar.js": true,
|
||||
"ReactNativeClient/lib/services/BaseService.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/BasePluginRunner.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/sandboxProxy.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/Global.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/Joplin.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinCommands.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinData.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinFilters.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinInterop.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinPlugins.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinSettings.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinUtils.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinViews.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js": true,
|
||||
"ElectronClient/services/plugins/PlatformImplementation.js": true,
|
||||
"ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js": true,
|
||||
"ElectronClient/bridge.js": true,
|
||||
"ElectronClient/ElectronAppWrapper.js": true,
|
||||
"ElectronClient/services/bridge.js": true,
|
||||
"ElectronClient/services/plugins/PluginRunner.js": true,
|
||||
"ReactNativeClient/lib/Logger.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Exporter_Html.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Exporter_Jex.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Exporter_Md.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Exporter_Raw.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToHtml.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToMd.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_Jex.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_Md.js": true,
|
||||
"ReactNativeClient/lib/services/interop/InteropService_Importer_Raw.js": true,
|
||||
"ReactNativeClient/lib/shim.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinViewsDialogs.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinViewsMenuItems.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinViewsPanels.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinViewsToolbarButtons.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/utils/createViewHandle.js": true,
|
||||
"ReactNativeClient/lib/uuid.js": true,
|
||||
"CliClient/tests/support/plugins/alarm/dist/*": true,
|
||||
"CliClient/tests/support/plugins/alarm/**/node_modules/": true,
|
||||
"CliClient/tests/support/plugins/alarm/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/alarm/src/index.js": true,
|
||||
"ReactNativeClient/lib/models/Alarm.js": true,
|
||||
"ReactNativeClient/lib/services/AlarmService.js": true,
|
||||
"ReactNativeClient/lib/services/AlarmServiceDriver.android.js": true,
|
||||
"ReactNativeClient/lib/services/AlarmServiceDriver.ios.js": true,
|
||||
"ReactNativeClient/lib/services/AlarmServiceDriverNode.js": true,
|
||||
"ReactNativeClient/lib/Synchronizer.js": true,
|
||||
"CliClient/tests/support/plugins/events/dist/*": true,
|
||||
"CliClient/tests/support/plugins/events/**/node_modules/": true,
|
||||
"CliClient/tests/services_CommandService.js": true,
|
||||
"CliClient/tests/support/plugins/events/global.d.js": true,
|
||||
"CliClient/tests/support/plugins/events/src/index.js": true,
|
||||
"ReactNativeClient/lib/services/BooleanExpression.js": true,
|
||||
"ReactNativeClient/lib/services/contextkey/contextkey.js": true,
|
||||
"ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useToolbarItems.js": true,
|
||||
"ReactNativeClient/lib/services/commands/propsHaveChanged.js": true,
|
||||
"ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js": true,
|
||||
"ReactNativeClient/lib/BaseApplication.js": true,
|
||||
"ElectronClient/gui/MenuBar.js": true,
|
||||
"ElectronClient/InteropServiceHelper.js": true,
|
||||
"ReactNativeClient/lib/services/commands/MenuUtils.js": true,
|
||||
"ReactNativeClient/lib/locale.js": true,
|
||||
"**/plugin_types/": true,
|
||||
"ElectronClient/commands/toggleExternalEditing.js": true,
|
||||
"ReactNativeClient/lib/errorUtils.js": true,
|
||||
"ReactNativeClient/lib/markdownUtils.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/types.js": true,
|
||||
"ReactNativeClient/lib/services/rest/Api.js": true,
|
||||
"CliClient/tests/services_rest_Api.js": true,
|
||||
"ReactNativeClient/*/fastlane/report.xml": true,
|
||||
"ReactNativeClient/*/fastlane/Preview.html": true,
|
||||
"ReactNativeClient/*/fastlane/screenshots": true,
|
||||
"ReactNativeClient/**/*.jsbundle": true,
|
||||
"ReactNativeClient/ios/Pods/": true,
|
||||
"QtClient/build-*": true,
|
||||
"ReactNativeClient/lib/components/BackButtonDialogBox.js": true,
|
||||
"ReactNativeClient/lib/components/SelectDateTimeDialog.js": true,
|
||||
"ReactNativeClient/lib/PoorManIntervals.js": true,
|
||||
"ReactNativeClient/lib/components/CameraView.js": true,
|
||||
"ReactNativeClient/lib/components/NoteBodyViewer.js": true,
|
||||
"CliClient/tests/InMemoryCache.js": true,
|
||||
"ReactNativeClient/lib/InMemoryCache.js": true,
|
||||
"ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnMessage.js": true,
|
||||
"ReactNativeClient/lib/components/NoteBodyViewer/hooks/useOnResourceLongPress.js": true,
|
||||
"ReactNativeClient/lib/components/NoteBodyViewer/hooks/useSource.js": true,
|
||||
"ReactNativeClient/lib/components/NoteBodyViewer/NoteBodyViewer.js": true,
|
||||
"ReactNativeClient/lib/components/screens/Note.js": true,
|
||||
"CliClient/tests/services/plugins/api/JoplinSetting.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/api/JoplinViewsMenus.js": true,
|
||||
"ReactNativeClient/lib/services/plugins/MenuController.js": true
|
||||
"**/*.js": {"when": "$(basename).ts"},
|
||||
"**/*?.js": { "when": "$(basename).tsx"},
|
||||
},
|
||||
"spellright.language": [
|
||||
"en"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"generatePluginTypes": "rm -rf ./plugin_types && node node_modules/typescript/bin/tsc --declaration --declarationDir ./plugin_types --project tsconfig.json",
|
||||
"setupNewRelease": "node ./Tools/setupNewRelease",
|
||||
"linkChecker": "linkchecker https://joplinapp.org",
|
||||
"clean": "npm run clean",
|
||||
"clean": "gulp clean",
|
||||
"postinstall": "cd Tools && npm i && cd .. && cd ReactNativeClient && npm i && cd .. && cd ElectronClient && npm i && cd .. && cd CliClient && npm i && cd .. && gulp build"
|
||||
},
|
||||
"husky": {
|
||||
|
||||
@@ -125,17 +125,17 @@ joplin.plugins.register({
|
||||
});
|
||||
```
|
||||
|
||||
Later you will also need a way to generate the slug for each header. A slug is an identifier which is used to link to a particular header. Essentially a header text like "My Header" is converted to "my-header". And if there's already a slug with that name, a number is appended to it. Without going into too much details, you will need the "slug" package to generate this for you, so install it using `npm i -s slug` from the root of your plugin directory.
|
||||
Later you will also need a way to generate the slug for each header. A slug is an identifier which is used to link to a particular header. Essentially a header text like "My Header" is converted to "my-header". And if there's already a slug with that name, a number is appended to it. Without going into too much details, you will need the "slug" package to generate this for you, so install it using `npm i -s 'git+https://github.com/laurent22/uslug.git#emoji-support'` from the root of your plugin directory (Note: you can also install the "uslug" package on its own, but it won't have emoji support).
|
||||
|
||||
Then this is the function you will need for Joplin, so copy it somewhere in your file:
|
||||
|
||||
```typescript
|
||||
const nodeSlug = require('slug');
|
||||
const uslug = require('uslug');
|
||||
|
||||
let slugs = {};
|
||||
|
||||
function headerSlug(headerText) {
|
||||
const s = nodeSlug(headerText);
|
||||
const s = uslug(headerText);
|
||||
let num = slugs[s] ? slugs[s] : 1;
|
||||
const output = [s];
|
||||
if (num > 1) output.push(num);
|
||||
@@ -322,9 +322,7 @@ joplin.plugins.register({
|
||||
if (message.name === 'scrollToHash') {
|
||||
// As the name says, the scrollToHash command makes the note scroll
|
||||
// to the provided hash.
|
||||
joplin.commands.execute('scrollToHash', {
|
||||
hash: message.hash,
|
||||
})
|
||||
joplin.commands.execute('scrollToHash', message.hash)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user