Compare commits
247 Commits
android-v3
...
android-v3
Author | SHA1 | Date | |
---|---|---|---|
|
5c35569b5b | ||
|
5f02af9724 | ||
|
975f16d21c | ||
|
06359834d6 | ||
|
0cc0fec8c3 | ||
|
68ab5dcda5 | ||
|
65544123e6 | ||
|
cfbded00e2 | ||
|
a898e17b4c | ||
|
d12e2d9a81 | ||
|
7025321d76 | ||
|
6c890121b9 | ||
|
9c4be00745 | ||
|
7f51712311 | ||
|
502c929c88 | ||
|
1abf9e9602 | ||
|
8bdb6c5d72 | ||
|
9cbd1b855c | ||
|
ae8658554f | ||
|
bc385d59e9 | ||
|
00ccd994e3 | ||
|
9251299289 | ||
|
fe67a44285 | ||
|
50a1b184fd | ||
|
3caa718132 | ||
|
d0e16c0878 | ||
|
4fcb250c27 | ||
|
86e59ad621 | ||
|
12baa9827d | ||
|
95c50ada7c | ||
|
55a57f7baf | ||
|
69b24b4437 | ||
|
5143fae0f6 | ||
|
01a62acfdf | ||
|
c663742689 | ||
|
0c405951ed | ||
|
4b411e600c | ||
|
bf58a52394 | ||
|
36d3736bff | ||
|
4df0b9f851 | ||
|
914b5e230d | ||
|
9278fd7910 | ||
|
2180ad1d9b | ||
|
d301cdf992 | ||
|
200d3c84e0 | ||
|
6cadaa2137 | ||
|
8221081514 | ||
|
dd06b1e680 | ||
|
70e0ae0c2c | ||
|
7aeec923e3 | ||
|
70d64225c8 | ||
|
ad0ecc2320 | ||
|
8a28edcda8 | ||
|
c8640aa7f8 | ||
|
ddf75d6c52 | ||
|
0a42317e07 | ||
|
51ce1b06fe | ||
|
44c735afac | ||
|
c6154cfb4e | ||
|
d2aad1d6c7 | ||
|
3e81cc8585 | ||
|
abc5c062c3 | ||
|
316ef9d960 | ||
|
b870f8344c | ||
|
6f6683d15d | ||
|
da59aef95b | ||
|
c55979cd03 | ||
|
07f4217f17 | ||
|
8a7071179d | ||
|
2c9a12307e | ||
|
dd3864fa47 | ||
|
43c1c5849b | ||
|
5e08ff0621 | ||
|
45838c0223 | ||
|
91ac4f8526 | ||
|
3603350287 | ||
|
bcde346ebe | ||
|
9803d7985d | ||
|
30f6b3ecb2 | ||
|
0b287d1113 | ||
|
be18655ceb | ||
|
be43ff42c9 | ||
|
1230e1b30c | ||
|
f5d168b16a | ||
|
7055d3db18 | ||
|
18a9c3f841 | ||
|
a4ab197c42 | ||
|
2b5881a103 | ||
|
e9dee4cd99 | ||
|
dd948f5c95 | ||
|
798e1b8f4f | ||
|
b3f69794b1 | ||
|
f25e1a5e80 | ||
|
17e463b6bc | ||
|
786e55c972 | ||
|
cd9155514c | ||
|
3e9e669642 | ||
|
e36a30eb1a | ||
|
1975ebd438 | ||
|
94bff77313 | ||
|
6e3258a5d8 | ||
|
c55c8d62ec | ||
|
c7031568a8 | ||
|
c2c72215b9 | ||
|
cc09f92d3b | ||
|
8312196faa | ||
|
a16a66c37b | ||
|
a8210225a0 | ||
|
cd50454664 | ||
|
986163721d | ||
|
e41dcb9bc9 | ||
|
f90e642f43 | ||
|
67d1dd36be | ||
|
2cba693905 | ||
|
a226ede5d7 | ||
|
7994c0bc79 | ||
|
d589891a86 | ||
|
fe6c949cc1 | ||
|
4e677d2baf | ||
|
25aab57af5 | ||
|
db81064c98 | ||
|
9b82578253 | ||
|
bb513c83ac | ||
|
662185816d | ||
|
cc1582d535 | ||
|
aa6348a127 | ||
|
68f4b8ed0c | ||
|
98540493e0 | ||
|
762daa5a68 | ||
|
827233605e | ||
|
31b13defb6 | ||
|
8611391d01 | ||
|
5a3d57e39a | ||
|
e22ccd6edf | ||
|
8aec0ae445 | ||
|
24a2f5452c | ||
|
d6f1ca4ba4 | ||
|
2a058ed809 | ||
|
877123bda7 | ||
|
d621e631f7 | ||
|
64d1da9773 | ||
|
2643bb9b32 | ||
|
5c737b3ccd | ||
|
23f75f8784 | ||
|
60b2f69620 | ||
|
1d00b7a68e | ||
|
d0b783c595 | ||
|
9c446b03da | ||
|
0603c56446 | ||
|
4223864302 | ||
|
0ddf5732a8 | ||
|
a5ffc11831 | ||
|
157ad2c0cd | ||
|
0ac710ecf9 | ||
|
d3046582e1 | ||
|
9642640cda | ||
|
dab2438df0 | ||
|
dc7871b655 | ||
|
ff465767ab | ||
|
c58aac9387 | ||
|
29e55b8231 | ||
|
dc10ff6215 | ||
|
e8e3ef36ed | ||
|
e1b41cff5f | ||
|
5782ee6ba1 | ||
|
cbf81d1257 | ||
|
c357b77a48 | ||
|
64c14fe76f | ||
|
b2c1d7a2ba | ||
|
dbd4cffef3 | ||
|
d190463325 | ||
|
2c1aa5d620 | ||
|
52d255352a | ||
|
76274033db | ||
|
92abfac3af | ||
|
c6956df1c9 | ||
|
0bd1e202a2 | ||
|
f602ad8a63 | ||
|
6a1b498e96 | ||
|
ca64451503 | ||
|
519f3f5898 | ||
|
907b1e969e | ||
|
216b750a90 | ||
|
219d5bcae3 | ||
|
b59774a763 | ||
|
0494719e4f | ||
|
0e1b81685a | ||
|
c157cd0cb3 | ||
|
5c711df2e4 | ||
|
e520a695a6 | ||
|
5ee8a9a454 | ||
|
6b73879512 | ||
|
f08235f05c | ||
|
a4b1b9a2bf | ||
|
fc8ea6df0b | ||
|
2fba101333 | ||
|
35a0b22df2 | ||
|
e177bffb1c | ||
|
f95ca578c2 | ||
|
4bed47a1af | ||
|
5a0b0e6314 | ||
|
f119212068 | ||
|
cd12de78d6 | ||
|
6aa2c5f116 | ||
|
e287e5cbab | ||
|
d70a5b25a0 | ||
|
d2df7e6feb | ||
|
e9ee8c8419 | ||
|
8d2ae7e20e | ||
|
50d5843344 | ||
|
1fdc327977 | ||
|
c18ab5a7fb | ||
|
11216902d0 | ||
|
950ffef84d | ||
|
86e6445526 | ||
|
ab286b6da3 | ||
|
8c24928cf4 | ||
|
3952060dac | ||
|
877f39bb0e | ||
|
652812a15c | ||
|
597f3188bd | ||
|
d7d50f4373 | ||
|
83db585c0b | ||
|
d817ddd5c6 | ||
|
98fce34fe9 | ||
|
a81af0711c | ||
|
72575e3c6f | ||
|
e8f305dea5 | ||
|
e1e2ba8888 | ||
|
633d87ebfe | ||
|
a9e1be944f | ||
|
6048f9613c | ||
|
0a76494555 | ||
|
edbb6137ea | ||
|
4d216ef907 | ||
|
2f71c40ceb | ||
|
d3ea6fbe1d | ||
|
d45864888a | ||
|
0e92ab654a | ||
|
9e5c0ef3ce | ||
|
431cc15a51 | ||
|
82118810d9 | ||
|
bacaf800f2 | ||
|
4d827afccb | ||
|
e70efcbd60 | ||
|
ac154ee1e8 | ||
|
6220267abb |
@@ -170,6 +170,7 @@ packages/app-desktop/commands/switchProfile2.js
|
||||
packages/app-desktop/commands/switchProfile3.js
|
||||
packages/app-desktop/commands/toggleExternalEditing.js
|
||||
packages/app-desktop/commands/toggleSafeMode.js
|
||||
packages/app-desktop/commands/toggleTabMovesFocus.js
|
||||
packages/app-desktop/gui/Button/Button.js
|
||||
packages/app-desktop/gui/ClipperConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
|
||||
@@ -201,6 +202,7 @@ packages/app-desktop/gui/FolderIconBox.js
|
||||
packages/app-desktop/gui/HelpButton.js
|
||||
packages/app-desktop/gui/IconButton.js
|
||||
packages/app-desktop/gui/ImportScreen.js
|
||||
packages/app-desktop/gui/InlineCombobox.js
|
||||
packages/app-desktop/gui/ItemList.js
|
||||
packages/app-desktop/gui/JoplinCloudConfigScreen.js
|
||||
packages/app-desktop/gui/JoplinCloudLoginScreen.js
|
||||
@@ -249,13 +251,15 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useRefocusOnVis
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/enableTextAreaTab.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.test.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useEditDialog.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useEditDialogEventListeners.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useKeyboardRefocusHandler.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useLinkTooltips.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
@@ -263,10 +267,12 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useTabIndenter.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
packages/app-desktop/gui/NoteEditor/StatusBar.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/BannerContent.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/WarningBanner.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementToolbar.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/index.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/pasteAsText.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
|
||||
@@ -289,12 +295,12 @@ packages/app-desktop/gui/NoteEditor/utils/useEffectiveNoteId.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useFolder.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useFormNote.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useFormNote.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useResourceUnwatcher.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
@@ -312,6 +318,7 @@ packages/app-desktop/gui/NoteList/utils/useItemCss.js
|
||||
packages/app-desktop/gui/NoteList/utils/useMoveNote.js
|
||||
packages/app-desktop/gui/NoteList/utils/useOnKeyDown.js
|
||||
packages/app-desktop/gui/NoteList/utils/useOnNoteClick.js
|
||||
packages/app-desktop/gui/NoteList/utils/useOnNoteDoubleClick.js
|
||||
packages/app-desktop/gui/NoteList/utils/useScroll.js
|
||||
packages/app-desktop/gui/NoteList/utils/useVisibleRange.test.js
|
||||
packages/app-desktop/gui/NoteList/utils/useVisibleRange.js
|
||||
@@ -354,6 +361,7 @@ packages/app-desktop/gui/PasswordInput/types.js
|
||||
packages/app-desktop/gui/PdfViewer.js
|
||||
packages/app-desktop/gui/PluginNotification/PluginNotification.js
|
||||
packages/app-desktop/gui/PromptDialog.js
|
||||
packages/app-desktop/gui/ResizableLayout/LayoutItemContainer.js
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
||||
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
|
||||
@@ -428,6 +436,7 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/gotoAnything.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/hideModalMessage.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/linkToNote.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/moveToFolder.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newFolder.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newNote.js
|
||||
@@ -448,7 +457,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
|
||||
@@ -457,7 +465,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareFolderDialog
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.test.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditorPlugin.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditors.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleLayoutMoveMode.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleMenuBar.js
|
||||
@@ -479,6 +486,7 @@ packages/app-desktop/gui/hooks/useDocument.js
|
||||
packages/app-desktop/gui/hooks/useEffectDebugger.js
|
||||
packages/app-desktop/gui/hooks/useElementHeight.js
|
||||
packages/app-desktop/gui/hooks/useImperativeHandlerDebugger.js
|
||||
packages/app-desktop/gui/hooks/useMarkupToHtml.js
|
||||
packages/app-desktop/gui/hooks/usePrevious.js
|
||||
packages/app-desktop/gui/hooks/usePropsDebugger.js
|
||||
packages/app-desktop/gui/lib/SearchInput/SearchInput.js
|
||||
@@ -489,6 +497,7 @@ packages/app-desktop/gui/style/StyledInput.js
|
||||
packages/app-desktop/gui/style/StyledLink.js
|
||||
packages/app-desktop/gui/style/StyledMessage.js
|
||||
packages/app-desktop/gui/style/StyledTextInput.js
|
||||
packages/app-desktop/gui/utils/NoteListUtils.test.js
|
||||
packages/app-desktop/gui/utils/NoteListUtils.js
|
||||
packages/app-desktop/gui/utils/announceForAccessibility.js
|
||||
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
|
||||
@@ -498,6 +507,8 @@ packages/app-desktop/gulpfile.js
|
||||
packages/app-desktop/integration-tests/goToAnything.spec.js
|
||||
packages/app-desktop/integration-tests/main.spec.js
|
||||
packages/app-desktop/integration-tests/markdownEditor.spec.js
|
||||
packages/app-desktop/integration-tests/models/ChangeAppLayoutScreen.js
|
||||
packages/app-desktop/integration-tests/models/EditorCodeDialog.js
|
||||
packages/app-desktop/integration-tests/models/GoToAnything.js
|
||||
packages/app-desktop/integration-tests/models/MainScreen.js
|
||||
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
|
||||
@@ -506,6 +517,7 @@ packages/app-desktop/integration-tests/models/SettingsScreen.js
|
||||
packages/app-desktop/integration-tests/models/Sidebar.js
|
||||
packages/app-desktop/integration-tests/noteList.spec.js
|
||||
packages/app-desktop/integration-tests/pluginApi.spec.js
|
||||
packages/app-desktop/integration-tests/resizableLayout.spec.js
|
||||
packages/app-desktop/integration-tests/richTextEditor.spec.js
|
||||
packages/app-desktop/integration-tests/settings.spec.js
|
||||
packages/app-desktop/integration-tests/sidebar.spec.js
|
||||
@@ -562,6 +574,7 @@ packages/app-desktop/utils/customProtocols/constants.js
|
||||
packages/app-desktop/utils/customProtocols/handleCustomProtocols.test.js
|
||||
packages/app-desktop/utils/customProtocols/handleCustomProtocols.js
|
||||
packages/app-desktop/utils/customProtocols/registerCustomProtocols.js
|
||||
packages/app-desktop/utils/initializeCommandService.js
|
||||
packages/app-desktop/utils/isSafeToOpen.test.js
|
||||
packages/app-desktop/utils/isSafeToOpen.js
|
||||
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
|
||||
@@ -606,6 +619,7 @@ packages/app-mobile/components/EditorToolbar/utils/allToolbarCommandNamesFromSta
|
||||
packages/app-mobile/components/EditorToolbar/utils/isSelected.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/selectedCommandNamesFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/toolbarButtonsFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/useButtonSize.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.jest.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.web.js
|
||||
@@ -640,10 +654,12 @@ packages/app-mobile/components/NoteEditor/ImageEditor/isEditableResource.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/applyTemplateToEditor.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/polyfills.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/startAutosaveLoop.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/types.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/watchEditorForTemplateChanges.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/promptRestoreAutosave.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/utils/useEditorMessenger.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js
|
||||
@@ -681,6 +697,7 @@ packages/app-mobile/components/buttons/index.js
|
||||
packages/app-mobile/components/getResponsiveValue.test.js
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/global-style.js
|
||||
packages/app-mobile/components/plugins/PluginNotification.js
|
||||
packages/app-mobile/components/plugins/PluginRunner.js
|
||||
packages/app-mobile/components/plugins/PluginRunnerWebView.js
|
||||
packages/app-mobile/components/plugins/backgroundPage/initializeDialogWebView.js
|
||||
@@ -701,6 +718,7 @@ packages/app-mobile/components/plugins/dialogs/hooks/useViewInfos.js
|
||||
packages/app-mobile/components/plugins/dialogs/hooks/useWebViewSetup.js
|
||||
packages/app-mobile/components/plugins/types.js
|
||||
packages/app-mobile/components/plugins/utils/createOnLogHandler.js
|
||||
packages/app-mobile/components/plugins/utils/useOnDevPluginsUpdated.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/FileSystemPathSelector.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/JoplinCloudConfig.js
|
||||
@@ -770,12 +788,16 @@ packages/app-mobile/components/screens/ShareManager/index.test.js
|
||||
packages/app-mobile/components/screens/ShareManager/index.js
|
||||
packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js
|
||||
packages/app-mobile/components/screens/dropbox-login.js
|
||||
packages/app-mobile/components/screens/encryption-config.test.js
|
||||
packages/app-mobile/components/screens/encryption-config.js
|
||||
packages/app-mobile/components/screens/status.js
|
||||
packages/app-mobile/components/screens/tags.js
|
||||
packages/app-mobile/components/side-menu-content.js
|
||||
packages/app-mobile/components/testing/TestProviderStack.js
|
||||
packages/app-mobile/components/voiceTyping/VoiceTypingDialog.js
|
||||
packages/app-mobile/components/voiceTyping/AudioRecordingBanner.js
|
||||
packages/app-mobile/components/voiceTyping/RecordingControls.js
|
||||
packages/app-mobile/components/voiceTyping/SpeechToTextBanner.js
|
||||
packages/app-mobile/components/voiceTyping/types.js
|
||||
packages/app-mobile/gulpfile.js
|
||||
packages/app-mobile/index.web.js
|
||||
packages/app-mobile/root.js
|
||||
@@ -789,12 +811,11 @@ packages/app-mobile/services/e2ee/crypto.js
|
||||
packages/app-mobile/services/plugins/PlatformImplementation.js
|
||||
packages/app-mobile/services/profiles/index.js
|
||||
packages/app-mobile/services/voiceTyping/VoiceTyping.js
|
||||
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.test.js
|
||||
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.js
|
||||
packages/app-mobile/services/voiceTyping/utils/unzip.android.js
|
||||
packages/app-mobile/services/voiceTyping/utils/unzip.js
|
||||
packages/app-mobile/services/voiceTyping/vosk.android.js
|
||||
packages/app-mobile/services/voiceTyping/vosk.js
|
||||
packages/app-mobile/services/voiceTyping/whisper.test.js
|
||||
packages/app-mobile/services/voiceTyping/whisper.js
|
||||
packages/app-mobile/setupQuickActions.js
|
||||
packages/app-mobile/tools/buildInjectedJs/BundledFile.js
|
||||
@@ -853,6 +874,7 @@ packages/app-mobile/utils/testing/getWebViewWindowById.js
|
||||
packages/app-mobile/utils/testing/setupGlobalStore.js
|
||||
packages/app-mobile/utils/types.js
|
||||
packages/app-mobile/web/serviceWorker.js
|
||||
packages/app-mobile/web/webpack.config.js
|
||||
packages/default-plugins/build.js
|
||||
packages/default-plugins/buildDefaultPlugins.js
|
||||
packages/default-plugins/commands/buildAll.js
|
||||
@@ -879,6 +901,10 @@ packages/editor/CodeMirror/editorCommands/sortSelectedLines.test.js
|
||||
packages/editor/CodeMirror/editorCommands/sortSelectedLines.js
|
||||
packages/editor/CodeMirror/editorCommands/supportsCommand.js
|
||||
packages/editor/CodeMirror/getScrollFraction.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.test.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownMathExtension.test.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownMathExtension.js
|
||||
packages/editor/CodeMirror/markdown/codeBlockLanguages/allLanguages.js
|
||||
packages/editor/CodeMirror/markdown/codeBlockLanguages/defaultLanguage.js
|
||||
packages/editor/CodeMirror/markdown/codeBlockLanguages/lookUpLanguage.js
|
||||
@@ -890,8 +916,6 @@ packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownCommands.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownCommands.toggleList.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownCommands.js
|
||||
packages/editor/CodeMirror/markdown/markdownMathParser.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownMathParser.js
|
||||
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.test.js
|
||||
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.js
|
||||
packages/editor/CodeMirror/markdown/utils/stripBlockquote.js
|
||||
@@ -902,6 +926,7 @@ packages/editor/CodeMirror/pluginApi/customEditorCompletion.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorControl.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorSettings.js
|
||||
packages/editor/CodeMirror/testUtil/createTestEditor.js
|
||||
packages/editor/CodeMirror/testUtil/findNodesWithName.js
|
||||
packages/editor/CodeMirror/testUtil/forceFullParse.js
|
||||
packages/editor/CodeMirror/testUtil/loadLanguages.js
|
||||
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
|
||||
@@ -931,6 +956,7 @@ packages/editor/CodeMirror/utils/keyUpHandlerExtension.js
|
||||
packages/editor/CodeMirror/utils/overwriteModeExtension.test.js
|
||||
packages/editor/CodeMirror/utils/overwriteModeExtension.js
|
||||
packages/editor/CodeMirror/utils/searchExtension.js
|
||||
packages/editor/CodeMirror/utils/selectedNoteIdExtension.js
|
||||
packages/editor/CodeMirror/utils/setupVim.js
|
||||
packages/editor/SelectionFormatting.js
|
||||
packages/editor/events.js
|
||||
@@ -998,7 +1024,10 @@ packages/lib/commands/openMasterPasswordDialog.js
|
||||
packages/lib/commands/permanentlyDeleteNote.js
|
||||
packages/lib/commands/renderMarkup.test.js
|
||||
packages/lib/commands/renderMarkup.js
|
||||
packages/lib/commands/showEditorPlugin.js
|
||||
packages/lib/commands/synchronize.js
|
||||
packages/lib/commands/toggleAllFolders.js
|
||||
packages/lib/commands/toggleEditorPlugin.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.test.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.js
|
||||
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
|
||||
@@ -1092,6 +1121,9 @@ packages/lib/models/settings/builtInMetadata.js
|
||||
packages/lib/models/settings/settingValidations.test.js
|
||||
packages/lib/models/settings/settingValidations.js
|
||||
packages/lib/models/settings/types.js
|
||||
packages/lib/models/utils/areAllFoldersCollapsed.test.js
|
||||
packages/lib/models/utils/areAllFoldersCollapsed.js
|
||||
packages/lib/models/utils/getCanBeCollapsedFolderIds.js
|
||||
packages/lib/models/utils/getCollator.js
|
||||
packages/lib/models/utils/getConflictFolderId.js
|
||||
packages/lib/models/utils/isItemId.js
|
||||
@@ -1234,6 +1266,7 @@ packages/lib/services/ocr/utils/filterOcrText.js
|
||||
packages/lib/services/ocr/utils/types.js
|
||||
packages/lib/services/plugins/BasePlatformImplementation.js
|
||||
packages/lib/services/plugins/BasePluginRunner.js
|
||||
packages/lib/services/plugins/EditorPluginHandler.js
|
||||
packages/lib/services/plugins/MenuController.js
|
||||
packages/lib/services/plugins/MenuItemController.js
|
||||
packages/lib/services/plugins/Plugin.js
|
||||
@@ -1280,6 +1313,7 @@ packages/lib/services/plugins/utils/getPluginIssueReportUrl.js
|
||||
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
|
||||
packages/lib/services/plugins/utils/getPluginSettingKeyPrefix.js
|
||||
packages/lib/services/plugins/utils/getPluginSettingValue.js
|
||||
packages/lib/services/plugins/utils/getShownPluginEditorView.js
|
||||
packages/lib/services/plugins/utils/isCompatible/getDefaultPlatforms.js
|
||||
packages/lib/services/plugins/utils/isCompatible/index.test.js
|
||||
packages/lib/services/plugins/utils/isCompatible/index.js
|
||||
|
52
.gitignore
vendored
@@ -145,6 +145,7 @@ packages/app-desktop/commands/switchProfile2.js
|
||||
packages/app-desktop/commands/switchProfile3.js
|
||||
packages/app-desktop/commands/toggleExternalEditing.js
|
||||
packages/app-desktop/commands/toggleSafeMode.js
|
||||
packages/app-desktop/commands/toggleTabMovesFocus.js
|
||||
packages/app-desktop/gui/Button/Button.js
|
||||
packages/app-desktop/gui/ClipperConfigScreen.js
|
||||
packages/app-desktop/gui/ConfigScreen/ButtonBar.js
|
||||
@@ -176,6 +177,7 @@ packages/app-desktop/gui/FolderIconBox.js
|
||||
packages/app-desktop/gui/HelpButton.js
|
||||
packages/app-desktop/gui/IconButton.js
|
||||
packages/app-desktop/gui/ImportScreen.js
|
||||
packages/app-desktop/gui/InlineCombobox.js
|
||||
packages/app-desktop/gui/ItemList.js
|
||||
packages/app-desktop/gui/JoplinCloudConfigScreen.js
|
||||
packages/app-desktop/gui/JoplinCloudLoginScreen.js
|
||||
@@ -224,13 +226,15 @@ packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/v6/utils/useRefocusOnVis
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/PlainEditor/PlainEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/enableTextAreaTab.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/joplinCommandToTinyMceCommands.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.test.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useEditDialog.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useEditDialogEventListeners.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useKeyboardRefocusHandler.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useLinkTooltips.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
@@ -238,10 +242,12 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useTabIndenter.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
packages/app-desktop/gui/NoteEditor/StatusBar.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/BannerContent.js
|
||||
packages/app-desktop/gui/NoteEditor/WarningBanner/WarningBanner.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteTitle.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementToolbar.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/index.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/pasteAsText.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/showLocalSearch.js
|
||||
@@ -264,12 +270,12 @@ packages/app-desktop/gui/NoteEditor/utils/useEffectiveNoteId.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useFolder.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useFormNote.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useFormNote.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.test.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginEditorView.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useResourceUnwatcher.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
|
||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
@@ -287,6 +293,7 @@ packages/app-desktop/gui/NoteList/utils/useItemCss.js
|
||||
packages/app-desktop/gui/NoteList/utils/useMoveNote.js
|
||||
packages/app-desktop/gui/NoteList/utils/useOnKeyDown.js
|
||||
packages/app-desktop/gui/NoteList/utils/useOnNoteClick.js
|
||||
packages/app-desktop/gui/NoteList/utils/useOnNoteDoubleClick.js
|
||||
packages/app-desktop/gui/NoteList/utils/useScroll.js
|
||||
packages/app-desktop/gui/NoteList/utils/useVisibleRange.test.js
|
||||
packages/app-desktop/gui/NoteList/utils/useVisibleRange.js
|
||||
@@ -329,6 +336,7 @@ packages/app-desktop/gui/PasswordInput/types.js
|
||||
packages/app-desktop/gui/PdfViewer.js
|
||||
packages/app-desktop/gui/PluginNotification/PluginNotification.js
|
||||
packages/app-desktop/gui/PromptDialog.js
|
||||
packages/app-desktop/gui/ResizableLayout/LayoutItemContainer.js
|
||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
||||
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
|
||||
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
|
||||
@@ -403,6 +411,7 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/gotoAnything.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/hideModalMessage.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/index.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/linkToNote.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/moveToFolder.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newFolder.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/newNote.js
|
||||
@@ -423,7 +432,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/restoreNote.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/revealResourceFile.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/search.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/setTags.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showEditorPlugin.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showModalMessage.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteContentProperties.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showNoteProperties.js
|
||||
@@ -432,7 +440,6 @@ packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareFolderDialog
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showShareNoteDialog.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.test.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/showSpellCheckerMenu.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditorPlugin.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleEditors.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleLayoutMoveMode.js
|
||||
packages/app-desktop/gui/WindowCommandsAndDialogs/commands/toggleMenuBar.js
|
||||
@@ -454,6 +461,7 @@ packages/app-desktop/gui/hooks/useDocument.js
|
||||
packages/app-desktop/gui/hooks/useEffectDebugger.js
|
||||
packages/app-desktop/gui/hooks/useElementHeight.js
|
||||
packages/app-desktop/gui/hooks/useImperativeHandlerDebugger.js
|
||||
packages/app-desktop/gui/hooks/useMarkupToHtml.js
|
||||
packages/app-desktop/gui/hooks/usePrevious.js
|
||||
packages/app-desktop/gui/hooks/usePropsDebugger.js
|
||||
packages/app-desktop/gui/lib/SearchInput/SearchInput.js
|
||||
@@ -464,6 +472,7 @@ packages/app-desktop/gui/style/StyledInput.js
|
||||
packages/app-desktop/gui/style/StyledLink.js
|
||||
packages/app-desktop/gui/style/StyledMessage.js
|
||||
packages/app-desktop/gui/style/StyledTextInput.js
|
||||
packages/app-desktop/gui/utils/NoteListUtils.test.js
|
||||
packages/app-desktop/gui/utils/NoteListUtils.js
|
||||
packages/app-desktop/gui/utils/announceForAccessibility.js
|
||||
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
|
||||
@@ -473,6 +482,8 @@ packages/app-desktop/gulpfile.js
|
||||
packages/app-desktop/integration-tests/goToAnything.spec.js
|
||||
packages/app-desktop/integration-tests/main.spec.js
|
||||
packages/app-desktop/integration-tests/markdownEditor.spec.js
|
||||
packages/app-desktop/integration-tests/models/ChangeAppLayoutScreen.js
|
||||
packages/app-desktop/integration-tests/models/EditorCodeDialog.js
|
||||
packages/app-desktop/integration-tests/models/GoToAnything.js
|
||||
packages/app-desktop/integration-tests/models/MainScreen.js
|
||||
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
|
||||
@@ -481,6 +492,7 @@ packages/app-desktop/integration-tests/models/SettingsScreen.js
|
||||
packages/app-desktop/integration-tests/models/Sidebar.js
|
||||
packages/app-desktop/integration-tests/noteList.spec.js
|
||||
packages/app-desktop/integration-tests/pluginApi.spec.js
|
||||
packages/app-desktop/integration-tests/resizableLayout.spec.js
|
||||
packages/app-desktop/integration-tests/richTextEditor.spec.js
|
||||
packages/app-desktop/integration-tests/settings.spec.js
|
||||
packages/app-desktop/integration-tests/sidebar.spec.js
|
||||
@@ -537,6 +549,7 @@ packages/app-desktop/utils/customProtocols/constants.js
|
||||
packages/app-desktop/utils/customProtocols/handleCustomProtocols.test.js
|
||||
packages/app-desktop/utils/customProtocols/handleCustomProtocols.js
|
||||
packages/app-desktop/utils/customProtocols/registerCustomProtocols.js
|
||||
packages/app-desktop/utils/initializeCommandService.js
|
||||
packages/app-desktop/utils/isSafeToOpen.test.js
|
||||
packages/app-desktop/utils/isSafeToOpen.js
|
||||
packages/app-desktop/utils/restartInSafeModeFromMain.test.js
|
||||
@@ -581,6 +594,7 @@ packages/app-mobile/components/EditorToolbar/utils/allToolbarCommandNamesFromSta
|
||||
packages/app-mobile/components/EditorToolbar/utils/isSelected.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/selectedCommandNamesFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/toolbarButtonsFromState.js
|
||||
packages/app-mobile/components/EditorToolbar/utils/useButtonSize.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.jest.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.js
|
||||
packages/app-mobile/components/ExtendedWebView/index.web.js
|
||||
@@ -615,10 +629,12 @@ packages/app-mobile/components/NoteEditor/ImageEditor/isEditableResource.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/applyTemplateToEditor.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/polyfills.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/startAutosaveLoop.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/types.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/js-draw/watchEditorForTemplateChanges.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/promptRestoreAutosave.js
|
||||
packages/app-mobile/components/NoteEditor/ImageEditor/utils/useEditorMessenger.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.test.js
|
||||
packages/app-mobile/components/NoteEditor/NoteEditor.js
|
||||
packages/app-mobile/components/NoteEditor/SearchPanel.js
|
||||
@@ -656,6 +672,7 @@ packages/app-mobile/components/buttons/index.js
|
||||
packages/app-mobile/components/getResponsiveValue.test.js
|
||||
packages/app-mobile/components/getResponsiveValue.js
|
||||
packages/app-mobile/components/global-style.js
|
||||
packages/app-mobile/components/plugins/PluginNotification.js
|
||||
packages/app-mobile/components/plugins/PluginRunner.js
|
||||
packages/app-mobile/components/plugins/PluginRunnerWebView.js
|
||||
packages/app-mobile/components/plugins/backgroundPage/initializeDialogWebView.js
|
||||
@@ -676,6 +693,7 @@ packages/app-mobile/components/plugins/dialogs/hooks/useViewInfos.js
|
||||
packages/app-mobile/components/plugins/dialogs/hooks/useWebViewSetup.js
|
||||
packages/app-mobile/components/plugins/types.js
|
||||
packages/app-mobile/components/plugins/utils/createOnLogHandler.js
|
||||
packages/app-mobile/components/plugins/utils/useOnDevPluginsUpdated.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/ConfigScreen.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/FileSystemPathSelector.js
|
||||
packages/app-mobile/components/screens/ConfigScreen/JoplinCloudConfig.js
|
||||
@@ -745,12 +763,16 @@ packages/app-mobile/components/screens/ShareManager/index.test.js
|
||||
packages/app-mobile/components/screens/ShareManager/index.js
|
||||
packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js
|
||||
packages/app-mobile/components/screens/dropbox-login.js
|
||||
packages/app-mobile/components/screens/encryption-config.test.js
|
||||
packages/app-mobile/components/screens/encryption-config.js
|
||||
packages/app-mobile/components/screens/status.js
|
||||
packages/app-mobile/components/screens/tags.js
|
||||
packages/app-mobile/components/side-menu-content.js
|
||||
packages/app-mobile/components/testing/TestProviderStack.js
|
||||
packages/app-mobile/components/voiceTyping/VoiceTypingDialog.js
|
||||
packages/app-mobile/components/voiceTyping/AudioRecordingBanner.js
|
||||
packages/app-mobile/components/voiceTyping/RecordingControls.js
|
||||
packages/app-mobile/components/voiceTyping/SpeechToTextBanner.js
|
||||
packages/app-mobile/components/voiceTyping/types.js
|
||||
packages/app-mobile/gulpfile.js
|
||||
packages/app-mobile/index.web.js
|
||||
packages/app-mobile/root.js
|
||||
@@ -764,12 +786,11 @@ packages/app-mobile/services/e2ee/crypto.js
|
||||
packages/app-mobile/services/plugins/PlatformImplementation.js
|
||||
packages/app-mobile/services/profiles/index.js
|
||||
packages/app-mobile/services/voiceTyping/VoiceTyping.js
|
||||
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.test.js
|
||||
packages/app-mobile/services/voiceTyping/utils/splitWhisperText.js
|
||||
packages/app-mobile/services/voiceTyping/utils/unzip.android.js
|
||||
packages/app-mobile/services/voiceTyping/utils/unzip.js
|
||||
packages/app-mobile/services/voiceTyping/vosk.android.js
|
||||
packages/app-mobile/services/voiceTyping/vosk.js
|
||||
packages/app-mobile/services/voiceTyping/whisper.test.js
|
||||
packages/app-mobile/services/voiceTyping/whisper.js
|
||||
packages/app-mobile/setupQuickActions.js
|
||||
packages/app-mobile/tools/buildInjectedJs/BundledFile.js
|
||||
@@ -828,6 +849,7 @@ packages/app-mobile/utils/testing/getWebViewWindowById.js
|
||||
packages/app-mobile/utils/testing/setupGlobalStore.js
|
||||
packages/app-mobile/utils/types.js
|
||||
packages/app-mobile/web/serviceWorker.js
|
||||
packages/app-mobile/web/webpack.config.js
|
||||
packages/default-plugins/build.js
|
||||
packages/default-plugins/buildDefaultPlugins.js
|
||||
packages/default-plugins/commands/buildAll.js
|
||||
@@ -854,6 +876,10 @@ packages/editor/CodeMirror/editorCommands/sortSelectedLines.test.js
|
||||
packages/editor/CodeMirror/editorCommands/sortSelectedLines.js
|
||||
packages/editor/CodeMirror/editorCommands/supportsCommand.js
|
||||
packages/editor/CodeMirror/getScrollFraction.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.test.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownHighlightExtension.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownMathExtension.test.js
|
||||
packages/editor/CodeMirror/markdown/MarkdownMathExtension.js
|
||||
packages/editor/CodeMirror/markdown/codeBlockLanguages/allLanguages.js
|
||||
packages/editor/CodeMirror/markdown/codeBlockLanguages/defaultLanguage.js
|
||||
packages/editor/CodeMirror/markdown/codeBlockLanguages/lookUpLanguage.js
|
||||
@@ -865,8 +891,6 @@ packages/editor/CodeMirror/markdown/markdownCommands.bulletedVsChecklist.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownCommands.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownCommands.toggleList.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownCommands.js
|
||||
packages/editor/CodeMirror/markdown/markdownMathParser.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownMathParser.js
|
||||
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.test.js
|
||||
packages/editor/CodeMirror/markdown/utils/renumberSelectedLists.js
|
||||
packages/editor/CodeMirror/markdown/utils/stripBlockquote.js
|
||||
@@ -877,6 +901,7 @@ packages/editor/CodeMirror/pluginApi/customEditorCompletion.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorControl.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorSettings.js
|
||||
packages/editor/CodeMirror/testUtil/createTestEditor.js
|
||||
packages/editor/CodeMirror/testUtil/findNodesWithName.js
|
||||
packages/editor/CodeMirror/testUtil/forceFullParse.js
|
||||
packages/editor/CodeMirror/testUtil/loadLanguages.js
|
||||
packages/editor/CodeMirror/testUtil/pressReleaseKey.js
|
||||
@@ -906,6 +931,7 @@ packages/editor/CodeMirror/utils/keyUpHandlerExtension.js
|
||||
packages/editor/CodeMirror/utils/overwriteModeExtension.test.js
|
||||
packages/editor/CodeMirror/utils/overwriteModeExtension.js
|
||||
packages/editor/CodeMirror/utils/searchExtension.js
|
||||
packages/editor/CodeMirror/utils/selectedNoteIdExtension.js
|
||||
packages/editor/CodeMirror/utils/setupVim.js
|
||||
packages/editor/SelectionFormatting.js
|
||||
packages/editor/events.js
|
||||
@@ -973,7 +999,10 @@ packages/lib/commands/openMasterPasswordDialog.js
|
||||
packages/lib/commands/permanentlyDeleteNote.js
|
||||
packages/lib/commands/renderMarkup.test.js
|
||||
packages/lib/commands/renderMarkup.js
|
||||
packages/lib/commands/showEditorPlugin.js
|
||||
packages/lib/commands/synchronize.js
|
||||
packages/lib/commands/toggleAllFolders.js
|
||||
packages/lib/commands/toggleEditorPlugin.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.test.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.js
|
||||
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
|
||||
@@ -1067,6 +1096,9 @@ packages/lib/models/settings/builtInMetadata.js
|
||||
packages/lib/models/settings/settingValidations.test.js
|
||||
packages/lib/models/settings/settingValidations.js
|
||||
packages/lib/models/settings/types.js
|
||||
packages/lib/models/utils/areAllFoldersCollapsed.test.js
|
||||
packages/lib/models/utils/areAllFoldersCollapsed.js
|
||||
packages/lib/models/utils/getCanBeCollapsedFolderIds.js
|
||||
packages/lib/models/utils/getCollator.js
|
||||
packages/lib/models/utils/getConflictFolderId.js
|
||||
packages/lib/models/utils/isItemId.js
|
||||
@@ -1209,6 +1241,7 @@ packages/lib/services/ocr/utils/filterOcrText.js
|
||||
packages/lib/services/ocr/utils/types.js
|
||||
packages/lib/services/plugins/BasePlatformImplementation.js
|
||||
packages/lib/services/plugins/BasePluginRunner.js
|
||||
packages/lib/services/plugins/EditorPluginHandler.js
|
||||
packages/lib/services/plugins/MenuController.js
|
||||
packages/lib/services/plugins/MenuItemController.js
|
||||
packages/lib/services/plugins/Plugin.js
|
||||
@@ -1255,6 +1288,7 @@ packages/lib/services/plugins/utils/getPluginIssueReportUrl.js
|
||||
packages/lib/services/plugins/utils/getPluginNamespacedSettingKey.js
|
||||
packages/lib/services/plugins/utils/getPluginSettingKeyPrefix.js
|
||||
packages/lib/services/plugins/utils/getPluginSettingValue.js
|
||||
packages/lib/services/plugins/utils/getShownPluginEditorView.js
|
||||
packages/lib/services/plugins/utils/isCompatible/getDefaultPlatforms.js
|
||||
packages/lib/services/plugins/utils/isCompatible/index.test.js
|
||||
packages/lib/services/plugins/utils/isCompatible/index.js
|
||||
|
@@ -1,7 +1,15 @@
|
||||
# This patch prevents the installer from considering itself as a running instance of Joplin.
|
||||
# This patch's goal is to work around an issue in the NSIS uninstaller on Windows:
|
||||
# - For future uninstallers, this patch backports an upstream commit that changes how
|
||||
# running copies of the app are found.
|
||||
# - See https://github.com/electron-userland/electron-builder/pull/8133
|
||||
# - If an existing uninstaller fails, gives an option to continue with the installation
|
||||
# despite the failure.
|
||||
# - Updates "uninstall failed" error messages to state that uninstallation failed (rather
|
||||
# than incorrectly stating that the issue was with closing the app).
|
||||
#
|
||||
# See https://github.com/laurent22/joplin/pull/11541
|
||||
diff --git a/templates/nsis/include/allowOnlyOneInstallerInstance.nsh b/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
|
||||
index fe5d45c730f36c9fe8d8cfea12e242e501b67139..af2ce5c90ac910b079e24992519bffe33d57668a 100644
|
||||
index fe5d45c730f36c9fe8d8cfea12e242e501b67139..97b27fce6798e30e3e631221435f09b3579e77c3 100644
|
||||
--- a/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
|
||||
+++ b/templates/nsis/include/allowOnlyOneInstallerInstance.nsh
|
||||
@@ -42,7 +42,7 @@
|
||||
@@ -9,7 +17,74 @@ index fe5d45c730f36c9fe8d8cfea12e242e501b67139..af2ce5c90ac910b079e24992519bffe3
|
||||
!else
|
||||
# find process owned by current user
|
||||
- nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" /FO csv | %SYSTEMROOT%\System32\find.exe "${_FILE}"`
|
||||
+ nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c tasklist /FI "USERNAME eq %USERNAME%" /FI "PID ne $pid" /FI "IMAGENAME eq ${_FILE}" /FO csv | %SYSTEMROOT%\System32\find.exe "${_FILE}"`
|
||||
+ nsExec::Exec `"$SYSDIR\cmd.exe" /c tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" /FO csv | "$SYSDIR\find.exe" "${_FILE}"`
|
||||
Pop ${_ERR}
|
||||
!endif
|
||||
!macroend
|
||||
@@ -73,7 +73,7 @@
|
||||
!ifdef INSTALL_MODE_PER_ALL_USERS
|
||||
nsExec::Exec `taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid"`
|
||||
!else
|
||||
- nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
|
||||
+ nsExec::Exec `"$SYSDIR\cmd.exe" /c taskkill /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
|
||||
!endif
|
||||
# to ensure that files are not "in-use"
|
||||
Sleep 300
|
||||
@@ -91,7 +91,7 @@
|
||||
!ifdef INSTALL_MODE_PER_ALL_USERS
|
||||
nsExec::Exec `taskkill /f /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid"`
|
||||
!else
|
||||
- nsExec::Exec `%SYSTEMROOT%\System32\cmd.exe /c taskkill /f /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
|
||||
+ nsExec::Exec `"$SYSDIR\cmd.exe" /c taskkill /f /im "${APP_EXECUTABLE_FILENAME}" /fi "PID ne $pid" /fi "USERNAME eq %USERNAME%"`
|
||||
!endif
|
||||
!insertmacro FIND_PROCESS "${APP_EXECUTABLE_FILENAME}" $R0
|
||||
${If} $R0 == 0
|
||||
diff --git a/templates/nsis/include/installUtil.nsh b/templates/nsis/include/installUtil.nsh
|
||||
index 47367741632726ba0886ac516461dbe98b7aea58..675965762375925a505ca6d8bbb67507ef696c2e 100644
|
||||
--- a/templates/nsis/include/installUtil.nsh
|
||||
+++ b/templates/nsis/include/installUtil.nsh
|
||||
@@ -126,10 +126,11 @@ Function handleUninstallResult
|
||||
Return
|
||||
|
||||
${if} $R0 != 0
|
||||
- MessageBox MB_OK|MB_ICONEXCLAMATION "$(uninstallFailed): $R0"
|
||||
+ # MessageBox MB_OK|MB_ICONEXCLAMATION "$(uninstallFailed): $R0"
|
||||
DetailPrint `Uninstall was not successful. Uninstaller error code: $R0.`
|
||||
- SetErrorLevel 2
|
||||
- Quit
|
||||
+ DetailPrint `Continuing anyway. See https://github.com/laurent22/joplin/pull/11612.`
|
||||
+ # SetErrorLevel 2
|
||||
+ # Quit
|
||||
${endif}
|
||||
FunctionEnd
|
||||
|
||||
@@ -216,11 +217,13 @@ Function uninstallOldVersion
|
||||
IntOp $R5 $R5 + 1
|
||||
|
||||
${if} $R5 > 5
|
||||
- MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeClosed)" /SD IDCANCEL IDRETRY OneMoreAttempt
|
||||
- Return
|
||||
+ MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeUninstalled)" /SD IDCANCEL IDRETRY ContinueWithoutUninstall
|
||||
+ Abort ; Exit early
|
||||
+ ContinueWithoutUninstall:
|
||||
+ Return
|
||||
${endIf}
|
||||
|
||||
- OneMoreAttempt:
|
||||
+# OneMoreAttempt: ; Commented out because unused
|
||||
ExecWait '"$uninstallerFileNameTemp" /S /KEEP_APP_DATA $0 _?=$installationDir' $R0
|
||||
ifErrors TryInPlace CheckResult
|
||||
|
||||
diff --git a/templates/nsis/messages.yml b/templates/nsis/messages.yml
|
||||
index a1c2847fa48d79f835b30b48e999ccaf3c818657..6884c18d1e77dbd6be114401d23cf5caf3e0dd94 100644
|
||||
--- a/templates/nsis/messages.yml
|
||||
+++ b/templates/nsis/messages.yml
|
||||
@@ -235,3 +235,8 @@ uninstallFailed:
|
||||
sv: Det gick inte att avinstallera gamla programfiler. Försök att köra installationsprogrammet igen.
|
||||
uk: Не вдалось видалити старі файли застосунку. Будь ласка, спробуйте запустити встановлювач знов.
|
||||
zh_TW: 無法俺安裝舊的應用程式檔案。 請嘗試再次執行安裝程式。
|
||||
+
|
||||
+
|
||||
+appCannotBeUninstalled:
|
||||
+ en: "The old version of ${PRODUCT_NAME} could not be removed. \nClick Retry to skip this step."
|
||||
+
|
||||
|
50
.yarn/patches/react-native-paper-npm-5.13.1-f153e542e2.patch
Normal file
@@ -0,0 +1,50 @@
|
||||
# This is a (hopefully temporary) fix for an accessibility issue in the FAB.Group
|
||||
# component. See https://github.com/callstack/react-native-paper/pull/4498 for details.
|
||||
diff --git a/lib/commonjs/components/FAB/FABGroup.js b/lib/commonjs/components/FAB/FABGroup.js
|
||||
index 26933dd7ac6862c0dd95e52b8cd91c8bbd0b6efc..417c91a0257849eb597afb5e339e13b6d1d54486 100644
|
||||
--- a/lib/commonjs/components/FAB/FABGroup.js
|
||||
+++ b/lib/commonjs/components/FAB/FABGroup.js
|
||||
@@ -209,8 +209,9 @@ const FABGroup = _ref => {
|
||||
}],
|
||||
pointerEvents: open ? 'box-none' : 'none',
|
||||
accessibilityRole: "button",
|
||||
- importantForAccessibility: "yes",
|
||||
- accessible: true,
|
||||
+ importantForAccessibility: open ? 'yes' : 'no-hide-descendants',
|
||||
+ accessibilityElementsHidden: !open,
|
||||
+ accessible: open,
|
||||
accessibilityLabel: accessibilityLabel
|
||||
}, it.label && /*#__PURE__*/React.createElement(_reactNative.View, null, /*#__PURE__*/React.createElement(_Card.default, {
|
||||
mode: isV3 ? 'contained' : 'elevated',
|
||||
diff --git a/lib/module/components/FAB/FABGroup.js b/lib/module/components/FAB/FABGroup.js
|
||||
index ca5c02679539b17b048d4c82f570791dd8b57545..a06902b744b3bfb06b0644930eda0ba2ce2967ca 100644
|
||||
--- a/lib/module/components/FAB/FABGroup.js
|
||||
+++ b/lib/module/components/FAB/FABGroup.js
|
||||
@@ -200,8 +200,9 @@ const FABGroup = _ref => {
|
||||
}],
|
||||
pointerEvents: open ? 'box-none' : 'none',
|
||||
accessibilityRole: "button",
|
||||
- importantForAccessibility: "yes",
|
||||
- accessible: true,
|
||||
+ importantForAccessibility: open ? 'yes' : 'no-hide-descendants',
|
||||
+ accessibilityElementsHidden: !open,
|
||||
+ accessible: open,
|
||||
accessibilityLabel: accessibilityLabel
|
||||
}, it.label && /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(Card, {
|
||||
mode: isV3 ? 'contained' : 'elevated',
|
||||
diff --git a/src/components/FAB/FABGroup.tsx b/src/components/FAB/FABGroup.tsx
|
||||
index af1e85c4cbabfdd05499f9befb9f851be5911835..d010393975b0b31852efba1b7ce9cb09da4feaec 100644
|
||||
--- a/src/components/FAB/FABGroup.tsx
|
||||
+++ b/src/components/FAB/FABGroup.tsx
|
||||
@@ -383,8 +383,9 @@ const FABGroup = ({
|
||||
]}
|
||||
pointerEvents={open ? 'box-none' : 'none'}
|
||||
accessibilityRole="button"
|
||||
- importantForAccessibility="yes"
|
||||
- accessible={true}
|
||||
+ importantForAccessibility={open ? 'yes' : 'no-hide-descendants'}
|
||||
+ accessibilityElementsHidden={!open}
|
||||
+ accessible={open}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
>
|
||||
{it.label && (
|
@@ -0,0 +1,37 @@
|
||||
diff --git a/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java b/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
|
||||
index 4f2391b..f7cc433 100644
|
||||
--- a/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
|
||||
+++ b/platforms/android/src/main/java/org/pgsqlite/SQLitePlugin.java
|
||||
@@ -8,11 +8,14 @@
|
||||
package org.pgsqlite;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
+import android.database.AbstractWindowedCursor;
|
||||
import android.database.Cursor;
|
||||
+import android.database.CursorWindow;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
import android.content.Context;
|
||||
+import android.os.Build;
|
||||
import android.util.Base64;
|
||||
|
||||
import java.io.Closeable;
|
||||
@@ -808,6 +811,17 @@ public class SQLitePlugin extends ReactContextBaseJavaModule {
|
||||
throw ex;
|
||||
}
|
||||
|
||||
+ // To try to fix the error "Row too big to fit into CursorWindow" when using sqlite binary bundled with the device
|
||||
+ // https://github.com/andpor/react-native-sqlite-storage/issues/364#issuecomment-526423153
|
||||
+ // https://github.com/laurent22/joplin/issues/1767#issuecomment-515617991
|
||||
+
|
||||
+ if (cur != null && Build.VERSION.SDK_INT >= 28) {
|
||||
+ CursorWindow cw = new CursorWindow(null, 50 * 1024 * 1024);
|
||||
+ AbstractWindowedCursor ac = (AbstractWindowedCursor) cur;
|
||||
+ ac.setWindow(cw);
|
||||
+ cur = ac;
|
||||
+ }
|
||||
+
|
||||
// If query result has rows
|
||||
if (cur != null && cur.moveToFirst()) {
|
||||
WritableArray rowsArrayResult = Arguments.createArray();
|
@@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 682.66669 682.66669"
|
||||
height="682.66669"
|
||||
width="682.66669"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="JoplinLetterBlue.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview13"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.77490232"
|
||||
inkscape:cx="366.49781"
|
||||
inkscape:cy="360.69062"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="708"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs6">
|
||||
<linearGradient
|
||||
id="linearGradient26"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(-4387.91,4387.91,4387.91,4387.91,4753.95,366.05)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="0"
|
||||
x2="1"
|
||||
y1="0"
|
||||
x1="0">
|
||||
<stop
|
||||
id="stop22"
|
||||
offset="0"
|
||||
style="stop-opacity:1;stop-color:#004caf" />
|
||||
<stop
|
||||
id="stop24"
|
||||
offset="1"
|
||||
style="stop-opacity:1;stop-color:#1f95f8" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath829"><path
|
||||
id="path831"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.999997"
|
||||
d="M 3961.59,4435.23 H 2570.18 c -13.15,0 -23.78,-10.64 -23.78,-23.77 v -441.84 c 0,-14.87 12.04,-26.92 26.92,-26.92 h 190.77 c 77.16,0 139.73,-59.35 146.43,-134.77 V 3505 3336.23 1728.75 1717.36 h -0.052 c 0.48,-16.84 -0.1898,-33.4 -1.83,-49.71 -0.18,-2.38 -0.5003,-4.73 -0.7902,-7.09 -1.0998,-9.53 -2.3199,-19.01 -4.17,-28.29 -1.0098,-5.29 -2.4399,-10.44 -3.7098,-15.65 -1.71,-6.93 -3.09,-13.97 -5.22,-20.75 -12.5802,-40.27 -32.4702,-77.62 -59.9802,-110.5 -1.0098,-1.17 -2.2599,-2.25 -3.2598,-3.41 -8.3901,-9.72 -17.2002,-19.19 -26.9502,-28.06 -9.84,-8.95 -20.2599,-17.27 -31.2099,-25 -77.8401,-55.14 -182.61,-79.4 -299.67,-68.2 -149.2599,14.03 -297.3399,81.72 -417.03,190.62 -119.6701,108.89 -194.08,243.62 -209.4799,379.41 -13.8501,121.48 22.5498,228.38 102.42,301.05 0.21,0.1598 0.3997,0.3098 0.5602,0.48 3.09,2.77 6.4901,5.2 9.6701,7.87 57.16,47.89 131.6701,76.91 216.7,84.91 0.96,0.09 1.8801,0.24 2.79,0.3203 8.9499,0.79 18.0699,1.15 27.27,1.49 4.8099,0.1598 9.5601,0.5003 14.4399,0.54 1.62,0.023 3.1602,0.1898 4.7802,0.1898 2.8998,0 5.91,-0.3803 8.8098,-0.42 13.4001,-0.21 26.9001,-0.7601 40.6701,-1.9401 1.74,-0.1402 3.3999,-0.08 5.19,-0.24 1.2699,-0.1297 2.5299,-0.4102 3.8001,-0.54 78,-7.82 155.2299,-31.11 228.5199,-66.3999 1.53,-0.068 3.3,-0.54 5.5099,-1.7601 22.34,-12.3399 26.6201,0.9 27.2801,9.6501 v 382.2399 282.8201 c 0,19.05 -13.2501,35.8999 -31.83,39.99 -394.7601,86.88 -782.08,-3.5501 -1055.38,-252.3401 -238.7499,-217.1799 -354.24,-530.5799 -316.8201,-859.7899 33.39,-293.23 183.9102,-574.94 423.88,-793.33 233.8901,-212.79003 531.69,-345.86006 838.8801,-374.80106 42.33,-3.918 84.8601,-5.93797 126.36,-5.93797 293.3799,0 565.6099,100.59802 766.54,283.37903 190.3401,173.3 304.35,411.27 321.0799,670.16 l 1.55,1697.91 h 0.1703 v 453.97 h 0.06 v 7.92 c 1.72,80.1199 67.05,144.58 147.61,144.58 h 190.77 c 14.8599,0 26.9199,12.05 26.9199,26.9199 v 441.84 c 0,13.13 -10.6299,23.77 -23.7799,23.77" /></clipPath></defs>
|
||||
<g
|
||||
id="g14"
|
||||
transform="matrix(0.13333333,0,0,-0.13333333,0,682.66667)"
|
||||
mask="none"
|
||||
clip-path="url(#clipPath829)">
|
||||
<g
|
||||
clip-path="url(#clipPath20)"
|
||||
id="g16">
|
||||
<path
|
||||
id="path28"
|
||||
style="fill:url(#linearGradient26);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 3873.89,0 H 1246.11 C 560.754,0 0,560.75 0,1246.11 V 3873.88 C 0,4559.25 560.754,5120 1246.11,5120 H 3873.89 C 4559.25,5120 5120,4559.25 5120,3873.88 V 1246.11 C 5120,560.75 4559.25,0 3873.89,0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -8,7 +8,6 @@
|
||||
import { Node } from '@ephox/dom-globals';
|
||||
import { Arr, Option } from '@ephox/katamari';
|
||||
import { HTMLElement } from '@ephox/sand';
|
||||
import DomQuery from 'tinymce/core/api/dom/DomQuery';
|
||||
import Editor from 'tinymce/core/api/Editor';
|
||||
import Tools from 'tinymce/core/api/util/Tools';
|
||||
import * as NodeType from './NodeType';
|
||||
@@ -49,7 +48,7 @@ const findParentListItemsNodes = function (editor, elms) {
|
||||
return parentLi ? parentLi : elm;
|
||||
});
|
||||
|
||||
return DomQuery.unique(listItemsElms);
|
||||
return [...new Set(listItemsElms)];
|
||||
};
|
||||
|
||||
const getSelectedListItems = function (editor) {
|
||||
@@ -89,7 +88,7 @@ const getSelectedListRoots = (editor: Editor): Node[] => {
|
||||
|
||||
const getUniqueListRoots = (editor: Editor, lists: Node[]): Node[] => {
|
||||
const listRoots = Arr.map(lists, (list) => findLastParentListNode(editor, list).getOr(list));
|
||||
return DomQuery.unique(listRoots);
|
||||
return [...new Set(listRoots)];
|
||||
};
|
||||
|
||||
const isList = (editor: Editor): boolean => {
|
||||
|
@@ -48,8 +48,7 @@ const listState = function (editor: Editor, listName, options:any = {}) {
|
||||
|
||||
const register = function (editor: Editor) {
|
||||
const hasPlugin = function (editor, plugin) {
|
||||
const plugins = editor.settings.plugins ? editor.settings.plugins : '';
|
||||
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
|
||||
return editor.hasPlugin(plugin);
|
||||
};
|
||||
|
||||
const _ = Settings.getLocalizationFunction(editor);
|
||||
|
BIN
Assets/WebsiteAssets/images/news/20250114-mobile-toolbar.png
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
Assets/WebsiteAssets/images/news/20250114-multi-window.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
Assets/WebsiteAssets/images/ocr/view_ocr_text.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
Assets/WebsiteAssets/images/sponsors/EssayPro.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
Assets/WebsiteAssets/images/sponsors/RealGambling.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
Assets/WebsiteAssets/images/sponsors/Slotozilla.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
Assets/WebsiteAssets/images/sponsors/WebDesignAgency.png
Normal file
After Width: | Height: | Size: 138 KiB |
@@ -1,4 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Tue, 17 Dec 2024 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Tue, 17 Dec 2024 00:00:00 GMT</pubDate><item><title><![CDATA[Project 4: Handwritten Text Recognition (HTR) for Joplin]]></title><description><![CDATA[<p>Joplin is partnering with a French government institution to bring you innovative new features! We will work on accessibility, voice typing, HTR and add Rocketbook integration. Today we'll present the planned HTR integration:</p>
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Jan 2025 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><item><title><![CDATA[What's new in Joplin 3.2]]></title><description><![CDATA[<h2>Import OneNote Archives<a name="import-onenote-archives" href="#import-onenote-archives" class="heading-anchor">🔗</a></h2>
|
||||
<p>Joplin now supports importing OneNote archives, a significant step for users transitioning from OneNote. Microsoft has long made it challenging to leave OneNote, offering limited export options and complex formats that make it difficult for app developers to support it. Despite these hurdles, @pedr tackled these issues head-on, developing an import tool that simplifies the process. This addition makes Joplin a practical choice for those looking to move away from OneNote's ecosystem.</p>
|
||||
<p>To use this feature, select <strong>File</strong> => <strong>Import</strong> => <strong>ZIP - OneNote Notebook</strong></p>
|
||||
<h2>Multi-window support<a name="multi-window-support" href="#multi-window-support" class="heading-anchor">🔗</a></h2>
|
||||
<p>We're excited to introduce Multi-Window Support, a highly requested feature that makes managing multiple notes easier than ever. With this update, you can open notes in different windows and each window operates independently, allowing you to compare notes, reference content, and organise projects with greater flexibility.</p>
|
||||
<p>To use this feature, right-click on a note, select <strong>Open in...</strong> and select <strong>Edit in new window</strong></p>
|
||||
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250114-multi-window.png" alt=""></p>
|
||||
<h2>Customisable toolbar on mobile<a name="customisable-toolbar-on-mobile" href="#customisable-toolbar-on-mobile" class="heading-anchor">🔗</a></h2>
|
||||
<p>The new customisable toolbar on mobile is now draggable, making it easier to access the buttons you need. You can also choose which buttons to display by tapping the Cog button, allowing for a more personalised and efficient note-editing experience!</p>
|
||||
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20250114-mobile-toolbar.png" alt=""></p>
|
||||
<h2>Enhanced Accessibility<a name="enhanced-accessibility" href="#enhanced-accessibility" class="heading-anchor">🔗</a></h2>
|
||||
<p>In order to implement the <a href="https://www.w3.org/TR/WCAG20/">WCAG 2.0 guidelines</a>, accessibility has seen a substantial upgrade thanks to the efforts of @personalizedrefrigerator. The desktop and mobile apps now offer better keyboard navigation, including improved functionality in dropdown menus and settings. Focus indicators have been made more visible, while ARIA labels have been added to boost compatibility with screen readers. Specific areas like note attachments, sidebars, and dialogs have also been optimised to ensure accessibility for all users.</p>
|
||||
<h2>Refined Drawing and Markdown Editing<a name="refined-drawing-and-markdown-editing" href="#refined-drawing-and-markdown-editing" class="heading-anchor">🔗</a></h2>
|
||||
<p>Joplin's drawing and editing features have been fine-tuned for a smoother experience. Freehand Drawing on mobile and desktop has been updated to version 2.14.0, addressing several usability issues and bugs. Additionally, the Freehand Drawing plugin is now part of the desktop app by default. These changes enhance the reliability and integration of the drawing tool.</p>
|
||||
<h2>Faster and more secure encryption<a name="faster-and-more-secure-encryption" href="#faster-and-more-secure-encryption" class="heading-anchor">🔗</a></h2>
|
||||
<p>For GSoC 2024, @wh201906 worked on optimising the encryption and decryption processes, boosting speed for mobile devices in particular (but desktop too). Additionally, the encryption security was improved by transitioning to a more robust 256-bit key.</p>
|
||||
<p>As of now this feature is optional and can be enabled by going to the <strong>Configuration Screen</strong>, then <strong>Synchronisation</strong> => <strong>Advanced options</strong> => <strong>Use beta encryption</strong>.</p>
|
||||
<h2>Expanded Plugin Support<a name="expanded-plugin-support" href="#expanded-plugin-support" class="heading-anchor">🔗</a></h2>
|
||||
<p>Developers will appreciate the updates to Joplin's plugin ecosystem. A new API has been introduced to create <a href="https://joplinapp.org/api/references/plugin_api/classes/joplinviewsdialogs.html#showtoast">toast notifications</a>, alongside updates to CodeMirror packages. A new <a href="https://github.com/laurent22/joplin/blob/5ee8a9a45493683c72a36b52e1460b5acdd4f1ac/packages/lib/commands/renderMarkup.ts#L23"><code>renderMarkup</code></a> command has been introduced to allow you to render Markdown content to HTML using the Joplin built-in API.</p>
|
||||
<h1>Full changelogs<a name="full-changelogs" href="#full-changelogs" class="heading-anchor">🔗</a></h1>
|
||||
<p>This is just an overview of the main features. The full changelogs are available there:</p>
|
||||
<ul>
|
||||
<li>Desktop: <a href="https://joplinapp.org/help/about/changelog/desktop">https://joplinapp.org/help/about/changelog/desktop</a></li>
|
||||
<li>Android: <a href="https://joplinapp.org/help/about/changelog/android/">https://joplinapp.org/help/about/changelog/android/</a></li>
|
||||
<li>iOS: <a href="https://joplinapp.org/help/about/changelog/ios/">https://joplinapp.org/help/about/changelog/ios/</a></li>
|
||||
</ul>
|
||||
]]></description><link>https://joplinapp.org/news/20250114-release-3-2</link><guid isPermaLink="false">20250114-release-3-2</guid><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><twitter-text>What's new in Joplin 3.2</twitter-text></item><item><title><![CDATA[Project 4: Handwritten Text Recognition (HTR) for Joplin]]></title><description><![CDATA[<p>Joplin is partnering with a French government institution to bring you innovative new features! We will work on accessibility, voice typing, HTR and add Rocketbook integration. Today we'll present the planned HTR integration:</p>
|
||||
<p>Currently, Joplin’s OCR (Optical Character Recognition) feature is designed to recognise printed text, which works great for scanning documents with standard fonts. However, we’re looking to expand this functionality to support handwritten text recognition (HTR), which would be beneficial to handle scanned, handwritten documents, as well as for the upcoming Rocketbook integration.</p>
|
||||
<p>Handwritten text recognition is complex task, requiring significant processing power and large machine learning models. Because of this, we plan to implement HTR via a server, possibly integrated with Joplin Cloud or Joplin Server. The beauty of this approach is that handwritten text recognition is a rapidly evolving field, so we can continuously improve the server-side model. This means that every Joplin app can benefit from these updates without needing to redeploy or update the app itself.</p>
|
||||
<p>For the Rocketbook integration, this integration will make a significant difference. Right now, your handwritten documents would be scanned as images, but with HTR, Joplin will be able to recognise the actual text you’ve written. Not only will your handwritten notes become searchable, but you’ll also be able to copy and paste the text into other documents.</p>
|
||||
@@ -393,15 +419,4 @@ sys 0m38.013s</p>
|
||||
<p>Renovate on the other hand upgrades packages one at a time, and run our test units to ensure everything is still working as expected. It also upgrades multiple instances of the same package across the monorepo, which is convenient to keep our code consistent. It also has a number of options to make our life easier, such as the ability to automatically merge a pull request for patch releases since this is usually safe (when a package is, for example upgraded from 1.0.1 to 1.0.3).</p>
|
||||
<p>Although Renovate automates the package upgrades it doesn't mean all upgrades are straightforward - our tests won't catch all issues, so the apps might end up being broken or cannot be compiled anymore. So there's manual work involved to get everything working after certain upgrades - for the most part this has been done and the apps appear to be stable so far.</p>
|
||||
<p>This will however be an important part of pre-release 2.10 (or should it be 3.0?) - we hope that everything works but we may need your support to try this version and report any glitch you may have found. As always pre-release regressions have the highest priority so we aim to fix them as quickly as possible.</p>
|
||||
]]></description><link>https://joplinapp.org/news/20221115-renovate</link><guid isPermaLink="false">20221115-renovate</guid><pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate><twitter-text>Modernising and securing Joplin, one package at a time</twitter-text></item><item><title><![CDATA[Joplin Cloud is now part of the Joplin company]]></title><description><![CDATA[<p>As some of you may know Joplin Cloud so far has been operating under my own single-person limited company in the UK. This was mostly for convenience since it meant I could get things going quickly without having to setup a special structure for it.</p>
|
||||
<p>Now that Joplin Cloud is becoming more mature however a proper company, simply called Joplin, has been created. This company will be based in France, and will be used mainly to handle the commercial part of the project, which currently is mostly Joplin Cloud. I'm still heading the company so there won't be any major change to the way the project is managed.</p>
|
||||
<h2>What does it mean for Joplin Cloud?<a name="what-does-it-mean-for-joplin-cloud" href="#what-does-it-mean-for-joplin-cloud" class="heading-anchor">🔗</a></h2>
|
||||
<p>There will be no significant change - the website ownership simply moves from one company in the UK to one in France. The new company is still owned by myself so I will keep following the same roadmap.</p>
|
||||
<h2>What does it mean for the open source apps?<a name="what-does-it-mean-for-the-open-source-apps" href="#what-does-it-mean-for-the-open-source-apps" class="heading-anchor">🔗</a></h2>
|
||||
<p>On the short term, the only visible change will be moving the non-open source assets, such as logo or trademark from the UK company to the French one. So expect a few changes in copyright notices here and there.</p>
|
||||
<p>In the medium to long term, I would like to hire one or two software developers to help me with the Joplin Cloud development, because we reached a point where managing the whole project is difficult for a single person, so some help is needed. Some of their work might also touch the open source apps since both are quite related - but of course that work will remain open source too.</p>
|
||||
<p>As a general rule, there will be a permanent commitment to keep the apps open source and to derive value from Joplin Cloud/Server.</p>
|
||||
<p>Longer term I would like to create a non-profit organisation to handle the open source applications and to make decisions about the project, as well as to decide how to allocate any funding we receive (for example from GSoC).</p>
|
||||
<h2>Looking forward<a name="looking-forward" href="#looking-forward" class="heading-anchor">🔗</a></h2>
|
||||
<p>Those past 6 years of developing Joplin have been an exciting and rewarding experience, thank you to all of you of the friendly and vibrant Joplin community for your contribution toward making Joplin the software it is today, and looking forward to continuing the journey together!</p>
|
||||
]]></description><link>https://joplinapp.org/news/20221012-Joplin-Company</link><guid isPermaLink="false">20221012-Joplin-Company</guid><pubDate>Wed, 12 Oct 2022 00:00:00 GMT</pubDate><twitter-text>Joplin Cloud is now operated by the Joplin company! More info on the announcement post.</twitter-text></item></channel></rss>
|
||||
]]></description><link>https://joplinapp.org/news/20221115-renovate</link><guid isPermaLink="false">20221115-renovate</guid><pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate><twitter-text>Modernising and securing Joplin, one package at a time</twitter-text></item></channel></rss>
|
@@ -1,7 +1,6 @@
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-12 social-links">
|
||||
<a class="social-link-bluesky" href="https://bsky.app/profile/joplinapp.bsky.social" title="Joplin Bluesky feed"><i class="fa-brands fa-bluesky"></i></a>
|
||||
<a class="social-link-twitter" href="https://twitter.com/joplinapp" title="Joplin Twitter feed"><i class="fab fa-twitter"></i></a>
|
||||
<a class="social-link-mastodon" href="https://mastodon.social/@joplinapp" title="Joplin Mastodon feed"><i class="fab fa-mastodon"></i></a>
|
||||
<a class="social-link-patreon" href="https://www.patreon.com/joplin" title="Joplin Patreon"><i class="fab fa-patreon"></i></a>
|
||||
<a class="social-link-discord" href="https://discord.gg/VSj7AFHvpq" title="Joplin Discord chat"><i class="fab fa-discord"></i></a>
|
||||
|
@@ -1,3 +1 @@
|
||||
<!-- <a href="https://twitter.com/joplinapp" title="Joplin Twitter feed" class="fw500 twitter-link"><i class="fab fa-twitter"></i></a> -->
|
||||
|
||||
<a href="https://bsky.app/profile/joplinapp.bsky.social" title="Joplin Bluesky feed" class="fw500 twitter-link"><i class="fa-brands fa-bluesky"></i></a>
|
@@ -2,11 +2,11 @@
|
||||
# Build stage
|
||||
# =============================================================================
|
||||
|
||||
FROM node:18-bullseye AS builder
|
||||
FROM node:18 AS builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
python tini \
|
||||
python3 tini \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Enables Yarn
|
||||
@@ -56,7 +56,7 @@ RUN BUILD_SEQUENCIAL=1 yarn install --inline-builds \
|
||||
# from a smaller base image.
|
||||
# =============================================================================
|
||||
|
||||
FROM node:18-bullseye-slim
|
||||
FROM node:18-slim
|
||||
|
||||
ARG user=joplin
|
||||
RUN useradd --create-home --shell /bin/bash $user
|
||||
|
@@ -31,7 +31,7 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
|
||||
# Sponsors
|
||||
|
||||
<!-- SPONSORS-ORG -->
|
||||
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&mtm_kwd=joplinapp&mtm_source=joplinapp-webseite&mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://buyyoutubviews.com"><img title="BYTV" width="256" src="https://joplinapp.org/images/sponsors/BYTV.png"/></a> <a href="https://casinoreviews.net"><img title="Casino Reviews" width="256" src="https://joplinapp.org/images/sponsors/CasinoReviews.png"/></a> <a href="https://useviral.com.br/"><img title="Comprar seguidores Instagram" width="256" src="https://joplinapp.org/images/sponsors/Useviral.png"/></a> <a href="https://ca.edubirdie.com/"><img title="Achieve academic success with Edubirdie — your trusted partner for expert writing assistance and resources!" width="256" src="https://joplinapp.org/images/sponsors/Edubirdie.png" alt="EduBirdie"/></a>
|
||||
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&mtm_kwd=joplinapp&mtm_source=joplinapp-webseite&mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://citricsheep.com"><img title="Citric Sheep" width="256" src="https://joplinapp.org/images/sponsors/CitricSheep.png"/></a> <a href="https://sorted.travel/?utm_source=joplinapp"><img title="Sorted Travel" width="256" src="https://joplinapp.org/images/sponsors/SortedTravel.png"/></a> <a href="https://celebian.com"><img title="Celebian" width="256" src="https://joplinapp.org/images/sponsors/Celebian.png"/></a> <a href="https://bestkru.com"><img title="BestKru" width="256" src="https://joplinapp.org/images/sponsors/BestKru.png"/></a> <a href="https://www.socialfollowers.uk/buy-tiktok-followers/"><img title="Social Followers" width="256" src="https://joplinapp.org/images/sponsors/SocialFollowers.png"/></a> <a href="https://stormlikes.com/"><img title="Stormlikes" width="256" src="https://joplinapp.org/images/sponsors/Stormlikes.png"/></a> <a href="https://route4me.com"><img title="Route4Me" width="256" src="https://joplinapp.org/images/sponsors/Route4Me.png"/></a> <a href="https://casinoreviews.net"><img title="Casino Reviews" width="256" src="https://joplinapp.org/images/sponsors/CasinoReviews.png"/></a> <a href="https://useviral.com.br/"><img title="Comprar seguidores Instagram" width="256" src="https://joplinapp.org/images/sponsors/Useviral.png"/></a> <a href="https://ca.edubirdie.com/"><img title="Achieve academic success with Edubirdie — your trusted partner for expert writing assistance and resources!" width="256" src="https://joplinapp.org/images/sponsors/Edubirdie.png" alt="EduBirdie"/></a> <a href="https://topagency.webflow.io"><img title="WebDesignAgency" width="256" src="https://joplinapp.org/images/sponsors/WebDesignAgency.png" alt="web design agency"/></a> <a href="https://realgambling.ca/"><img title="RealGambling.ca" width="256" src="https://joplinapp.org/images/sponsors/RealGambling.png" alt="RealGambling.ca"/></a> <a href="https://essaypro.com/"><img title="write an essay online with EssayPro" width="256" src="https://joplinapp.org/images/sponsors/EssayPro.png" alt="write an essay online with EssayPro"/></a> <a href="https://www.slotozilla.com/nz/no-deposit-bonus"><img title="casino without making any upfront cost" width="256" src="https://joplinapp.org/images/sponsors/Slotozilla.png" alt="casino without making any upfront cost"/></a>
|
||||
<!-- SPONSORS-ORG -->
|
||||
|
||||
* * *
|
||||
@@ -40,8 +40,8 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
|
||||
| | | | |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/97193607?s=96&v=4"/></br>[Akhil-CM](https://github.com/Akhil-CM) | <img width="50" src="https://avatars2.githubusercontent.com/u/552452?s=96&v=4"/></br>[andypiper](https://github.com/andypiper) | <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars2.githubusercontent.com/u/67130?s=96&v=4"/></br>[chr15m](https://github.com/chr15m) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/8030470?s=96&v=4"/></br>[Galliver7](https://github.com/Galliver7) | <img width="50" src="https://avatars2.githubusercontent.com/u/64712218?s=96&v=4"/></br>[Hegghammer](https://github.com/Hegghammer) | <img width="50" src="https://avatars2.githubusercontent.com/u/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) | <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/126279083?s=96&v=4"/></br>[matmoly](https://github.com/matmoly) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1177810?s=96&v=4"/></br>[felixstorm](https://github.com/felixstorm) | <img width="50" src="https://avatars2.githubusercontent.com/u/8030470?s=96&v=4"/></br>[Galliver7](https://github.com/Galliver7) | <img width="50" src="https://avatars2.githubusercontent.com/u/64712218?s=96&v=4"/></br>[Hegghammer](https://github.com/Hegghammer) | <img width="50" src="https://avatars2.githubusercontent.com/u/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) |
|
||||
| | | | |
|
||||
<!-- SPONSORS-GITHUB -->
|
||||
|
||||
@@ -51,7 +51,6 @@ Name | Description
|
||||
--- | ---
|
||||
[Support Forum](https://discourse.joplinapp.org/) | This is the main place for general discussion about Joplin, user support, software development questions, and to discuss new features. Also where the latest beta versions are released and discussed.
|
||||
[Bluesky feed](https://bsky.app/profile/joplinapp.bsky.social) | Follow us on Bluesky
|
||||
[Twitter feed](https://twitter.com/joplinapp) | Follow us on Twitter
|
||||
[Mastodon feed](https://mastodon.social/@joplinapp) | Follow us on Mastodon
|
||||
[Patreon page](https://www.patreon.com/joplin) |The latest news are often posted there
|
||||
[Discord server](https://discord.gg/VSj7AFHvpq) | Our chat server
|
||||
|
30
SECURITY.md
@@ -10,6 +10,36 @@ Please [contact support](https://raw.githubusercontent.com/laurent22/joplin/dev/
|
||||
|
||||
For general opinions on what makes an app more or less secure, please use the forum.
|
||||
|
||||
## Areas outside Joplin's Threat Model
|
||||
|
||||
Note: we're mostly linking to Chrome's documentation since our reasoning for these exclusions is the same.
|
||||
|
||||
### Denial of Service (DoS)
|
||||
|
||||
[Reference](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#are-denial-of-service-issues-considered-security-bugs)
|
||||
|
||||
### Physically-local attacks
|
||||
|
||||
[Reference](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
|
||||
|
||||
### Compromised/infected machines
|
||||
|
||||
[Reference](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-compromised_infected-machines-in-chromes-threat-model)
|
||||
|
||||
### Is opening a file on the local machine a security vulnerability?
|
||||
|
||||
No - users are allowed to link to files on their local computer. This was a feature that was implemented by popular request. There are measures in place to mitigate security risks such as a dialog to confirm whether a file with an unknown file extension should be opened.
|
||||
|
||||
### Is DLL sideloading a security vulnerability?
|
||||
|
||||
No. This is an Electron issue and not one they will fix: https://github.com/electron/electron/issues/28384
|
||||
|
||||
See also [Physically-local attacks](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
|
||||
|
||||
### Is local data not being encrypted a security vulnerability?
|
||||
|
||||
No, but you should use disk encryption. See also [Physically-local attacks](https://chromium.googlesource.com/chromium/src.git/+/master/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
|
||||
|
||||
## Bounty
|
||||
|
||||
We **do not** offer a bounty for discovering vulnerabilities, please do not ask. We can however credit you and link to your website in the changelog and release announcement.
|
||||
|
@@ -33,6 +33,7 @@
|
||||
"/packages/app-desktop/build/",
|
||||
"/packages/app-desktop/utils/checkForUpdatesUtilsTestData.ts",
|
||||
"/packages/app-desktop/vendor/",
|
||||
"/packages/app-mobile/android/vendor/",
|
||||
"/packages/app-mobile/ios/Pods/",
|
||||
"/packages/app-mobile/lib/rnInjectedJs",
|
||||
"/packages/app-mobile/pluginAssets",
|
||||
|
@@ -16,7 +16,7 @@ services:
|
||||
- POSTGRES_DATABASE=joplin
|
||||
- POSTGRES_USER=joplin
|
||||
- POSTGRES_PORT=5432
|
||||
- POSTGRES_HOST=localhost
|
||||
- POSTGRES_HOST=db
|
||||
db:
|
||||
image: postgres:16
|
||||
ports:
|
||||
|
1
fastlane/metadata/android/uk/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Додаток для заміток і завдань із синхронізацією між Linux, macOS, Windows і мобільними пристроями
|
@@ -114,6 +114,8 @@
|
||||
"react-native@0.74.1": "patch:react-native@npm%3A0.74.1#./.yarn/patches/react-native-npm-0.74.1-754c02ae9e.patch",
|
||||
"rn-fetch-blob@0.12.0": "patch:rn-fetch-blob@npm%3A0.12.0#./.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch",
|
||||
"app-builder-lib@26.0.0-alpha.7": "patch:app-builder-lib@npm%3A26.0.0-alpha.7#./.yarn/patches/app-builder-lib-npm-26.0.0-alpha.7-e1b3dca119.patch",
|
||||
"app-builder-lib@24.13.3": "patch:app-builder-lib@npm%3A24.13.3#./.yarn/patches/app-builder-lib-npm-24.13.3-86a66c0bf3.patch"
|
||||
"app-builder-lib@24.13.3": "patch:app-builder-lib@npm%3A24.13.3#./.yarn/patches/app-builder-lib-npm-24.13.3-86a66c0bf3.patch",
|
||||
"react-native-sqlite-storage@6.0.1": "patch:react-native-sqlite-storage@npm%3A6.0.1#./.yarn/patches/react-native-sqlite-storage-npm-6.0.1-8369d747bd.patch",
|
||||
"react-native-paper@5.13.1": "patch:react-native-paper@npm%3A5.13.1#./.yarn/patches/react-native-paper-npm-5.13.1-f153e542e2.patch"
|
||||
}
|
||||
}
|
||||
|
@@ -189,9 +189,9 @@ async function fetchAllNotes() {
|
||||
|
||||
lines.push('## Searching');
|
||||
lines.push('');
|
||||
lines.push('Call **GET /search?query=YOUR_QUERY** to search for notes. This end-point supports the `field` parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/help/#searching');
|
||||
lines.push('Call **GET /search?query=YOUR_QUERY** to search for notes. This end-point supports the `field` parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/help/apps/search');
|
||||
lines.push('');
|
||||
lines.push('To retrieve non-notes items, such as notebooks or tags, add a `type` parameter and set it to the required [item type name](#item-type-id). In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use `*` as a wildcard. This is convenient for example to retrieve notebooks or tags by title.');
|
||||
lines.push('To retrieve non-notes items, such as notebooks or tags, add a `type` parameter and set it to the required [item type name](#item-type-ids). In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use `*` as a wildcard. This is convenient for example to retrieve notebooks or tags by title.');
|
||||
lines.push('');
|
||||
lines.push('For example, to retrieve the notebook named `recipes`: **GET /search?query=recipes&type=folder**');
|
||||
lines.push('');
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env -S NODE_OPTIONS=--no-deprecation node
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Use njstrace to find out what Node.js might be spending time on
|
||||
// var njstrace = require('njstrace').inject();
|
||||
|
@@ -35,15 +35,15 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "3.2.2",
|
||||
"version": "3.3.0",
|
||||
"bin": "./main.js",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~3.2",
|
||||
"@joplin/renderer": "~3.2",
|
||||
"@joplin/utils": "~3.2",
|
||||
"@joplin/lib": "~3.3",
|
||||
"@joplin/renderer": "~3.3",
|
||||
"@joplin/utils": "~3.3",
|
||||
"aws-sdk": "2.1340.0",
|
||||
"chalk": "4.1.2",
|
||||
"compare-version": "0.1.2",
|
||||
@@ -69,10 +69,10 @@
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~3.2",
|
||||
"@joplin/tools": "~3.3",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/node": "18.19.55",
|
||||
"@types/node": "18.19.67",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"gulp": "4.0.2",
|
||||
"jest": "29.7.0",
|
||||
|
@@ -1 +1 @@
|
||||
Should keep this comment: <!-- keep this & that -->
|
||||
Should keep this comment: <!-- keep this & that -->
|
3
packages/app-cli/tests/md_to_html/sanitize_22.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<img src="test/" id="getElementById" class="jop-noMdConv"/>
|
||||
<img src="http://example.com/test.png" id="getElementById" class="jop-noMdConv"/>
|
||||
<img id="test2" src="http://example.com/test.png" class="jop-noMdConv"/>
|
5
packages/app-cli/tests/md_to_html/sanitize_22.md
Normal file
@@ -0,0 +1,5 @@
|
||||
<img name=getElementById src=test/>
|
||||
|
||||
<IMG NAME="getElementById" SRC="http://example.com/test.png">
|
||||
|
||||
<IMG NAME="test" ID="test2" SRC="http://example.com/test.png">
|
1
packages/app-cli/tests/md_to_html/sanitize_23.html
Normal file
@@ -0,0 +1 @@
|
||||
<math class="jop-noMdConv"><p class="jop-noMdConv"><style class="jop-noMdConv"><!--</style><img src onerror=alert(1)>--></style>
|
1
packages/app-cli/tests/md_to_html/sanitize_23.md
Normal file
@@ -0,0 +1 @@
|
||||
<math><p><style><!--</style><img src onerror=alert(1)>--></style>
|
BIN
packages/app-cli/tests/support/onenote/corrupted_attachment.zip
Normal file
@@ -0,0 +1 @@
|
||||

|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "3.2.0",
|
||||
"version": "3.3.0",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": {
|
||||
|
596
packages/app-clipper/popup/package-lock.json
generated
@@ -23,7 +23,7 @@
|
||||
"react-redux": "9.0.4",
|
||||
"redux": "5.0.1",
|
||||
"style-loader": "3.3.3",
|
||||
"webpack": "5.89.0",
|
||||
"webpack": "5.97.1",
|
||||
"webpack-cli": "5.1.4"
|
||||
}
|
||||
},
|
||||
@@ -1928,10 +1928,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/html-minifier-terser": {
|
||||
"version": "6.1.0",
|
||||
@@ -1961,148 +1962,163 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
|
||||
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
|
||||
"@webassemblyjs/helper-numbers": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
|
||||
"dev": true
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
|
||||
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
|
||||
"dev": true
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
|
||||
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
|
||||
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
|
||||
"dev": true
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
|
||||
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
|
||||
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
|
||||
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
|
||||
"dev": true
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
|
||||
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
|
||||
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
|
||||
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ieee754": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
|
||||
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
|
||||
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/leb128": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
|
||||
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
|
||||
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/utf8": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
|
||||
"dev": true
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
|
||||
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
|
||||
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
|
||||
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-opt": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6",
|
||||
"@webassemblyjs/wast-printer": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-section": "1.14.1",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1",
|
||||
"@webassemblyjs/wasm-opt": "1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "1.14.1",
|
||||
"@webassemblyjs/wast-printer": "1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-gen": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
|
||||
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
|
||||
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/ieee754": "1.13.2",
|
||||
"@webassemblyjs/leb128": "1.13.2",
|
||||
"@webassemblyjs/utf8": "1.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-opt": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
|
||||
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
|
||||
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-parser": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
|
||||
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/ieee754": "1.13.2",
|
||||
"@webassemblyjs/leb128": "1.13.2",
|
||||
"@webassemblyjs/utf8": "1.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/wast-printer": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
|
||||
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
|
||||
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
@@ -2154,19 +2170,22 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -2174,15 +2193,6 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-assertions": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
|
||||
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -2393,9 +2403,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.22.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
|
||||
"integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2411,11 +2421,12 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001565",
|
||||
"electron-to-chromium": "^1.4.601",
|
||||
"node-releases": "^2.0.14",
|
||||
"update-browserslist-db": "^1.0.13"
|
||||
"caniuse-lite": "^1.0.30001688",
|
||||
"electron-to-chromium": "^1.5.73",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -2441,9 +2452,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001574",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz",
|
||||
"integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==",
|
||||
"version": "1.0.30001692",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
|
||||
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2458,7 +2469,8 @@
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
]
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
@@ -2736,16 +2748,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.623",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz",
|
||||
"integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==",
|
||||
"dev": true
|
||||
"version": "1.5.83",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
|
||||
"integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
|
||||
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
|
||||
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
@@ -2782,10 +2796,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -3017,7 +3032,8 @@
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
@@ -3433,10 +3449,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
|
||||
"dev": true
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
@@ -3504,10 +3521,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
@@ -4254,9 +4272,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
|
||||
"integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4272,9 +4290,10 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
"escalade": "^3.2.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"update-browserslist-db": "cli.js"
|
||||
@@ -4314,10 +4333,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
@@ -4327,34 +4347,34 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.89.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
|
||||
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
|
||||
"version": "5.97.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.0",
|
||||
"@webassemblyjs/ast": "^1.11.5",
|
||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"browserslist": "^4.14.5",
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
"acorn": "^8.14.0",
|
||||
"browserslist": "^4.24.0",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.15.0",
|
||||
"enhanced-resolve": "^5.17.1",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.2.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.7",
|
||||
"watchpack": "^2.4.0",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"bin": {
|
||||
@@ -5830,9 +5850,9 @@
|
||||
}
|
||||
},
|
||||
"@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/html-minifier-terser": {
|
||||
@@ -5863,148 +5883,148 @@
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
|
||||
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
|
||||
"@webassemblyjs/helper-numbers": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
|
||||
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
|
||||
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-buffer": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
|
||||
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
|
||||
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
|
||||
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
|
||||
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
|
||||
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
|
||||
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
|
||||
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ieee754": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
|
||||
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
|
||||
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/leb128": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
|
||||
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
|
||||
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/utf8": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
|
||||
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@webassemblyjs/wasm-edit": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
|
||||
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
|
||||
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-section": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-opt": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6",
|
||||
"@webassemblyjs/wast-printer": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-section": "1.14.1",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1",
|
||||
"@webassemblyjs/wasm-opt": "1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "1.14.1",
|
||||
"@webassemblyjs/wast-printer": "1.14.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-gen": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
|
||||
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
|
||||
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/ieee754": "1.13.2",
|
||||
"@webassemblyjs/leb128": "1.13.2",
|
||||
"@webassemblyjs/utf8": "1.13.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-opt": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
|
||||
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
|
||||
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
"@webassemblyjs/wasm-gen": "1.11.6",
|
||||
"@webassemblyjs/wasm-parser": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
"@webassemblyjs/wasm-gen": "1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "1.14.1"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wasm-parser": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
|
||||
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
"@webassemblyjs/ieee754": "1.11.6",
|
||||
"@webassemblyjs/leb128": "1.11.6",
|
||||
"@webassemblyjs/utf8": "1.11.6"
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
"@webassemblyjs/ieee754": "1.13.2",
|
||||
"@webassemblyjs/leb128": "1.13.2",
|
||||
"@webassemblyjs/utf8": "1.13.2"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/wast-printer": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
|
||||
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
|
||||
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
@@ -6042,18 +6062,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn-import-assertions": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
|
||||
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -6213,15 +6226,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.22.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz",
|
||||
"integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001565",
|
||||
"electron-to-chromium": "^1.4.601",
|
||||
"node-releases": "^2.0.14",
|
||||
"update-browserslist-db": "^1.0.13"
|
||||
"caniuse-lite": "^1.0.30001688",
|
||||
"electron-to-chromium": "^1.5.73",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
@@ -6241,9 +6254,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001574",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz",
|
||||
"integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==",
|
||||
"version": "1.0.30001692",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
|
||||
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@@ -6449,15 +6462,15 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.623",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.623.tgz",
|
||||
"integrity": "sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==",
|
||||
"version": "1.5.83",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz",
|
||||
"integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
|
||||
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
|
||||
"integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@@ -6483,9 +6496,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
@@ -6937,9 +6950,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
||||
"dev": true
|
||||
},
|
||||
"nth-check": {
|
||||
@@ -6996,9 +7009,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true
|
||||
},
|
||||
"pkg-dir": {
|
||||
@@ -7520,13 +7533,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
|
||||
"integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
"escalade": "^3.2.0",
|
||||
"picocolors": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"uri-js": {
|
||||
@@ -7558,9 +7571,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
@@ -7568,34 +7581,33 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "5.89.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
|
||||
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
|
||||
"version": "5.97.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.0",
|
||||
"@webassemblyjs/ast": "^1.11.5",
|
||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"browserslist": "^4.14.5",
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
"acorn": "^8.14.0",
|
||||
"browserslist": "^4.24.0",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.15.0",
|
||||
"enhanced-resolve": "^5.17.1",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.2.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.7",
|
||||
"watchpack": "^2.4.0",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@@ -23,7 +23,7 @@
|
||||
"react-redux": "9.0.4",
|
||||
"redux": "5.0.1",
|
||||
"style-loader": "3.3.3",
|
||||
"webpack": "5.89.0",
|
||||
"webpack": "5.97.1",
|
||||
"webpack-cli": "5.1.4"
|
||||
},
|
||||
"browserslist": [
|
||||
|
@@ -262,15 +262,25 @@ export default class ElectronAppWrapper {
|
||||
// the easiest is to use a timeout. Keep in mind that if you get a white window on Windows it might be due
|
||||
// to this line though.
|
||||
if (debugEarlyBugs) {
|
||||
setTimeout(() => {
|
||||
// Since a recent release of Electron (v34?), calling openDevTools() here does nothing
|
||||
// if a plugin devtool window is already opened. Maybe because they do a check on
|
||||
// `isDevToolsOpened` which indeed returns `true` (but shouldn't since it's for a
|
||||
// different window). However, if you open the dev tools twice from the Help menu it
|
||||
// works. So instead we do that here and call openDevTool() three times.
|
||||
let openDevToolCount = 0;
|
||||
const openDevToolInterval = setInterval(() => {
|
||||
try {
|
||||
this.win_.webContents.openDevTools();
|
||||
openDevToolCount++;
|
||||
if (openDevToolCount >= 3) {
|
||||
clearInterval(openDevToolInterval);
|
||||
}
|
||||
} catch (error) {
|
||||
// This will throw an exception "Object has been destroyed" if the app is closed
|
||||
// in less that the timeout interval. It can be ignored.
|
||||
// This will throw an exception "Object has been destroyed" if the app is closed
|
||||
// in less that the timeout interval. It can be ignored.
|
||||
console.warn('Error opening dev tools', error);
|
||||
}
|
||||
}, 3000);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const addWindowEventHandlers = (webContents: WebContents) => {
|
||||
|
@@ -40,6 +40,8 @@ export interface AppWindowState extends WindowState {
|
||||
visibleDialogs: VisibleDialogs;
|
||||
dialogs: AppStateDialog[];
|
||||
devToolsVisible: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
watchedResources: any;
|
||||
}
|
||||
|
||||
interface BackgroundWindowStates {
|
||||
@@ -62,8 +64,6 @@ export interface AppState extends State, AppWindowState {
|
||||
modalOverlayMessage: string|null;
|
||||
|
||||
// Extra reducer keys go here
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
watchedResources: any;
|
||||
mainLayout: LayoutItem;
|
||||
isResettingLayout: boolean;
|
||||
}
|
||||
@@ -76,6 +76,7 @@ export const createAppDefaultWindowState = (): AppWindowState => {
|
||||
noteVisiblePanes: ['editor', 'viewer'],
|
||||
editorCodeView: true,
|
||||
devToolsVisible: false,
|
||||
watchedResources: {},
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -19,7 +19,6 @@ import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerS
|
||||
import SpellCheckerServiceDriverNative from './services/spellChecker/SpellCheckerServiceDriverNative';
|
||||
import bridge from './services/bridge';
|
||||
import menuCommandNames from './gui/menuCommandNames';
|
||||
import stateToWhenClauseContext from './services/commands/stateToWhenClauseContext';
|
||||
import ResourceService from '@joplin/lib/services/ResourceService';
|
||||
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||
import appReducer, { createAppDefaultState } from './app.reducer';
|
||||
@@ -35,29 +34,15 @@ const PluginManager = require('@joplin/lib/services/PluginManager');
|
||||
import RevisionService from '@joplin/lib/services/RevisionService';
|
||||
import MigrationService from '@joplin/lib/services/MigrationService';
|
||||
import { loadCustomCss } from '@joplin/lib/CssUtils';
|
||||
import mainScreenCommands from './gui/WindowCommandsAndDialogs/commands/index';
|
||||
import noteEditorCommands from './gui/NoteEditor/commands/index';
|
||||
import noteListCommands from './gui/NoteList/commands/index';
|
||||
import noteListControlsCommands from './gui/NoteListControls/commands/index';
|
||||
import sidebarCommands from './gui/Sidebar/commands/index';
|
||||
import appCommands from './commands/index';
|
||||
import libCommands from '@joplin/lib/commands/index';
|
||||
import { homedir } from 'os';
|
||||
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||
const electronContextMenu = require('./services/electron-context-menu');
|
||||
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
|
||||
|
||||
const commands = mainScreenCommands
|
||||
.concat(noteEditorCommands)
|
||||
.concat(noteListCommands)
|
||||
.concat(noteListControlsCommands)
|
||||
.concat(sidebarCommands);
|
||||
|
||||
// Commands that are not tied to any particular component.
|
||||
// The runtime for these commands can be loaded when the app starts.
|
||||
const globalCommands = appCommands.concat(libCommands);
|
||||
|
||||
import editorCommandDeclarations from './gui/NoteEditor/editorCommandDeclarations';
|
||||
import PerFolderSortOrderService from './services/sortOrder/PerFolderSortOrderService';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import checkForUpdates from './checkForUpdates';
|
||||
@@ -74,6 +59,7 @@ import SearchEngine from '@joplin/lib/services/search/SearchEngine';
|
||||
import { PackageInfo } from '@joplin/lib/versionInfo';
|
||||
import { CustomProtocolHandler } from './utils/customProtocols/handleCustomProtocols';
|
||||
import { refreshFolders } from '@joplin/lib/folders-screen-utils';
|
||||
import initializeCommandService from './utils/initializeCommandService';
|
||||
|
||||
const pluginClasses = [
|
||||
require('./plugins/GotoAnything').default,
|
||||
@@ -492,20 +478,7 @@ class Application extends BaseApplication {
|
||||
|
||||
PerFolderSortOrderService.initialize();
|
||||
|
||||
CommandService.instance().initialize(this.store(), Setting.value('env') === 'dev', stateToWhenClauseContext);
|
||||
|
||||
for (const command of commands) {
|
||||
CommandService.instance().registerDeclaration(command.declaration);
|
||||
}
|
||||
|
||||
for (const command of globalCommands) {
|
||||
CommandService.instance().registerDeclaration(command.declaration);
|
||||
CommandService.instance().registerRuntime(command.declaration.name, command.runtime());
|
||||
}
|
||||
|
||||
for (const declaration of editorCommandDeclarations) {
|
||||
CommandService.instance().registerDeclaration(declaration);
|
||||
}
|
||||
initializeCommandService(this.store(), Setting.value('env') === 'dev');
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
// We only add the commands that appear in the menu because only
|
||||
@@ -583,6 +556,12 @@ class Application extends BaseApplication {
|
||||
value: Setting.value('flagOpenDevTools'),
|
||||
});
|
||||
|
||||
// Always disable on Mac for now - and disable too for the few apps that may have the flag enabled.
|
||||
// At present, it only seems to work on Windows.
|
||||
if (shim.isMac()) {
|
||||
Setting.setValue('featureFlag.autoUpdaterServiceEnabled', false);
|
||||
}
|
||||
|
||||
// Note: Auto-update is a misnomer in the code.
|
||||
// The code below only checks, if a new version is available.
|
||||
// We only allow Windows and macOS users to automatically check for updates
|
||||
@@ -653,6 +632,7 @@ class Application extends BaseApplication {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
(action: any) => { this.store().dispatch(action); },
|
||||
(path: string) => bridge().openItem(path),
|
||||
() => this.store().getState().windowId,
|
||||
);
|
||||
|
||||
// Forwards the local event to the global event manager, so that it can
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import ElectronAppWrapper from './ElectronAppWrapper';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import shim, { MessageBoxType } from '@joplin/lib/shim';
|
||||
import { _, setLocale } from '@joplin/lib/locale';
|
||||
import { BrowserWindow, nativeTheme, nativeImage, shell, dialog, MessageBoxSyncOptions, safeStorage } from 'electron';
|
||||
import { dirname, toSystemSlashes } from '@joplin/lib/path-utils';
|
||||
@@ -118,6 +118,8 @@ export class Bridge {
|
||||
return event;
|
||||
}
|
||||
},
|
||||
|
||||
integrations: [Sentry.electronMinidumpIntegration()],
|
||||
};
|
||||
|
||||
if (this.autoUploadCrashDumps_) options.dsn = 'https://cceec550871b1e8a10fee4c7a28d5cf2@o4506576757522432.ingest.sentry.io/4506594281783296';
|
||||
@@ -384,9 +386,14 @@ export class Bridge {
|
||||
|
||||
/* returns the index of the clicked button */
|
||||
public showMessageBox(message: string, options: MessageDialogOptions = {}) {
|
||||
const defaultButtons = [_('OK')];
|
||||
if (options.type !== MessageBoxType.Error && options.type !== MessageBoxType.Info) {
|
||||
defaultButtons.push(_('Cancel'));
|
||||
}
|
||||
|
||||
const result = this.showMessageBox_(this.activeWindow(), { type: 'question',
|
||||
message: message,
|
||||
buttons: [_('OK'), _('Cancel')], ...options });
|
||||
buttons: defaultButtons, ...options });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import shim, { MessageBoxType } from '@joplin/lib/shim';
|
||||
const app = require('@electron/remote').app;
|
||||
const { clipboard } = require('electron');
|
||||
|
||||
@@ -14,7 +15,7 @@ export const runtime = (): CommandRuntime => {
|
||||
const appPath = app.getPath('exe');
|
||||
const cmd = `${appPath} --env dev`;
|
||||
clipboard.writeText(cmd);
|
||||
alert(`The dev mode command has been copied to clipboard:\n\n${cmd}`);
|
||||
await shim.showMessageBox(`The dev mode command has been copied to clipboard:\n\n${cmd}`, { type: MessageBoxType.Info });
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@@ -16,6 +16,7 @@ export const runtime = (): CommandRuntime => {
|
||||
if (target === 'noteList') return CommandService.instance().execute('focusElementNoteList');
|
||||
if (target === 'sideBar') return CommandService.instance().execute('focusElementSideBar');
|
||||
if (target === 'noteTitle') return CommandService.instance().execute('focusElementNoteTitle', options);
|
||||
if (target === 'toolbar') return CommandService.instance().execute('focusElementToolbar', options);
|
||||
throw new Error(`Invalid focus target: ${target}`);
|
||||
},
|
||||
};
|
||||
|
@@ -18,6 +18,7 @@ import * as switchProfile2 from './switchProfile2';
|
||||
import * as switchProfile3 from './switchProfile3';
|
||||
import * as toggleExternalEditing from './toggleExternalEditing';
|
||||
import * as toggleSafeMode from './toggleSafeMode';
|
||||
import * as toggleTabMovesFocus from './toggleTabMovesFocus';
|
||||
|
||||
const index: any[] = [
|
||||
copyDevCommand,
|
||||
@@ -39,6 +40,7 @@ const index: any[] = [
|
||||
switchProfile3,
|
||||
toggleExternalEditing,
|
||||
toggleSafeMode,
|
||||
toggleTabMovesFocus,
|
||||
];
|
||||
|
||||
export default index;
|
||||
|
@@ -7,7 +7,7 @@ import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'openNoteInNewWindow',
|
||||
label: () => _('Edit in new window'),
|
||||
label: () => _('Open in new window'),
|
||||
iconName: 'icon-share',
|
||||
};
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import RevisionService from '@joplin/lib/services/RevisionService';
|
||||
import shim, { MessageBoxType } from '@joplin/lib/shim';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'restoreNoteRevision',
|
||||
@@ -11,9 +12,9 @@ export const runtime = (): CommandRuntime => {
|
||||
execute: async (_context: CommandContext, noteId: string, reverseRevIndex = 0) => {
|
||||
try {
|
||||
const note = await RevisionService.instance().restoreNoteById(noteId, reverseRevIndex);
|
||||
alert(RevisionService.instance().restoreSuccessMessage(note));
|
||||
await shim.showMessageBox(RevisionService.instance().restoreSuccessMessage(note), { type: MessageBoxType.Info });
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
await shim.showErrorDialog(error.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@@ -7,7 +7,7 @@ const bridge = require('@electron/remote').require('./bridge').default;
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'startExternalEditing',
|
||||
label: () => _('Edit in external editor'),
|
||||
label: () => _('Open in external editor'),
|
||||
iconName: 'icon-share',
|
||||
};
|
||||
|
||||
|
20
packages/app-desktop/commands/toggleTabMovesFocus.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { DesktopCommandContext } from '../services/commands/types';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'toggleTabMovesFocus',
|
||||
label: () => _('Toggle editor tab key navigation'),
|
||||
iconName: 'fas fa-keyboard',
|
||||
};
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context: DesktopCommandContext, enabled: boolean = null) => {
|
||||
const newValue = enabled ?? !Setting.value('editor.tabMovesFocus');
|
||||
Setting.setValue('editor.tabMovesFocus', newValue);
|
||||
},
|
||||
enabledCondition: 'oneNoteSelected',
|
||||
};
|
||||
};
|
@@ -18,27 +18,21 @@ export enum ButtonSize {
|
||||
Normal = 2,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
type ReactButtonProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
|
||||
interface Props extends Omit<ReactButtonProps, 'onClick'> {
|
||||
title?: string;
|
||||
iconName?: string;
|
||||
level?: ButtonLevel;
|
||||
iconLabel?: string;
|
||||
className?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
onClick?: Function;
|
||||
onClick?: ()=> void;
|
||||
color?: string;
|
||||
iconAnimation?: string;
|
||||
tooltip?: string;
|
||||
disabled?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied;
|
||||
style?: any;
|
||||
size?: ButtonSize;
|
||||
isSquare?: boolean;
|
||||
iconOnly?: boolean;
|
||||
fontSize?: number;
|
||||
|
||||
'aria-controls'?: string;
|
||||
'aria-expanded'?: string;
|
||||
}
|
||||
|
||||
const StyledTitle = styled.span`
|
||||
@@ -215,54 +209,52 @@ function buttonClass(level: ButtonLevel) {
|
||||
return StyledButtonSecondary;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef(({
|
||||
iconName, iconLabel, iconAnimation, color, title, level, fontSize, isSquare, tooltip, disabled, onClick: propsOnClick, ...unusedProps
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied;
|
||||
const Button = React.forwardRef((props: Props, ref: any) => {
|
||||
const iconOnly = props.iconName && !props.title;
|
||||
}: Props, ref: any) => {
|
||||
const iconOnly = iconName && !title;
|
||||
|
||||
const StyledButton = buttonClass(props.level);
|
||||
const StyledButton = buttonClass(level);
|
||||
|
||||
function renderIcon() {
|
||||
if (!props.iconName) return null;
|
||||
if (!iconName) return null;
|
||||
return <StyledIcon
|
||||
aria-label={props.iconLabel ?? undefined}
|
||||
aria-hidden={!props.iconLabel}
|
||||
animation={props.iconAnimation}
|
||||
aria-label={iconLabel ?? undefined}
|
||||
aria-hidden={!iconLabel}
|
||||
animation={iconAnimation}
|
||||
mr={iconOnly ? '0' : '6px'}
|
||||
color={props.color}
|
||||
className={props.iconName}
|
||||
color={color}
|
||||
className={iconName}
|
||||
role='img'
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderTitle() {
|
||||
if (!props.title) return null;
|
||||
return <StyledTitle color={props.color}>{props.title}</StyledTitle>;
|
||||
if (!title) return null;
|
||||
return <StyledTitle color={color}>{title}</StyledTitle>;
|
||||
}
|
||||
|
||||
function onClick() {
|
||||
if (props.disabled) return;
|
||||
props.onClick();
|
||||
if (disabled) return;
|
||||
propsOnClick();
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledButton
|
||||
ref={ref}
|
||||
fontSize={props.fontSize}
|
||||
isSquare={props.isSquare}
|
||||
size={props.size}
|
||||
style={props.style}
|
||||
disabled={props.disabled}
|
||||
title={props.tooltip}
|
||||
className={props.className}
|
||||
fontSize={fontSize}
|
||||
isSquare={isSquare}
|
||||
disabled={disabled}
|
||||
title={tooltip}
|
||||
iconOnly={iconOnly}
|
||||
onClick={onClick}
|
||||
|
||||
// When there's no title, the button needs a label. In this case, fall back
|
||||
// to the tooltip.
|
||||
aria-label={props.title ? undefined : props.tooltip}
|
||||
aria-disabled={props.disabled}
|
||||
aria-expanded={props['aria-expanded']}
|
||||
aria-controls={props['aria-controls']}
|
||||
aria-label={title ? undefined : tooltip}
|
||||
aria-disabled={disabled}
|
||||
{...unusedProps}
|
||||
>
|
||||
{renderIcon()}
|
||||
{renderTitle()}
|
||||
|
@@ -9,6 +9,7 @@ import ClipperServer from '@joplin/lib/ClipperServer';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import { AppState } from '../app.reducer';
|
||||
import shim, { MessageBoxType } from '@joplin/lib/shim';
|
||||
|
||||
class ClipperConfigScreenComponent extends React.Component {
|
||||
public constructor() {
|
||||
@@ -30,7 +31,7 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
private copyToken_click() {
|
||||
clipboard.writeText(this.props.apiToken);
|
||||
|
||||
alert(_('Token has been copied to the clipboard!'));
|
||||
void shim.showMessageBox(_('Token has been copied to the clipboard!'), { type: MessageBoxType.Info });
|
||||
}
|
||||
|
||||
private renewToken_click() {
|
||||
|
@@ -17,7 +17,7 @@ interface Props {
|
||||
onApplyClick?: Function;
|
||||
}
|
||||
|
||||
export const StyledRoot = styled.div`
|
||||
const StyledRoot = styled.nav`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
|
@@ -19,6 +19,7 @@ import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/conf
|
||||
import MacOSMissingPasswordHelpLink from './controls/MissingPasswordHelpLink';
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
import SettingComponent, { UpdateSettingValueEvent } from './controls/SettingComponent';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
|
||||
interface Font {
|
||||
@@ -144,7 +145,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
screenName = section.name;
|
||||
|
||||
if (this.hasChanges()) {
|
||||
const ok = confirm(_('This will open a new screen. Save your current changes?'));
|
||||
const ok = await shim.showConfirmationDialog(_('This will open a new screen. Save your current changes?'));
|
||||
if (ok) {
|
||||
await shared.saveSettings(this);
|
||||
}
|
||||
|
@@ -164,7 +164,7 @@ export default function Sidebar(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledRoot role='tablist'>
|
||||
<StyledRoot className='settings-sidebar _scrollbar2' role='tablist'>
|
||||
{buttons}
|
||||
</StyledRoot>
|
||||
);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import React = require('react');
|
||||
import { useMemo, useState, useCallback, CSSProperties, useEffect, useRef } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useState, useCallback, CSSProperties, useEffect } from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { SettingItemSubType } from '@joplin/lib/models/Setting';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
import InlineCombobox from '../../InlineCombobox';
|
||||
|
||||
interface Props {
|
||||
type: string;
|
||||
@@ -17,14 +17,8 @@ interface Props {
|
||||
const FontSearch = (props: Props) => {
|
||||
const { type, style, value, availableFonts, onChange, subtype } = props;
|
||||
const [filteredAvailableFonts, setFilteredAvailableFonts] = useState(availableFonts);
|
||||
const [inputText, setInputText] = useState(value);
|
||||
const [showList, setShowList] = useState(false);
|
||||
const [isListHovered, setIsListHovered] = useState(false);
|
||||
const [isFontSelected, setIsFontSelected] = useState(value !== '');
|
||||
const [visibleFonts, setVisibleFonts] = useState<string[]>([]);
|
||||
const [isMonoBoxChecked, setIsMonoBoxChecked] = useState(false);
|
||||
const isLoadingFonts = filteredAvailableFonts.length === 0;
|
||||
const fontInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (subtype === SettingItemSubType.MonospaceFontFamily) {
|
||||
@@ -41,112 +35,34 @@ const FontSearch = (props: Props) => {
|
||||
setFilteredAvailableFonts(localMonospacedFonts);
|
||||
}, [isMonoBoxChecked, availableFonts]);
|
||||
|
||||
const displayedFonts = useMemo(() => {
|
||||
if (isFontSelected) return filteredAvailableFonts;
|
||||
return filteredAvailableFonts.filter((font: string) =>
|
||||
font.toLowerCase().startsWith(inputText.toLowerCase()),
|
||||
);
|
||||
}, [filteredAvailableFonts, inputText, isFontSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
setVisibleFonts(displayedFonts.slice(0, 20));
|
||||
}, [displayedFonts]);
|
||||
|
||||
// Lazy loading
|
||||
const handleListScroll: React.UIEventHandler<HTMLDivElement> = useCallback((event) => {
|
||||
const scrollTop = (event.target as HTMLDivElement).scrollTop;
|
||||
const scrollHeight = (event.target as HTMLDivElement).scrollHeight;
|
||||
const clientHeight = (event.target as HTMLDivElement).clientHeight;
|
||||
|
||||
// Check if the user has scrolled to the bottom of the container
|
||||
// A small buffer of 20 pixels is subtracted from the total scrollHeight to ensure new content starts loading slightly before the user reaches the absolute bottom, providing a smoother experience.
|
||||
if (scrollTop + clientHeight >= scrollHeight - 20) {
|
||||
// Load the next 20 fonts
|
||||
const remainingFonts = displayedFonts.slice(visibleFonts.length, visibleFonts.length + 20);
|
||||
|
||||
setVisibleFonts([...visibleFonts, ...remainingFonts]);
|
||||
}
|
||||
}, [displayedFonts, visibleFonts]);
|
||||
|
||||
const handleTextChange: React.ChangeEventHandler<HTMLInputElement> = useCallback((event) => {
|
||||
setIsFontSelected(false);
|
||||
setInputText(event.target.value);
|
||||
onChange(event.target.value);
|
||||
}, [onChange]);
|
||||
|
||||
const handleFocus: React.FocusEventHandler<HTMLInputElement> = useCallback(() => setShowList(true), []);
|
||||
|
||||
const handleBlur: React.FocusEventHandler<HTMLInputElement> = useCallback(() => {
|
||||
if (!isListHovered) {
|
||||
setShowList(false);
|
||||
}
|
||||
}, [isListHovered]);
|
||||
|
||||
const handleFontClick: React.MouseEventHandler<HTMLDivElement> = useCallback((event) => {
|
||||
const font = (event.target as HTMLDivElement).innerText;
|
||||
setInputText(font);
|
||||
setShowList(false);
|
||||
onChange(font);
|
||||
setIsFontSelected(true);
|
||||
}, [onChange]);
|
||||
|
||||
const handleListHover: React.MouseEventHandler<HTMLDivElement> = useCallback(() => setIsListHovered(true), []);
|
||||
|
||||
const handleListLeave: React.MouseEventHandler<HTMLDivElement> = useCallback(() => setIsListHovered(false), []);
|
||||
|
||||
const handleMonoBoxCheck: React.ChangeEventHandler<HTMLInputElement> = useCallback(() => {
|
||||
setIsMonoBoxChecked(!isMonoBoxChecked);
|
||||
focus('FontSearch::fontInputRef', fontInputRef.current);
|
||||
}, [isMonoBoxChecked]);
|
||||
|
||||
return (
|
||||
<>
|
||||
const comboboxControls = <>
|
||||
{isLoadingFonts ? _('Loading...') : null}
|
||||
<div className='monospace-checkbox'>
|
||||
<input
|
||||
type={type}
|
||||
style={style}
|
||||
value={inputText}
|
||||
onChange={handleTextChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
spellCheck={false}
|
||||
id={props.inputId}
|
||||
ref={fontInputRef}
|
||||
type='checkbox'
|
||||
checked={isMonoBoxChecked}
|
||||
onChange={handleMonoBoxCheck}
|
||||
id={`show-monospace-fonts_${subtype}`}
|
||||
/>
|
||||
<div
|
||||
className={'font-search-list'}
|
||||
style={{ display: showList ? 'block' : 'none' }}
|
||||
onMouseEnter={handleListHover}
|
||||
onMouseLeave={handleListLeave}
|
||||
onScroll={handleListScroll}
|
||||
>
|
||||
{
|
||||
isLoadingFonts ? <div>{_('Loading...')}</div> :
|
||||
<>
|
||||
<div className='monospace-checkbox'>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={isMonoBoxChecked}
|
||||
onChange={handleMonoBoxCheck}
|
||||
id={`show-monospace-fonts_${subtype}`}
|
||||
/>
|
||||
<label htmlFor={`show-monospace-fonts_${subtype}`}>{_('Show monospace fonts only.')}</label>
|
||||
</div>
|
||||
{
|
||||
visibleFonts.map((font: string) =>
|
||||
<div
|
||||
key={font}
|
||||
style={{ fontFamily: `"${font}"` }}
|
||||
onClick={handleFontClick}
|
||||
className='font-search-item'
|
||||
>
|
||||
{font}
|
||||
</div>,
|
||||
)
|
||||
}
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
<label htmlFor={`show-monospace-fonts_${subtype}`}>{_('Show monospace fonts only.')}</label>
|
||||
</div>
|
||||
</>;
|
||||
|
||||
return (
|
||||
<InlineCombobox
|
||||
inputType={type}
|
||||
inputStyle={style}
|
||||
value={value}
|
||||
suggestedValues={filteredAvailableFonts}
|
||||
renderOption={font => <span style={{ fontFamily: font }}>{font}</span>}
|
||||
controls={comboboxControls}
|
||||
onChange={onChange}
|
||||
inputId={props.inputId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
@use "./setting-description.scss";
|
||||
@use "./setting-label.scss";
|
||||
@use "./setting-header.scss";
|
||||
|
@@ -63,23 +63,70 @@ const Dialog: React.FC<Props> = props => {
|
||||
</div>;
|
||||
};
|
||||
|
||||
// We keep track of the mouse events to allow the action to be cancellable on the mouseup
|
||||
// If dialogElement is the source of the mouse event it means
|
||||
// that the user clicked in the dimmed background and not in the content of the dialog
|
||||
const useClickedOutsideContent = (dialogElement: HTMLDialogElement|null) => {
|
||||
const mouseDownOutsideContent = useRef(false);
|
||||
mouseDownOutsideContent.current = false;
|
||||
const [clickedOutsideContent, setClickedOutsideContent] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dialogElement) return () => {};
|
||||
|
||||
const mouseDownListener = (event: MouseEvent) => {
|
||||
if (event.target === dialogElement) {
|
||||
mouseDownOutsideContent.current = true;
|
||||
} else {
|
||||
mouseDownOutsideContent.current = false;
|
||||
}
|
||||
};
|
||||
const mouseUpListener = (event: MouseEvent) => {
|
||||
if (!mouseDownOutsideContent.current) return;
|
||||
if (mouseDownOutsideContent.current && event.target === dialogElement) {
|
||||
setClickedOutsideContent(true);
|
||||
mouseDownOutsideContent.current = false;
|
||||
} else {
|
||||
setClickedOutsideContent(false);
|
||||
mouseDownOutsideContent.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
dialogElement.addEventListener('mousedown', mouseDownListener);
|
||||
dialogElement.addEventListener('mouseup', mouseUpListener);
|
||||
|
||||
return () => {
|
||||
dialogElement.removeEventListener('mousedown', mouseDownListener);
|
||||
dialogElement.removeEventListener('mouseup', mouseUpListener);
|
||||
};
|
||||
}, [dialogElement]);
|
||||
|
||||
return [clickedOutsideContent, setClickedOutsideContent] as const;
|
||||
};
|
||||
|
||||
const useDialogElement = (containerDocument: Document, onCancel: undefined|OnCancelListener) => {
|
||||
const [dialogElement, setDialogElement] = useState<HTMLDialogElement|null>(null);
|
||||
|
||||
const onCancelRef = useRef(onCancel);
|
||||
onCancelRef.current = onCancel;
|
||||
|
||||
const [clickedOutsideContent, setClickedOutsideContent] = useClickedOutsideContent(dialogElement);
|
||||
|
||||
useEffect(() => {
|
||||
if (clickedOutsideContent) {
|
||||
const onCancel = onCancelRef.current;
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
} else {
|
||||
setClickedOutsideContent(false);
|
||||
}
|
||||
}
|
||||
}, [clickedOutsideContent, setClickedOutsideContent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerDocument) return () => {};
|
||||
|
||||
const dialog = containerDocument.createElement('dialog');
|
||||
dialog.addEventListener('click', event => {
|
||||
const onCancel = onCancelRef.current;
|
||||
const isBackgroundClick = event.target === dialog;
|
||||
if (isBackgroundClick && onCancel) {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
dialog.classList.add('dialog-modal-layer');
|
||||
dialog.addEventListener('cancel', event => {
|
||||
const canCancel = !!onCancelRef.current;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const Root = styled.div<any>`
|
||||
const Root = styled.h1<any>`
|
||||
display: flex;
|
||||
justify-content: ${props => props.justifyContent ? props.justifyContent : 'center'};
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
|
@@ -3,7 +3,7 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import time from '@joplin/lib/time';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import shim, { MessageBoxType } from '@joplin/lib/shim';
|
||||
import dialogs from '../dialogs';
|
||||
import { decryptedStatText, determineKeyPassword, dontReencryptData, enableEncryptionConfirmationMessages, onSavePasswordClick, onToggleEnabledClick, reencryptData, upgradeMasterKey, useInputPasswords, useNeedMasterPassword, usePasswordChecker, useStats, useToggleShowDisabledMasterKeys } from '@joplin/lib/components/EncryptionConfigScreen/utils';
|
||||
import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types';
|
||||
@@ -47,7 +47,7 @@ const EncryptionConfigScreen = (props: Props) => {
|
||||
const onUpgradeMasterKey = useCallback(async (mk: MasterKeyEntity) => {
|
||||
const password = determineKeyPassword(mk.id, masterPasswordKeys, props.masterPassword, props.passwords);
|
||||
const result = await upgradeMasterKey(mk, password);
|
||||
alert(result);
|
||||
await shim.showMessageBox(result, { type: MessageBoxType.Info });
|
||||
}, [props.passwords, masterPasswordKeys, props.masterPassword]);
|
||||
|
||||
const renderNeedUpgradeSection = () => {
|
||||
|
199
packages/app-desktop/gui/InlineCombobox.tsx
Normal file
@@ -0,0 +1,199 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useCallback, CSSProperties, useEffect, useRef, useId } from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
import ItemList from './ItemList';
|
||||
|
||||
interface Props {
|
||||
inputType?: string;
|
||||
inputStyle: CSSProperties;
|
||||
|
||||
value: string;
|
||||
onChange: (newValue: string)=> void;
|
||||
|
||||
suggestedValues: string[];
|
||||
renderOption: (suggestedValue: string)=> React.ReactElement;
|
||||
|
||||
controls?: React.ReactNode;
|
||||
|
||||
inputId: string;
|
||||
}
|
||||
|
||||
const suggestionMatchesFilter = (suggestion: string, filter: string) => {
|
||||
return suggestion.toLowerCase().startsWith(filter.toLowerCase());
|
||||
};
|
||||
|
||||
const InlineCombobox: React.FC<Props> = ({ inputType, controls, inputStyle, value, suggestedValues, renderOption, onChange, inputId }) => {
|
||||
const [showList, setShowList] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement|null>(null);
|
||||
const inputRef = useRef<HTMLInputElement|null>(null);
|
||||
const listboxRef = useRef<ItemList<string>|null>(null);
|
||||
|
||||
const [filteredSuggestions, setFilteredSuggestions] = useState(suggestedValues);
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredSuggestions(suggestedValues);
|
||||
}, [suggestedValues]);
|
||||
|
||||
const selectedIndex = filteredSuggestions.indexOf(value);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedIndex >= 0 && showList) {
|
||||
listboxRef.current?.makeItemIndexVisible(selectedIndex);
|
||||
}
|
||||
}, [selectedIndex, showList]);
|
||||
|
||||
const focusInput = useCallback(() => {
|
||||
focus('ComboBox/focus input', inputRef.current);
|
||||
}, []);
|
||||
|
||||
const onTextChange: React.ChangeEventHandler<HTMLInputElement> = useCallback((event) => {
|
||||
const newValue = event.target.value;
|
||||
onChange(newValue);
|
||||
setShowList(true);
|
||||
|
||||
const filteredSuggestions = suggestedValues.filter((suggestion: string) =>
|
||||
suggestionMatchesFilter(suggestion, newValue),
|
||||
);
|
||||
// If no suggestions, show all fonts
|
||||
setFilteredSuggestions(filteredSuggestions.length > 0 ? filteredSuggestions : suggestedValues);
|
||||
}, [onChange, suggestedValues]);
|
||||
|
||||
const onFocus: React.FocusEventHandler<HTMLElement> = useCallback(() => {
|
||||
setShowList(true);
|
||||
}, []);
|
||||
|
||||
const onBlur = useCallback((event: React.FocusEvent) => {
|
||||
const hasHoverOrFocus = !!containerRef.current.querySelector(':focus-within, :hover');
|
||||
const movesToContainedItem = containerRef.current.contains(event.relatedTarget);
|
||||
if (!hasHoverOrFocus && !movesToContainedItem) {
|
||||
setShowList(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onItemClick: React.MouseEventHandler<HTMLDivElement> = useCallback((event) => {
|
||||
const newValue = event.currentTarget.getAttribute('data-key');
|
||||
if (!newValue) return;
|
||||
|
||||
focusInput();
|
||||
onChange(newValue);
|
||||
setFilteredSuggestions(suggestedValues);
|
||||
setShowList(false);
|
||||
}, [onChange, suggestedValues, focusInput]);
|
||||
|
||||
const onKeyDown: React.KeyboardEventHandler<HTMLInputElement> = useCallback(event => {
|
||||
if (event.nativeEvent.isComposing) return;
|
||||
|
||||
let closestIndex = selectedIndex;
|
||||
if (selectedIndex === -1) {
|
||||
closestIndex = filteredSuggestions.findIndex(suggestion => {
|
||||
return suggestionMatchesFilter(suggestion, value);
|
||||
});
|
||||
}
|
||||
|
||||
const isGoToNext = event.code === 'ArrowDown';
|
||||
if (isGoToNext || event.code === 'ArrowUp') {
|
||||
event.preventDefault();
|
||||
|
||||
if (!event.altKey) {
|
||||
let newSelectedIndex;
|
||||
if (isGoToNext) {
|
||||
newSelectedIndex = (selectedIndex + 1) % filteredSuggestions.length;
|
||||
} else {
|
||||
newSelectedIndex = selectedIndex - 1;
|
||||
if (newSelectedIndex < 0) {
|
||||
newSelectedIndex += filteredSuggestions.length;
|
||||
}
|
||||
}
|
||||
const newKey = filteredSuggestions[newSelectedIndex];
|
||||
onChange(newKey);
|
||||
}
|
||||
setShowList(true);
|
||||
} else if (event.code === 'Enter') {
|
||||
event.preventDefault();
|
||||
onChange(filteredSuggestions[closestIndex]);
|
||||
setShowList(false);
|
||||
} else if (event.code === 'Escape') {
|
||||
event.preventDefault();
|
||||
setShowList(false);
|
||||
}
|
||||
}, [filteredSuggestions, value, selectedIndex, onChange]);
|
||||
|
||||
const valuesListId = useId();
|
||||
|
||||
const itemId = (index: number) => {
|
||||
if (index < 0) {
|
||||
return undefined;
|
||||
} else {
|
||||
return `combobox-${valuesListId}-option-${index}`;
|
||||
}
|
||||
};
|
||||
const onRenderItem = (key: string, index: number) => {
|
||||
const selected = key === value;
|
||||
const id = itemId(index);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
data-key={key}
|
||||
className={`combobox-suggestion-option ${selected ? '-selected' : ''}`}
|
||||
role='option'
|
||||
aria-posinset={1 + index}
|
||||
aria-setsize={filteredSuggestions.length}
|
||||
onClick={onItemClick}
|
||||
aria-selected={selected}
|
||||
id={id}
|
||||
>{renderOption(key)}</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`combobox-wrapper ${showList ? '-expanded' : ''}`}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
|
||||
onKeyDown={onKeyDown}
|
||||
ref={containerRef}
|
||||
>
|
||||
<input
|
||||
type={inputType ?? 'text'}
|
||||
style={inputStyle}
|
||||
value={value}
|
||||
onChange={onTextChange}
|
||||
onKeyDown={onKeyDown}
|
||||
spellCheck={false}
|
||||
id={inputId}
|
||||
ref={inputRef}
|
||||
|
||||
role='combobox'
|
||||
aria-autocomplete='list'
|
||||
aria-controls={valuesListId}
|
||||
aria-expanded={showList}
|
||||
aria-activedescendant={itemId(selectedIndex)}
|
||||
/>
|
||||
<div className='suggestions'>
|
||||
{
|
||||
// Custom controls
|
||||
controls
|
||||
}
|
||||
<ItemList
|
||||
role='listbox'
|
||||
aria-label={_('Suggestions')}
|
||||
style={{ height: 200 }}
|
||||
itemHeight={26}
|
||||
|
||||
alwaysRenderSelection={true}
|
||||
selectedIndex={selectedIndex >= 0 ? selectedIndex : undefined}
|
||||
|
||||
items={filteredSuggestions}
|
||||
itemRenderer={onRenderItem}
|
||||
id={valuesListId}
|
||||
ref={listboxRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineCombobox;
|
@@ -43,6 +43,7 @@ import UpdateNotification from './UpdateNotification/UpdateNotification';
|
||||
import NoteEditor from './NoteEditor/NoteEditor';
|
||||
import PluginNotification from './PluginNotification/PluginNotification';
|
||||
import { Toast } from '@joplin/lib/services/plugins/api/types';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
@@ -121,6 +122,18 @@ const defaultLayout: LayoutItem = {
|
||||
],
|
||||
};
|
||||
|
||||
const layoutKeyToLabel = (key: string, plugins: PluginStates) => {
|
||||
if (key === 'sideBar') return _('Sidebar');
|
||||
if (key === 'noteList') return _('Note list');
|
||||
if (key === 'editor') return _('Editor');
|
||||
|
||||
const viewInfo = pluginUtils.viewInfoByViewId(plugins, key);
|
||||
if (viewInfo) {
|
||||
return PluginService.instance().safePluginNameById(viewInfo.plugin.id);
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
class MainScreenComponent extends React.Component<Props, State> {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
@@ -567,7 +580,14 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
|
||||
return (
|
||||
<div style={styles.messageBox}>
|
||||
<span style={theme.textStyle}>{msg}</span>
|
||||
<span
|
||||
style={theme.textStyle}
|
||||
role='alert'
|
||||
// role='alert' has an implicit aria-live='assertive', which tells screen readers that changes
|
||||
// to the warning's content should be announced as soon as possible. However, since it's generally
|
||||
// okay for announcements related to these notifications to be delayed, use aria-live='polite'.
|
||||
aria-live='polite'
|
||||
>{msg}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -728,6 +748,10 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private layoutKeyToLabel = (key: string) => {
|
||||
return layoutKeyToLabel(key, this.props.plugins);
|
||||
};
|
||||
|
||||
public render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = {
|
||||
@@ -746,6 +770,7 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
onResize={this.resizableLayout_resize}
|
||||
onMoveButtonClick={this.resizableLayout_moveButtonClick}
|
||||
renderItem={this.resizableLayout_renderItem}
|
||||
layoutKeyToLabel={this.layoutKeyToLabel}
|
||||
moveMode={this.props.layoutMoveMode}
|
||||
moveModeMessage={_('Use the arrows to move the layout items. Press "Escape" to exit.')}
|
||||
/>
|
||||
|
@@ -11,6 +11,7 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import KvStore from '@joplin/lib/services/KvStore';
|
||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||
import LabelledPasswordInput from '../PasswordInput/LabelledPasswordInput';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
@@ -80,7 +81,7 @@ export default function(props: Props) {
|
||||
void reg.waitForSyncFinishedThenSync();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
void shim.showErrorDialog(error.message);
|
||||
} finally {
|
||||
setUpdatingPassword(false);
|
||||
}
|
||||
|
@@ -165,6 +165,7 @@ interface Props {
|
||||
showNoteCounts: boolean;
|
||||
uncompletedTodosOnTop: boolean;
|
||||
showCompletedTodos: boolean;
|
||||
tabMovesFocus: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
pluginMenuItems: any[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
@@ -256,6 +257,7 @@ function useMenuStates(menu: any, props: Props) {
|
||||
menuItemSetChecked('showNoteCounts', props.showNoteCounts);
|
||||
menuItemSetChecked('uncompletedTodosOnTop', props.uncompletedTodosOnTop);
|
||||
menuItemSetChecked('showCompletedTodos', props.showCompletedTodos);
|
||||
menuItemSetChecked('toggleTabMovesFocus', props.tabMovesFocus);
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(scheduleUpdate, 150);
|
||||
@@ -276,6 +278,7 @@ function useMenuStates(menu: any, props: Props) {
|
||||
props['notes.sortOrder.reverse'],
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
props['folders.sortOrder.reverse'],
|
||||
props.tabMovesFocus,
|
||||
props.noteListRendererId,
|
||||
props.showNoteCounts,
|
||||
props.uncompletedTodosOnTop,
|
||||
@@ -476,6 +479,7 @@ function useMenu(props: Props) {
|
||||
menuItemDic.focusElementNoteList,
|
||||
menuItemDic.focusElementNoteTitle,
|
||||
menuItemDic.focusElementNoteBody,
|
||||
menuItemDic.focusElementToolbar,
|
||||
];
|
||||
|
||||
const importItems = [];
|
||||
@@ -824,6 +828,12 @@ function useMenu(props: Props) {
|
||||
},
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
...menuItemDic['toggleTabMovesFocus'],
|
||||
label: Setting.settingMetadata('editor.tabMovesFocus').label(),
|
||||
type: 'checkbox',
|
||||
},
|
||||
separator(),
|
||||
{
|
||||
label: _('Actual Size'),
|
||||
click: () => {
|
||||
@@ -877,7 +887,9 @@ function useMenu(props: Props) {
|
||||
note: {
|
||||
label: _('&Note'),
|
||||
submenu: [
|
||||
menuItemDic.openNoteInNewWindow,
|
||||
menuItemDic.toggleExternalEditing,
|
||||
separator(),
|
||||
menuItemDic.setTags,
|
||||
menuItemDic.showShareNoteDialog,
|
||||
separator(),
|
||||
@@ -987,6 +999,7 @@ function useMenu(props: Props) {
|
||||
|
||||
rootMenus.go.submenu.push(menuItemDic.gotoAnything);
|
||||
rootMenus.tools.submenu.push(menuItemDic.commandPalette);
|
||||
rootMenus.tools.submenu.push(menuItemDic.linkToNote);
|
||||
rootMenus.tools.submenu.push(menuItemDic.openMasterPasswordDialog);
|
||||
|
||||
for (const view of props.pluginMenuItems) {
|
||||
@@ -1143,6 +1156,7 @@ const mapStateToProps = (state: AppState): Partial<Props> => {
|
||||
['folders.sortOrder.field']: state.settings['folders.sortOrder.field'],
|
||||
['notes.sortOrder.reverse']: state.settings['notes.sortOrder.reverse'],
|
||||
['folders.sortOrder.reverse']: state.settings['folders.sortOrder.reverse'],
|
||||
tabMovesFocus: state.settings['editor.tabMovesFocus'],
|
||||
pluginSettings: state.settings['plugins.states'],
|
||||
showNoteCounts: state.settings.showNoteCounts,
|
||||
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
|
||||
|
@@ -32,6 +32,14 @@ function styles_(props: MultiNoteActionsProps) {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
divider: {
|
||||
borderTopWidth: 1,
|
||||
borderTopStyle: 'solid',
|
||||
borderTopColor: theme.dividerColor,
|
||||
width: '100%',
|
||||
height: 1,
|
||||
marginBottom: 10,
|
||||
},
|
||||
button: {
|
||||
...theme.buttonStyle,
|
||||
marginBottom: 10,
|
||||
@@ -68,11 +76,17 @@ export default function MultiNoteActions(props: MultiNoteActionsProps) {
|
||||
const item = menuItems[i];
|
||||
if (!item.enabled) continue;
|
||||
|
||||
itemComps.push(
|
||||
<button key={item.label} style={styles.button} onClick={() => multiNotesButton_click(item)}>
|
||||
{item.label}
|
||||
</button>,
|
||||
);
|
||||
if (item.type === 'separator') {
|
||||
itemComps.push(
|
||||
<div key={`divider${i}`} style={styles.divider}/>,
|
||||
);
|
||||
} else {
|
||||
itemComps.push(
|
||||
<button key={item.label} style={styles.button} onClick={() => multiNotesButton_click(item)}>
|
||||
{item.label}
|
||||
</button>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@@ -1,50 +1,66 @@
|
||||
import * as React from 'react';
|
||||
const { connect } = require('react-redux');
|
||||
import { connect } from 'react-redux';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { AppState, AppStateRoute } from '../app.reducer';
|
||||
import bridge from '../services/bridge';
|
||||
import { useContext, useEffect, useRef } from 'react';
|
||||
import { useContext, useEffect, useMemo, useRef } from 'react';
|
||||
import { WindowIdContext } from './NewWindowOrIFrame';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of code from before rule was applied
|
||||
type ScreenProps = any;
|
||||
|
||||
interface AppScreen {
|
||||
screen: React.ComponentType<ScreenProps>;
|
||||
title?: ()=> string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
route: AppStateRoute;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
screens: Record<string, any>;
|
||||
screens: Record<string, AppScreen>;
|
||||
|
||||
style: React.CSSProperties;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const NavigatorComponent: React.FC<Props> = props => {
|
||||
const useWindowTitleManager = (screenInfo: AppScreen) => {
|
||||
const windowTitle = useMemo(() => {
|
||||
const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : '';
|
||||
const windowTitle = [`Joplin${devMarker}`];
|
||||
if (screenInfo?.title) {
|
||||
windowTitle.push(screenInfo.title());
|
||||
}
|
||||
return windowTitle.join(' - ');
|
||||
}, [screenInfo]);
|
||||
|
||||
const windowId = useContext(WindowIdContext);
|
||||
useEffect(() => {
|
||||
bridge().windowById(windowId)?.setTitle(windowTitle);
|
||||
}, [windowTitle, windowId]);
|
||||
};
|
||||
|
||||
const useWindowRefocusManager = (route: AppStateRoute) => {
|
||||
const windowId = useContext(WindowIdContext);
|
||||
|
||||
const route = props.route;
|
||||
const screenInfo = props.screens[route?.routeName];
|
||||
|
||||
const screensRef = useRef(props.screens);
|
||||
screensRef.current = props.screens;
|
||||
|
||||
const prevRoute = useRef<AppStateRoute|null>(null);
|
||||
const prevRouteName = useRef<string|null>(null);
|
||||
const routeName = route?.routeName;
|
||||
useEffect(() => {
|
||||
const routeName = route?.routeName;
|
||||
if (route) {
|
||||
const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : '';
|
||||
const windowTitle = [`Joplin${devMarker}`];
|
||||
if (screenInfo.title) {
|
||||
windowTitle.push(screenInfo.title());
|
||||
}
|
||||
bridge().windowById(windowId)?.setTitle(windowTitle.join(' - '));
|
||||
}
|
||||
|
||||
// When a navigation happens in an unfocused window, show the window to the user.
|
||||
// This might happen if, for example, a secondary window triggers a navigation in
|
||||
// the main window.
|
||||
if (routeName && routeName !== prevRoute.current?.routeName) {
|
||||
if (routeName && routeName !== prevRouteName.current) {
|
||||
bridge().switchToWindow(windowId);
|
||||
}
|
||||
|
||||
prevRoute.current = route;
|
||||
}, [route, screenInfo, windowId]);
|
||||
prevRouteName.current = routeName;
|
||||
}, [routeName, windowId]);
|
||||
};
|
||||
|
||||
const NavigatorComponent: React.FC<Props> = props => {
|
||||
const route = props.route;
|
||||
const screenInfo = props.screens[route?.routeName];
|
||||
|
||||
useWindowTitleManager(screenInfo);
|
||||
useWindowRefocusManager(route);
|
||||
|
||||
if (!route) throw new Error('Route must not be null');
|
||||
|
||||
|
@@ -32,7 +32,9 @@ function Toolbar(props: ToolbarProps) {
|
||||
const styles = styles_(props);
|
||||
return (
|
||||
<ToolbarBase
|
||||
id="CodeMirrorToolbar"
|
||||
style={styles.root}
|
||||
scrollable={true}
|
||||
items={props.toolbarButtonInfos}
|
||||
disabled={!!props.disabled}
|
||||
aria-label={_('Editor actions')}
|
||||
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHandle, ForwardedRef, useContext } from 'react';
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { EditorCommand, MarkupToHtmlOptions, NoteBodyEditorProps, NoteBodyEditorRef } from '../../../utils/types';
|
||||
import { EditorCommand, NoteBodyEditorProps, NoteBodyEditorRef } from '../../../utils/types';
|
||||
import { commandAttachFileToBody, getResourcesFromPasteEvent } from '../../../utils/resourceHandling';
|
||||
import { ScrollOptions, ScrollOptionTypes } from '../../../utils/types';
|
||||
import { CommandValue } from '../../../utils/types';
|
||||
@@ -34,6 +34,7 @@ import useWebviewIpcMessage from '../utils/useWebviewIpcMessage';
|
||||
import useEditorSearchHandler from '../utils/useEditorSearchHandler';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
import { WindowIdContext } from '../../../../NewWindowOrIFrame';
|
||||
import { MarkupToHtmlOptions } from '../../../../hooks/useMarkupToHtml';
|
||||
|
||||
function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions {
|
||||
return { ...override };
|
||||
@@ -48,7 +49,10 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
const [webviewReady, setWebviewReady] = useState(false);
|
||||
|
||||
const editorRef = useRef(null);
|
||||
const rootRef = useRef(null);
|
||||
const [editorRoot, setEditorRoot] = useState<HTMLDivElement|null>(null);
|
||||
const rootRef = useRef<HTMLDivElement|null>(null);
|
||||
rootRef.current = editorRoot;
|
||||
|
||||
const webviewRef = useRef(null);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
const props_onChangeRef = useRef<Function>(null);
|
||||
@@ -410,6 +414,8 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
}, [styles.editor.codeMirrorTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editorRoot) return () => {};
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
// Selection in dark mode is hard to see so make it brighter.
|
||||
@@ -431,10 +437,11 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
max-width: ${props.contentMaxWidth}px !important;
|
||||
` : '';
|
||||
|
||||
const element = document.createElement('style');
|
||||
const ownerDoc = editorRoot.ownerDocument;
|
||||
const element = ownerDoc.createElement('style');
|
||||
element.setAttribute('id', 'codemirrorStyle');
|
||||
document.head.appendChild(element);
|
||||
element.appendChild(document.createTextNode(`
|
||||
ownerDoc.head.appendChild(element);
|
||||
element.appendChild(ownerDoc.createTextNode(`
|
||||
/* These must be important to prevent the codemirror defaults from taking over*/
|
||||
.CodeMirror {
|
||||
font-family: monospace;
|
||||
@@ -449,6 +456,11 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
line-height: ${theme.lineHeight} !important;
|
||||
}
|
||||
|
||||
.CodeMirror-code:focus-visible {
|
||||
/* Avoid showing additional focus-visible decoration */
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.CodeMirror-lines {
|
||||
/* This is used to enable the scroll-past end behaviour. The same height should */
|
||||
/* be applied to the viewer. */
|
||||
@@ -591,10 +603,9 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
`));
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(element);
|
||||
ownerDoc.head.removeChild(element);
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [props.themeId, props.contentMaxWidth]);
|
||||
}, [props.themeId, props.contentMaxWidth, props.fontSize, editorRoot]);
|
||||
|
||||
const webview_domReady = useCallback(() => {
|
||||
setWebviewReady(true);
|
||||
@@ -774,7 +785,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
const windowId = useContext(WindowIdContext);
|
||||
return (
|
||||
<ErrorBoundary message="The text editor encountered a fatal error and could not continue. The error might be due to a plugin, so please try to disable some of them and try again.">
|
||||
<div style={styles.root} ref={rootRef}>
|
||||
<div style={styles.root} ref={setEditorRoot}>
|
||||
<div style={styles.rowToolbar}>
|
||||
<Toolbar themeId={props.themeId} windowId={windowId}/>
|
||||
{props.noteToolbar}
|
||||
|
@@ -358,6 +358,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
return {
|
||||
language: isHTMLNote ? EditorLanguageType.Html : EditorLanguageType.Markdown,
|
||||
readOnly: props.disabled,
|
||||
markdownMarkEnabled: Setting.value('markdown.plugin.mark'),
|
||||
katexEnabled: Setting.value('markdown.plugin.katex'),
|
||||
themeData: {
|
||||
...styles.globalTheme,
|
||||
@@ -372,18 +373,23 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
spellcheckEnabled: Setting.value('editor.spellcheckBeta'),
|
||||
keymap: keyboardMode,
|
||||
indentWithTabs: true,
|
||||
tabMovesFocus: props.tabMovesFocus,
|
||||
editorLabel: _('Markdown editor'),
|
||||
};
|
||||
}, [
|
||||
props.contentMarkupLanguage, props.disabled, props.keyboardMode, styles.globalTheme,
|
||||
props.tabMovesFocus,
|
||||
]);
|
||||
|
||||
// Update the editor's value
|
||||
useEffect(() => {
|
||||
if (editorRef.current?.updateBody(props.content)) {
|
||||
// Include the noteId in the update props to give plugins access
|
||||
// to the current note ID.
|
||||
const updateProps = { noteId: props.noteId };
|
||||
if (editorRef.current?.updateBody(props.content, updateProps)) {
|
||||
editorRef.current?.clearHistory();
|
||||
}
|
||||
}, [props.content]);
|
||||
}, [props.content, props.noteId]);
|
||||
|
||||
const renderEditor = () => {
|
||||
return (
|
||||
@@ -391,6 +397,7 @@ const CodeMirror = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditor
|
||||
<Editor
|
||||
style={styles.editor}
|
||||
initialText={props.content}
|
||||
initialNoteId={props.noteId}
|
||||
ref={editorRef}
|
||||
settings={editorSettings}
|
||||
pluginStates={props.plugins}
|
||||
|
@@ -19,12 +19,11 @@ import { MarkupLanguage, MarkupToHtml } from '@joplin/renderer';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import setupToolbarButtons from './utils/setupToolbarButtons';
|
||||
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
|
||||
import openEditDialog from './utils/openEditDialog';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { loadScript } from '../../../utils/loadScript';
|
||||
import bridge from '../../../../services/bridge';
|
||||
import { TinyMceEditorEvents } from './utils/types';
|
||||
import type { Editor } from 'tinymce';
|
||||
import type { Editor, EditorEvent } from 'tinymce';
|
||||
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
|
||||
import shouldPasteResources from './utils/shouldPasteResources';
|
||||
import lightTheme from '@joplin/lib/themes/light';
|
||||
@@ -42,6 +41,8 @@ import { hasProtocol } from '@joplin/utils/url';
|
||||
import useTabIndenter from './utils/useTabIndenter';
|
||||
import useKeyboardRefocusHandler from './utils/useKeyboardRefocusHandler';
|
||||
import useDocument from '../../../hooks/useDocument';
|
||||
import useEditDialog from './utils/useEditDialog';
|
||||
import useEditDialogEventListeners from './utils/useEditDialogEventListeners';
|
||||
|
||||
const logger = Logger.create('TinyMCE');
|
||||
|
||||
@@ -72,14 +73,6 @@ function awfulInitHack(html: string): string {
|
||||
return html === '<div id="rendered-md"></div>' ? '<div id="rendered-md"><p></p></div>' : html;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
function findEditableContainer(node: any): any {
|
||||
while (node) {
|
||||
if (node.classList && node.classList.contains('joplin-editable')) return node;
|
||||
node = node.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let markupToHtml_ = new MarkupToHtml();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
@@ -130,19 +123,23 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
|
||||
const { scrollToPercent } = useScroll({ editor, onScroll: props.onScroll });
|
||||
|
||||
usePluginServiceRegistration(ref);
|
||||
useContextMenu(editor, props.plugins, props.dispatch, props.htmlToMarkdown, props.markupToHtml);
|
||||
useTabIndenter(editor);
|
||||
useKeyboardRefocusHandler(editor);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const dispatchDidUpdate = (editor: any) => {
|
||||
const dispatchDidUpdate = useCallback((editor: Editor) => {
|
||||
if (dispatchDidUpdateIID_) shim.clearTimeout(dispatchDidUpdateIID_);
|
||||
dispatchDidUpdateIID_ = shim.setTimeout(() => {
|
||||
dispatchDidUpdateIID_ = null;
|
||||
if (editor && editor.getDoc()) editor.getDoc().dispatchEvent(new Event('joplin-noteDidUpdate'));
|
||||
}, 10);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const editDialog = useEditDialog({ editor, markupToHtml, dispatchDidUpdate });
|
||||
const editDialogRef = useRef(editDialog);
|
||||
editDialogRef.current = editDialog;
|
||||
|
||||
useEditDialogEventListeners(editor, editDialog);
|
||||
usePluginServiceRegistration(ref);
|
||||
useContextMenu(editor, props.plugins, props.dispatch, props.htmlToMarkdown, props.markupToHtml, editDialog);
|
||||
useTabIndenter(editor, !props.tabMovesFocus);
|
||||
useKeyboardRefocusHandler(editor);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const insertResourcesIntoContent = useCallback(async (filePaths: string[] = null, options: any = null) => {
|
||||
@@ -179,7 +176,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
props.onMessage({ channel: href });
|
||||
}
|
||||
}
|
||||
}, [editor, props.onMessage]);
|
||||
}, [editor, props.onMessage, dispatchDidUpdate]);
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
@@ -412,9 +409,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
element.setAttribute('id', 'tinyMceStyle');
|
||||
editorContainerDom.head.appendChild(element);
|
||||
element.appendChild(editorContainerDom.createTextNode(`
|
||||
.joplin-tinymce .tox-editor-header {
|
||||
padding-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
|
||||
padding-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
|
||||
.joplin-tinymce .tox-editor-header.tox-editor-header {
|
||||
margin-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
|
||||
margin-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tox .tox-toolbar,
|
||||
@@ -434,7 +433,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
}
|
||||
|
||||
.tox .tox-dialog__body-content,
|
||||
.tox .tox-collection__item {
|
||||
.tox .tox-collection__item,
|
||||
.tox .tox-insert-table-picker__label {
|
||||
color: ${theme.color};
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
*/
|
||||
|
||||
.tox .tox-dialog textarea {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace !important;
|
||||
}
|
||||
|
||||
.tox .tox-dialog-wrap__backdrop {
|
||||
@@ -498,6 +498,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
.tox .tox-toolbar-label {
|
||||
color: ${theme.color3} !important;
|
||||
fill: ${theme.color3} !important;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tox .tox-statusbar a,
|
||||
@@ -524,6 +525,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
.tox .tox-split-button:focus {
|
||||
background-color: ${theme.backgroundColor3}
|
||||
}
|
||||
|
||||
.tox .tox-tbtn:focus-visible,
|
||||
.tox .tox-split-button:focus-visible {
|
||||
background-color: ${theme.backgroundColorHover3}
|
||||
}
|
||||
|
||||
.tox .tox-tbtn:hover,
|
||||
.tox .tox-menu button:hover > svg {
|
||||
@@ -560,6 +566,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
.tox .tox-split-button:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Decrease the spacing between groups */
|
||||
.tox .tox-toolbar__group {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
|
||||
.tox-tinymce,
|
||||
.tox .tox-toolbar__group,
|
||||
@@ -605,6 +617,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
background: none;
|
||||
background-color: ${theme.backgroundColor3} !important;
|
||||
}
|
||||
|
||||
.tox .tox-tbtn,
|
||||
.tox .tox-tbtn button,
|
||||
.tox .tox-split-button,
|
||||
.tox .tox-split-button button {
|
||||
margin: 0 !important;
|
||||
}
|
||||
`));
|
||||
|
||||
return () => {
|
||||
@@ -628,7 +647,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return;
|
||||
editor.setMode(props.disabled ? 'readonly' : 'design');
|
||||
editor.mode.set(props.disabled ? 'readonly' : 'design');
|
||||
}, [editor, props.disabled]);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
@@ -661,7 +680,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
// we create small groups of just one button towards the end.
|
||||
|
||||
const toolbar = [
|
||||
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
|
||||
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', '|',
|
||||
'joplinInsert', 'joplinSup', 'joplinSub', 'forecolor', '|',
|
||||
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
|
||||
'bullist', 'numlist', 'joplinChecklist', '|',
|
||||
'h1', 'h2', 'h3', '|',
|
||||
@@ -675,14 +695,19 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
const containerWindow = editorContainerDom.defaultView as any;
|
||||
const editors = await containerWindow.tinymce.init({
|
||||
selector: `#${editorContainer.id}`,
|
||||
|
||||
// Ensures that the "Premium plugins" toolbar option is disabled. See
|
||||
// https://www.tiny.cloud/docs/tinymce/latest/editor-premium-upgrade-promotion/
|
||||
promotion: false,
|
||||
width: '100%',
|
||||
body_class: 'jop-tinymce',
|
||||
height: '100%',
|
||||
resize: false,
|
||||
highlight_on_focus: false,
|
||||
icons: 'Joplin',
|
||||
icons_url: 'gui/NoteEditor/NoteBody/TinyMCE/icons.js',
|
||||
plugins: 'noneditable link joplinLists hr searchreplace codesample table',
|
||||
noneditable_noneditable_class: 'joplin-editable', // Can be a regex too
|
||||
plugins: 'link joplinLists searchreplace codesample table',
|
||||
noneditable_class: 'joplin-editable', // Can be a regex too
|
||||
iframe_aria_text: _('Rich Text editor. Press Escape then Tab to escape focus.'),
|
||||
|
||||
// #p: Pad empty paragraphs with to prevent them from being removed.
|
||||
@@ -694,7 +719,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
relative_urls: false,
|
||||
branding: false,
|
||||
statusbar: false,
|
||||
target_list: false,
|
||||
link_target_list: false,
|
||||
// Handle the first table row as table header.
|
||||
// https://www.tiny.cloud/docs/plugins/table/#table_header_type
|
||||
table_header_type: 'sectionCells',
|
||||
@@ -704,13 +729,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
contextmenu: false,
|
||||
browser_spellcheck: true,
|
||||
|
||||
// Work around an issue where images with a base64 SVG data URL would be broken.
|
||||
//
|
||||
// See https://github.com/tinymce/tinymce/issues/3864
|
||||
//
|
||||
// This was fixed in TinyMCE 6.1, so remove it when we upgrade.
|
||||
images_dataimg_filter: (img: HTMLImageElement) => !img.src.startsWith('data:'),
|
||||
|
||||
formats: {
|
||||
joplinHighlight: { inline: 'mark', remove: 'all' },
|
||||
joplinStrikethrough: { inline: 's', remove: 'all' },
|
||||
@@ -739,7 +757,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
tooltip: _('Code Block'),
|
||||
icon: 'code-sample',
|
||||
onAction: async function() {
|
||||
openEditDialog(editor, markupToHtml, dispatchDidUpdate, null);
|
||||
editDialogRef.current.editNew();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -747,14 +765,15 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
tooltip: _('Inline Code'),
|
||||
icon: 'sourcecode',
|
||||
onAction: function() {
|
||||
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' } as any);
|
||||
},
|
||||
onSetup: function(api) {
|
||||
api.setActive(editor.formatter.match('code'));
|
||||
const unbind = editor.formatter.formatChanged('code', api.setActive).unbind;
|
||||
const handle = editor.formatter.formatChanged('code', active => api.setActive(active));
|
||||
|
||||
return function() {
|
||||
if (unbind) unbind();
|
||||
handle?.unbind();
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -805,11 +824,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
editor.addShortcut('Meta+Shift+9', '', () => editor.execCommand('InsertJoplinChecklist'));
|
||||
|
||||
// TODO: remove event on unmount?
|
||||
editor.on('DblClick', (event) => {
|
||||
const editable = findEditableContainer(event.target);
|
||||
if (editable) openEditDialog(editor, markupToHtml, dispatchDidUpdate, editable);
|
||||
});
|
||||
|
||||
editor.on('drop', (event) => {
|
||||
// Prevent the message "Dropped file type is not supported" from showing up.
|
||||
// It was added in TinyMCE 5.4 and doesn't apply since we do support
|
||||
@@ -1035,6 +1049,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
|
||||
const allAssetsOptions: NoteStyleOptions = {
|
||||
contentMaxWidthTarget: '.mce-content-body',
|
||||
scrollbarSize: props.scrollbarSize,
|
||||
themeId: props.contentMarkupLanguage === MarkupLanguage.Html ? 1 : null,
|
||||
whiteBackgroundNoteRendering: props.whiteBackgroundNoteRendering,
|
||||
};
|
||||
@@ -1051,7 +1066,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
cancelled = true;
|
||||
};
|
||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
||||
}, [editor, props.themeId, props.markupToHtml, props.allAssets, props.content, props.resourceInfos, props.contentKey, props.contentMarkupLanguage, props.whiteBackgroundNoteRendering]);
|
||||
}, [editor, props.themeId, props.scrollbarSize, props.markupToHtml, props.allAssets, props.content, props.resourceInfos, props.contentKey, props.contentMarkupLanguage, props.whiteBackgroundNoteRendering]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return () => {};
|
||||
@@ -1205,9 +1220,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const onSetAttrib = (event: any) => {
|
||||
const onSetAttrib = (event: EditorEvent<any>) => {
|
||||
// Dispatch onChange when a link is edited
|
||||
if (event.attrElm[0].nodeName === 'A') {
|
||||
const target = Array.isArray(event.attrElm) ? event.attrElm[0] : event.attrElm;
|
||||
if (target.nodeName === 'A') {
|
||||
if (event.attrName === 'title' || event.attrName === 'href' || event.attrName === 'rel') {
|
||||
onChangeHandler();
|
||||
}
|
||||
@@ -1336,7 +1352,9 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
editor.on(TinyMceEditorEvents.KeyUp, onKeyUp);
|
||||
editor.on(TinyMceEditorEvents.KeyDown, onKeyDown);
|
||||
editor.on(TinyMceEditorEvents.KeyPress, onKeypress);
|
||||
editor.on(TinyMceEditorEvents.Paste, onPaste);
|
||||
// Passing `true` adds the listener to the front of the listener list.
|
||||
// This allows overriding TinyMCE's built-in paste handler with .preventDefault.
|
||||
editor.on(TinyMceEditorEvents.Paste, onPaste, true);
|
||||
editor.on(TinyMceEditorEvents.PasteAsText, onPasteAsText);
|
||||
editor.on(TinyMceEditorEvents.Copy, onCopy);
|
||||
// `compositionend` means that a user has finished entering a Chinese
|
||||
@@ -1379,7 +1397,17 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (editorRef.current) editorRef.current.remove();
|
||||
if (!editorRef.current) return;
|
||||
|
||||
const ownerDocument = editorRef.current.getContainer().ownerDocument;
|
||||
const parentWindow = ownerDocument.defaultView;
|
||||
|
||||
// Calling .remove after the parent window is closed throws an Error
|
||||
// related to DOM API access. Since closing the window also removes the editor,
|
||||
// it shouldn't be necessary to call .remove in this case:
|
||||
if (parentWindow) {
|
||||
editorRef.current.remove();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
@@ -886,7 +886,7 @@
|
||||
var parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListRootElm(editor, elm));
|
||||
return parentLi ? parentLi : elm;
|
||||
});
|
||||
return DomQuery.unique(listItemsElms);
|
||||
return [...new Set(listItemsElms)];
|
||||
};
|
||||
var getSelectedListItems = function (editor) {
|
||||
var selectedBlocks = editor.selection.getSelectedBlocks();
|
||||
@@ -919,7 +919,7 @@
|
||||
var listRoots = map(lists, function (list) {
|
||||
return findLastParentListNode(editor, list).getOr(list);
|
||||
});
|
||||
return DomQuery.unique(listRoots);
|
||||
return [...new Set(listRoots)];
|
||||
};
|
||||
|
||||
var shouldIndentOnTab = function (editor) {
|
||||
@@ -2119,8 +2119,7 @@
|
||||
};
|
||||
var register$1 = function (editor) {
|
||||
var hasPlugin = function (editor, plugin) {
|
||||
var plugins = editor.settings.plugins ? editor.settings.plugins : '';
|
||||
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
|
||||
return editor.hasPlugin(plugin);
|
||||
};
|
||||
var _ = getLocalizationFunction(editor);
|
||||
var exec = function (command) {
|
||||
|
@@ -0,0 +1,71 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
const taboverride = require('taboverride');
|
||||
|
||||
export interface TextAreaTabHandler {
|
||||
remove(): void;
|
||||
}
|
||||
|
||||
const createTextAreaKeyListeners = () => {
|
||||
let hasListeners = true;
|
||||
|
||||
// Selectively enable/disable taboverride based on settings -- remove taboverride
|
||||
// when pressing tab if tab is expected to move focus.
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Tab') {
|
||||
if (Setting.value('editor.tabMovesFocus')) {
|
||||
taboverride.utils.removeListeners(event.currentTarget);
|
||||
hasListeners = false;
|
||||
} else {
|
||||
// Prevent the default focus-changing behavior
|
||||
event.preventDefault();
|
||||
requestAnimationFrame(() => {
|
||||
focus('openEditDialog::dialogTextArea_keyDown', event.target);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Tab' && !hasListeners) {
|
||||
taboverride.utils.addListeners(event.currentTarget);
|
||||
hasListeners = true;
|
||||
}
|
||||
};
|
||||
|
||||
return { onKeyDown, onKeyUp };
|
||||
};
|
||||
|
||||
// Allows pressing tab in a textarea to input an actual tab (instead of changing focus)
|
||||
// taboverride will take care of actually inserting the tab character, while the keydown
|
||||
// event listener will override the default behaviour, which is to focus the next field.
|
||||
const enableTextAreaTab = (textAreas: HTMLTextAreaElement[]): TextAreaTabHandler => {
|
||||
type RemoveCallback = ()=> void;
|
||||
const removeCallbacks: RemoveCallback[] = [];
|
||||
|
||||
for (const textArea of textAreas) {
|
||||
const { onKeyDown, onKeyUp } = createTextAreaKeyListeners();
|
||||
textArea.addEventListener('keydown', onKeyDown);
|
||||
textArea.addEventListener('keyup', onKeyUp);
|
||||
|
||||
// Enable/disable taboverride **after** the listeners above.
|
||||
// The custom keyup/keydown need to have higher precedence.
|
||||
taboverride.set(textArea, true);
|
||||
|
||||
removeCallbacks.push(() => {
|
||||
taboverride.set(textArea, false);
|
||||
textArea.removeEventListener('keyup', onKeyUp);
|
||||
textArea.removeEventListener('keydown', onKeyDown);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
remove: () => {
|
||||
for (const callback of removeCallbacks) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default enableTextAreaTab;
|
@@ -60,13 +60,12 @@ export default function(editor: any) {
|
||||
});
|
||||
}
|
||||
|
||||
const items: string[] = definitions.filter(d => !!d.grouped).map(d => d.name);
|
||||
|
||||
// Additional built-in buttons to show in the formatting sub-menu:
|
||||
items.push('forecolor');
|
||||
|
||||
editor.ui.registry.addGroupToolbarButton('formattingExtras', {
|
||||
icon: 'image-options',
|
||||
items: items.join(' '),
|
||||
});
|
||||
// Old code to format a group of buttons into a dropdown
|
||||
// const items: string[] = definitions.filter(d => !!d.grouped).map(d => d.name);
|
||||
// items.push('forecolor');
|
||||
// editor.ui.registry.addGroupToolbarButton('formattingExtras', {
|
||||
// icon: 'image-options',
|
||||
// tooltip: _('Formatting'),
|
||||
// items: items.join(' '),
|
||||
// });
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { Editor } from 'tinymce';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export enum TinyMceEditorEvents {
|
||||
KeyUp = 'keyup',
|
||||
@@ -14,3 +16,5 @@ export enum TinyMceEditorEvents {
|
||||
ExecCommand = 'ExecCommand',
|
||||
SetAttrib = 'SetAttrib',
|
||||
}
|
||||
|
||||
export type DispatchDidUpdateCallback = (editor: Editor)=> void;
|
||||
|
@@ -8,12 +8,16 @@ import { menuItems } from '../../../utils/contextMenu';
|
||||
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import type { Event as ElectronEvent } from 'electron';
|
||||
import type { Event as ElectronEvent, MenuItemConstructorOptions } from 'electron';
|
||||
|
||||
import Resource from '@joplin/lib/models/Resource';
|
||||
import { TinyMceEditorEvents } from './types';
|
||||
import { HtmlToMarkdownHandler, MarkupToHtmlHandler } from '../../../utils/types';
|
||||
import { Editor } from 'tinymce';
|
||||
import { EditDialogControl } from './useEditDialog';
|
||||
import { Dispatch } from 'redux';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import type { MenuItem as MenuItemType } from 'electron';
|
||||
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
@@ -52,23 +56,14 @@ interface ContextMenuActionOptions {
|
||||
|
||||
const contextMenuActionOptions: ContextMenuActionOptions = { current: null };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
|
||||
export default function(editor: Editor, plugins: PluginStates, dispatch: Function, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) {
|
||||
export default function(editor: Editor, plugins: PluginStates, dispatch: Dispatch, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler, editDialog: EditDialogControl) {
|
||||
useEffect(() => {
|
||||
if (!editor) return () => {};
|
||||
|
||||
const contextMenuItems = menuItems(dispatch, htmlToMd, mdToHtml);
|
||||
const targetWindow = bridge().activeWindow();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
function onContextMenu(event: ElectronEvent, params: any) {
|
||||
const element = contextMenuElement(editor, params.x, params.y);
|
||||
if (!element) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
const makeMainMenuItems = (element: Element) => {
|
||||
let itemType: ContextMenuItemType = ContextMenuItemType.None;
|
||||
let resourceId = '';
|
||||
let linkToCopy = null;
|
||||
@@ -103,29 +98,64 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Functio
|
||||
mdToHtml,
|
||||
};
|
||||
|
||||
const result = [];
|
||||
for (const itemName in contextMenuItems) {
|
||||
const item = contextMenuItems[itemName];
|
||||
|
||||
if (!item.isActive(itemType, contextMenuActionOptions.current)) continue;
|
||||
|
||||
menu.append(new MenuItem({
|
||||
result.push(new MenuItem({
|
||||
label: item.label,
|
||||
click: () => {
|
||||
item.onAction(contextMenuActionOptions.current);
|
||||
},
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const makeEditableMenuItems = (element: Element) => {
|
||||
if (editDialog.isEditable(element)) {
|
||||
return [
|
||||
new MenuItem({
|
||||
type: 'normal',
|
||||
label: _('Edit'),
|
||||
click: () => {
|
||||
editDialog.editExisting(element);
|
||||
},
|
||||
}),
|
||||
new MenuItem({ type: 'separator' }),
|
||||
];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
function onContextMenu(event: ElectronEvent, params: any) {
|
||||
const element = contextMenuElement(editor, params.x, params.y);
|
||||
if (!element) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const menu = new Menu();
|
||||
const menuItems: MenuItemType[] = [];
|
||||
const toMenuItems = (specs: MenuItemConstructorOptions[]) => {
|
||||
return specs.map(spec => new MenuItem(spec));
|
||||
};
|
||||
|
||||
menuItems.push(...makeEditableMenuItems(element));
|
||||
menuItems.push(...makeMainMenuItems(element));
|
||||
const spellCheckerMenuItems = SpellCheckerService.instance().contextMenuItems(params.misspelledWord, params.dictionarySuggestions);
|
||||
menuItems.push(
|
||||
...toMenuItems(spellCheckerMenuItems),
|
||||
);
|
||||
menuItems.push(
|
||||
...toMenuItems(menuUtils.pluginContextMenuItems(plugins, MenuItemLocation.EditorContextMenu)),
|
||||
);
|
||||
|
||||
for (const item of spellCheckerMenuItems) {
|
||||
menu.append(new MenuItem(item));
|
||||
for (const item of menuItems) {
|
||||
menu.append(item);
|
||||
}
|
||||
|
||||
for (const item of menuUtils.pluginContextMenuItems(plugins, MenuItemLocation.EditorContextMenu)) {
|
||||
menu.append(new MenuItem(item));
|
||||
}
|
||||
|
||||
menu.popup({ window: targetWindow });
|
||||
}
|
||||
|
||||
@@ -136,5 +166,5 @@ export default function(editor: Editor, plugins: PluginStates, dispatch: Functio
|
||||
targetWindow.webContents.off('context-menu', onContextMenu);
|
||||
}
|
||||
};
|
||||
}, [editor, plugins, dispatch, htmlToMd, mdToHtml]);
|
||||
}, [editor, plugins, dispatch, htmlToMd, mdToHtml, editDialog]);
|
||||
}
|
||||
|
@@ -1,43 +1,32 @@
|
||||
import { RefObject, useMemo } from 'react';
|
||||
import type { Editor } from 'tinymce';
|
||||
import { DispatchDidUpdateCallback, TinyMceEditorEvents } from './types';
|
||||
import { MarkupToHtmlHandler } from '../../../utils/types';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import enableTextAreaTab, { TextAreaTabHandler } from './enableTextAreaTab';
|
||||
import { MarkupToHtml } from '@joplin/renderer';
|
||||
import { TinyMceEditorEvents } from './types';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
const taboverride = require('taboverride');
|
||||
|
||||
interface Props {
|
||||
editor: Editor;
|
||||
markupToHtml: RefObject<MarkupToHtmlHandler>;
|
||||
dispatchDidUpdate: DispatchDidUpdateCallback;
|
||||
}
|
||||
|
||||
export interface EditDialogControl {
|
||||
editNew: ()=> void;
|
||||
editExisting: (elementInEditable: Node)=> void;
|
||||
isEditable: (element: Node)=> boolean;
|
||||
}
|
||||
|
||||
interface SourceInfo {
|
||||
openCharacters: string;
|
||||
closeCharacters: string;
|
||||
content: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
node: any;
|
||||
node: Element;
|
||||
language: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
function dialogTextArea_keyDown(event: any) {
|
||||
if (event.key === 'Tab') {
|
||||
window.requestAnimationFrame(() => focus('openEditDialog::dialogTextArea_keyDown', event.target));
|
||||
}
|
||||
}
|
||||
|
||||
// Allows pressing tab in a textarea to input an actual tab (instead of changing focus)
|
||||
// taboverride will take care of actually inserting the tab character, while the keydown
|
||||
// event listener will override the default behaviour, which is to focus the next field.
|
||||
function enableTextAreaTab(enable: boolean) {
|
||||
const textAreas = document.getElementsByClassName('tox-textarea');
|
||||
for (const textArea of textAreas) {
|
||||
taboverride.set(textArea, enable);
|
||||
|
||||
if (enable) {
|
||||
textArea.addEventListener('keydown', dialogTextArea_keyDown);
|
||||
} else {
|
||||
textArea.removeEventListener('keydown', dialogTextArea_keyDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
function findBlockSource(node: any): SourceInfo {
|
||||
function findBlockSource(node: Element): SourceInfo {
|
||||
const sources = node.getElementsByClassName('joplin-source');
|
||||
if (!sources.length) throw new Error('No source for node');
|
||||
const source = sources[0];
|
||||
@@ -81,9 +70,14 @@ function editableInnerHtml(html: string): string {
|
||||
return editable[0].innerHTML;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
|
||||
export default function openEditDialog(editor: any, markupToHtml: any, dispatchDidUpdate: Function, editable: any) {
|
||||
function openEditDialog(
|
||||
editor: Editor,
|
||||
markupToHtml: RefObject<MarkupToHtmlHandler>,
|
||||
dispatchDidUpdate: DispatchDidUpdateCallback,
|
||||
editable: Element,
|
||||
) {
|
||||
const source = editable ? findBlockSource(editable) : newBlockSource();
|
||||
let tabHandler: TextAreaTabHandler|null = null;
|
||||
|
||||
editor.windowManager.open({
|
||||
title: _('Edit'),
|
||||
@@ -113,7 +107,7 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
|
||||
dispatchDidUpdate(editor);
|
||||
},
|
||||
onClose: () => {
|
||||
enableTextAreaTab(false);
|
||||
tabHandler?.remove();
|
||||
},
|
||||
body: {
|
||||
type: 'panel',
|
||||
@@ -124,12 +118,11 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
|
||||
label: 'Language',
|
||||
// Katex is a special case with special opening/closing tags
|
||||
// and we don't currently handle switching the language in this case.
|
||||
disabled: source.language === 'katex',
|
||||
enabled: source.language !== 'katex',
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
name: 'codeTextArea',
|
||||
value: source.content,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -142,6 +135,40 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
|
||||
});
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
enableTextAreaTab(true);
|
||||
const containerDocument = editor.getContainer().ownerDocument;
|
||||
const textAreas = containerDocument.querySelectorAll<HTMLTextAreaElement>('.tox-textarea');
|
||||
tabHandler = enableTextAreaTab([...textAreas]);
|
||||
});
|
||||
}
|
||||
|
||||
const findEditableContainer = (node: Node) => {
|
||||
if (node.nodeName.startsWith('#')) { // Not an element, e.g. #text
|
||||
node = node.parentElement;
|
||||
}
|
||||
return (node as Element)?.closest('.joplin-editable');
|
||||
};
|
||||
|
||||
const useEditDialog = ({
|
||||
editor, markupToHtml, dispatchDidUpdate,
|
||||
}: Props): EditDialogControl => {
|
||||
return useMemo(() => {
|
||||
const edit = (editable: Element|null) => {
|
||||
openEditDialog(editor, markupToHtml, dispatchDidUpdate, editable);
|
||||
};
|
||||
|
||||
return {
|
||||
isEditable: element => !!findEditableContainer(element),
|
||||
editExisting: (element: Node) => {
|
||||
const editable = findEditableContainer(element);
|
||||
if (editable) {
|
||||
edit(editable);
|
||||
}
|
||||
},
|
||||
editNew: () => {
|
||||
edit(null);
|
||||
},
|
||||
};
|
||||
}, [editor, markupToHtml, dispatchDidUpdate]);
|
||||
};
|
||||
|
||||
export default useEditDialog;
|
@@ -0,0 +1,34 @@
|
||||
import { Editor } from 'tinymce';
|
||||
import { EditDialogControl } from './useEditDialog';
|
||||
import { useEffect } from 'react';
|
||||
import { TinyMceEditorEvents } from './types';
|
||||
|
||||
const useEditDialogEventListeners = (editor: Editor|null, editDialog: EditDialogControl) => {
|
||||
useEffect(() => {
|
||||
if (!editor) return () => {};
|
||||
|
||||
const dblClickHandler = (event: Event) => {
|
||||
editDialog.editExisting(event.target as Node);
|
||||
};
|
||||
|
||||
const keyDownHandler = (event: KeyboardEvent) => {
|
||||
const hasModifiers = event.shiftKey || event.altKey || event.ctrlKey || event.metaKey;
|
||||
if (event.code === 'Enter' && !event.isComposing && !hasModifiers) {
|
||||
const selection = editor.selection.getNode();
|
||||
if (editDialog.isEditable(selection)) {
|
||||
editDialog.editExisting(selection);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
editor.on(TinyMceEditorEvents.KeyDown, keyDownHandler);
|
||||
editor.on('DblClick', dblClickHandler);
|
||||
return () => {
|
||||
editor.off(TinyMceEditorEvents.KeyDown, keyDownHandler);
|
||||
editor.off('DblClick', dblClickHandler);
|
||||
};
|
||||
}, [editor, editDialog]);
|
||||
};
|
||||
|
||||
export default useEditDialogEventListeners;
|
@@ -1,9 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import type { Editor, EditorEvent } from 'tinymce';
|
||||
|
||||
const useTabIndenter = (editor: Editor) => {
|
||||
const useTabIndenter = (editor: Editor, enabled: boolean) => {
|
||||
useEffect(() => {
|
||||
if (!editor) return () => {};
|
||||
if (!editor || !enabled) return () => {};
|
||||
|
||||
const canChangeIndentation = () => {
|
||||
const selectionElement = editor.selection.getNode();
|
||||
@@ -70,7 +70,7 @@ const useTabIndenter = (editor: Editor) => {
|
||||
return () => {
|
||||
editor.off('keydown', eventHandler);
|
||||
};
|
||||
}, [editor]);
|
||||
}, [editor, enabled]);
|
||||
};
|
||||
|
||||
export default useTabIndenter;
|
||||
|
@@ -9,20 +9,18 @@ import useNoteSearchBar from './utils/useNoteSearchBar';
|
||||
import useMessageHandler from './utils/useMessageHandler';
|
||||
import useWindowCommandHandler from './utils/useWindowCommandHandler';
|
||||
import useDropHandler from './utils/useDropHandler';
|
||||
import useMarkupToHtml from './utils/useMarkupToHtml';
|
||||
import useMarkupToHtml from '../hooks/useMarkupToHtml';
|
||||
import useFormNote, { OnLoadEvent, OnSetFormNote } from './utils/useFormNote';
|
||||
import useEffectiveNoteId from './utils/useEffectiveNoteId';
|
||||
import useFolder from './utils/useFolder';
|
||||
import styles_ from './styles';
|
||||
import { NoteEditorProps, FormNote, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import ToolbarButton from '../ToolbarButton/ToolbarButton';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
import eventManager, { EventName } from '@joplin/lib/eventManager';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import ToolbarButtonUtils, { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import { _, _n } from '@joplin/lib/locale';
|
||||
import TagList from '../TagList';
|
||||
import NoteTitleBar from './NoteTitle/NoteTitleBar';
|
||||
import markupLanguageUtils from '@joplin/lib/utils/markupLanguageUtils';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
@@ -45,7 +43,6 @@ import PlainEditor from './NoteBody/PlainEditor/PlainEditor';
|
||||
import CodeMirror6 from './NoteBody/CodeMirror/v6/CodeMirror';
|
||||
import CodeMirror5 from './NoteBody/CodeMirror/v5/CodeMirror';
|
||||
import { openItemById } from './utils/contextMenu';
|
||||
import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginSettingValue';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions';
|
||||
import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks';
|
||||
@@ -55,10 +52,10 @@ import Logger from '@joplin/utils/Logger';
|
||||
import usePluginEditorView from './utils/usePluginEditorView';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { WindowIdContext } from '../NewWindowOrIFrame';
|
||||
import { EditorActivationCheckFilterObject } from '@joplin/lib/services/plugins/api/types';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import WebviewController from '@joplin/lib/services/plugins/WebviewController';
|
||||
import AsyncActionQueue, { IntervalType } from '@joplin/lib/AsyncActionQueue';
|
||||
import EditorPluginHandler from '@joplin/lib/services/plugins/EditorPluginHandler';
|
||||
import useResourceUnwatcher from './utils/useResourceUnwatcher';
|
||||
import StatusBar from './StatusBar';
|
||||
|
||||
const debounce = require('debounce');
|
||||
|
||||
@@ -73,15 +70,6 @@ const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
|
||||
const onDragOver: React.DragEventHandler = event => event.preventDefault();
|
||||
let editorIdCounter = 0;
|
||||
|
||||
const makeNoteUpdateAction = (shownEditorViewIds: string[]) => {
|
||||
return async () => {
|
||||
for (const viewId of shownEditorViewIds) {
|
||||
const controller = PluginService.instance().viewControllerByViewId(viewId) as WebviewController;
|
||||
if (controller) controller.emitUpdate();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function NoteEditorContent(props: NoteEditorProps) {
|
||||
const [showRevisions, setShowRevisions] = useState(false);
|
||||
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
|
||||
@@ -91,7 +79,10 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
const titleInputRef = useRef<HTMLInputElement>();
|
||||
const isMountedRef = useRef(true);
|
||||
const noteSearchBarRef = useRef(null);
|
||||
const viewUpdateAsyncQueue_ = useRef<AsyncActionQueue>(new AsyncActionQueue(100, IntervalType.Fixed));
|
||||
|
||||
const editorPluginHandler = useMemo(() => {
|
||||
return new EditorPluginHandler(PluginService.instance());
|
||||
}, []);
|
||||
|
||||
const shownEditorViewIds = props['plugins.shownEditorViewIds'];
|
||||
|
||||
@@ -115,25 +106,15 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
|
||||
const effectiveNoteId = useEffectiveNoteId(props);
|
||||
|
||||
useAsyncEffect(async (event) => {
|
||||
useAsyncEffect(async (_event) => {
|
||||
if (!props.startupPluginsLoaded) return;
|
||||
|
||||
let filterObject: EditorActivationCheckFilterObject = {
|
||||
activatedEditors: [],
|
||||
};
|
||||
filterObject = await eventManager.filterEmit('editorActivationCheck', filterObject);
|
||||
if (event.cancelled) return;
|
||||
|
||||
for (const editor of filterObject.activatedEditors) {
|
||||
const controller = PluginService.instance().pluginById(editor.pluginId).viewController(editor.viewId) as WebviewController;
|
||||
controller.setActive(editor.isActive);
|
||||
}
|
||||
}, [effectiveNoteId, props.startupPluginsLoaded]);
|
||||
await editorPluginHandler.emitActivationCheck();
|
||||
}, [effectiveNoteId, editorPluginHandler, props.startupPluginsLoaded]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.startupPluginsLoaded) return;
|
||||
viewUpdateAsyncQueue_.current.push(makeNoteUpdateAction(shownEditorViewIds));
|
||||
}, [effectiveNoteId, shownEditorViewIds, props.startupPluginsLoaded]);
|
||||
editorPluginHandler.emitUpdate(shownEditorViewIds);
|
||||
}, [effectiveNoteId, editorPluginHandler, shownEditorViewIds, props.startupPluginsLoaded]);
|
||||
|
||||
const { editorPlugin, editorView } = usePluginEditorView(props.plugins, shownEditorViewIds);
|
||||
const builtInEditorVisible = !editorPlugin;
|
||||
@@ -179,7 +160,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
whiteBackgroundNoteRendering,
|
||||
customCss: props.customCss,
|
||||
plugins: props.plugins,
|
||||
settingValue: getPluginSettingValue,
|
||||
scrollbarSize: props.scrollbarSize,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
@@ -199,9 +180,10 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
return markupToHtml.allAssets(markupLanguage, theme, {
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
contentMaxWidthTarget: options.contentMaxWidthTarget,
|
||||
scrollbarSize: props.scrollbarSize,
|
||||
whiteBackgroundNoteRendering: options.whiteBackgroundNoteRendering,
|
||||
});
|
||||
}, [props.themeId, props.customCss, props.contentMaxWidth]);
|
||||
}, [props.themeId, props.scrollbarSize, props.customCss, props.contentMaxWidth]);
|
||||
|
||||
const handleProvisionalFlag = useCallback(() => {
|
||||
if (props.isProvisional) {
|
||||
@@ -358,6 +340,8 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
const windowId = useContext(WindowIdContext);
|
||||
const onMessage = useMessageHandler(scrollWhenReady, clearScrollWhenReady, windowId, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
|
||||
|
||||
useResourceUnwatcher({ noteId: formNote.id, windowId });
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
const externalEditWatcher_noteChange = useCallback((event: any) => {
|
||||
if (event.id === formNote.id) {
|
||||
@@ -437,24 +421,6 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
return <div style={emptyDivStyle} ref={containerRef}></div>;
|
||||
}
|
||||
|
||||
function renderTagButton() {
|
||||
return <ToolbarButton
|
||||
themeId={props.themeId}
|
||||
toolbarButtonInfo={props.setTagsToolbarButtonInfo}
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderTagBar() {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const noteIds = [formNote.id];
|
||||
const instructions = <span onClick={() => { void 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 (
|
||||
<div style={{ paddingLeft: 8, display: 'flex', flexDirection: 'row', alignItems: 'center' }}>{tagList}{instructions}</div>
|
||||
);
|
||||
}
|
||||
|
||||
const searchMarkers = useSearchMarkers(showLocalSearch, localSearchMarkerOptions, props.searches, props.selectedSearchId, props.highlightedWords);
|
||||
|
||||
const editorProps: NoteBodyEditorProps = {
|
||||
@@ -485,12 +451,14 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
searchMarkers: searchMarkers,
|
||||
visiblePanes: props.noteVisiblePanes || ['editor', 'viewer'],
|
||||
keyboardMode: Setting.value('editor.keyboardMode'),
|
||||
tabMovesFocus: props.tabMovesFocus,
|
||||
locale: Setting.value('locale'),
|
||||
onDrop: onDrop,
|
||||
noteToolbarButtonInfos: props.toolbarButtonInfos,
|
||||
plugins: props.plugins,
|
||||
fontSize: Setting.value('style.editor.fontSize'),
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
scrollbarSize: props.scrollbarSize,
|
||||
isSafeMode: props.isSafeMode,
|
||||
useCustomPdfViewer: props.useCustomPdfViewer,
|
||||
// We need it to identify the context for which media is rendered.
|
||||
@@ -535,6 +503,7 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
verticalAlign: 'top',
|
||||
boxSizing: 'border-box',
|
||||
flex: 1,
|
||||
overflowX: 'scroll',
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -685,10 +654,11 @@ function NoteEditorContent(props: NoteEditorProps) {
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{renderSearchBar()}
|
||||
</div>
|
||||
<div className="tag-bar" style={{ paddingLeft: theme.editorPaddingLeft, display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
|
||||
{renderTagButton()}
|
||||
{renderTagBar()}
|
||||
</div>
|
||||
<StatusBar
|
||||
noteId={formNote.id}
|
||||
setTagsToolbarButtonInfo={props.setTagsToolbarButtonInfo}
|
||||
selectedNoteTags={props.selectedNoteTags}
|
||||
/>
|
||||
<WarningBanner bodyEditor={props.bodyEditor}/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -729,7 +699,7 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
|
||||
selectedSearchId: windowState.selectedSearchId,
|
||||
customCss: state.customViewerCss,
|
||||
noteVisiblePanes: windowState.noteVisiblePanes,
|
||||
watchedResources: state.watchedResources,
|
||||
watchedResources: windowState.watchedResources,
|
||||
highlightedWords: state.highlightedWords,
|
||||
plugins: state.pluginService.plugins,
|
||||
pluginHtmlContents: state.pluginService.pluginHtmlContents,
|
||||
@@ -744,6 +714,8 @@ const mapStateToProps = (state: AppState, ownProps: ConnectProps) => {
|
||||
'setTags',
|
||||
], whenClauseContext)[0] as ToolbarButtonInfo,
|
||||
contentMaxWidth: state.settings['style.editor.contentMaxWidth'],
|
||||
scrollbarSize: state.settings['style.scrollbarSize'],
|
||||
tabMovesFocus: state.settings['editor.tabMovesFocus'],
|
||||
isSafeMode: state.settings.isSafeMode,
|
||||
useCustomPdfViewer: false,
|
||||
syncUserId: state.settings['sync.userId'],
|
||||
|
88
packages/app-desktop/gui/NoteEditor/StatusBar.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import * as React from 'react';
|
||||
import ToolbarButton from '../ToolbarButton/ToolbarButton';
|
||||
import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUtils';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { AppState } from '../../app.reducer';
|
||||
import { connect } from 'react-redux';
|
||||
import { TagEntity } from '@joplin/lib/services/database/types';
|
||||
import TagList from '../TagList';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { useCallback } from 'react';
|
||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
tabMovesFocus: boolean;
|
||||
noteId: string;
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
selectedNoteTags: TagEntity[];
|
||||
}
|
||||
|
||||
interface StatusIndicatorProps {
|
||||
commandName: string;
|
||||
showWhenUnfocused: boolean;
|
||||
// Even if not visible, [label] should reflect the current state
|
||||
// of the indicator.
|
||||
label: string;
|
||||
}
|
||||
|
||||
const StatusIndicator: React.FC<StatusIndicatorProps> = props => {
|
||||
const runCommand = useCallback(() => {
|
||||
void CommandService.instance().execute(props.commandName);
|
||||
}, [props.commandName]);
|
||||
|
||||
const keyshortcuts = KeymapService.instance().getAriaKeyShortcuts(props.commandName);
|
||||
return <span
|
||||
className={`status editor-status-indicator ${props.showWhenUnfocused ? '-show' : ''}`}
|
||||
aria-live='polite'
|
||||
>
|
||||
<button
|
||||
className='button'
|
||||
aria-keyshortcuts={keyshortcuts}
|
||||
onClick={runCommand}
|
||||
>
|
||||
{props.label}
|
||||
</button>
|
||||
</span>;
|
||||
};
|
||||
|
||||
const StatusBar: React.FC<Props> = props => {
|
||||
function renderTagButton() {
|
||||
return <ToolbarButton
|
||||
themeId={props.themeId}
|
||||
toolbarButtonInfo={props.setTagsToolbarButtonInfo}
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderTagBar() {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const noteIds = [props.noteId];
|
||||
const instructions = <span onClick={() => { void 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 <div className='tag-bar'>
|
||||
{renderTagButton()}
|
||||
<div className='content'>{tagList}{instructions}</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const keyboardStatus = <StatusIndicator
|
||||
commandName='toggleTabMovesFocus'
|
||||
label={props.tabMovesFocus ? _('Tab moves focus') : _('Tab indents')}
|
||||
showWhenUnfocused={props.tabMovesFocus}
|
||||
/>;
|
||||
|
||||
return <div className='editor-status-bar'>
|
||||
{renderTagBar()}
|
||||
<div className='spacer'/>
|
||||
{keyboardStatus}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default connect((state: AppState) => {
|
||||
return {
|
||||
themeId: state.settings.theme,
|
||||
tabMovesFocus: state.settings['editor.tabMovesFocus'],
|
||||
};
|
||||
})(StatusBar);
|
@@ -38,6 +38,7 @@ const incompatiblePluginIds = [
|
||||
'ylc395.noteLinkSystem',
|
||||
'outline',
|
||||
'joplin.plugin.cmoptions',
|
||||
'com.asdibiase.joplin-languagetool',
|
||||
// cSpell:enable
|
||||
];
|
||||
|
||||
|
@@ -0,0 +1,36 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
import { WindowCommandDependencies } from '../utils/types';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'focusElementToolbar',
|
||||
label: () => _('Toolbar'),
|
||||
parentLabel: () => _('Focus'),
|
||||
};
|
||||
|
||||
export const runtime = (dependencies: WindowCommandDependencies): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
if (!dependencies || !dependencies.containerRef || !dependencies.containerRef.current) return;
|
||||
|
||||
const firstButtonOnRTEToolbar = dependencies.containerRef.current.querySelector(
|
||||
'.tox-toolbar__group button',
|
||||
);
|
||||
|
||||
if (firstButtonOnRTEToolbar) {
|
||||
focus('focusElementToolbar', firstButtonOnRTEToolbar);
|
||||
return;
|
||||
}
|
||||
|
||||
const firstButtonOnMarkdownToolbar = dependencies.containerRef.current.querySelector(
|
||||
'#CodeMirrorToolbar .button:not(.disabled)',
|
||||
);
|
||||
|
||||
if (firstButtonOnMarkdownToolbar) {
|
||||
focus('focusElementToolbar', firstButtonOnMarkdownToolbar);
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
};
|
@@ -1,6 +1,7 @@
|
||||
// AUTO-GENERATED using `gulp buildScriptIndexes`
|
||||
import * as focusElementNoteBody from './focusElementNoteBody';
|
||||
import * as focusElementNoteTitle from './focusElementNoteTitle';
|
||||
import * as focusElementToolbar from './focusElementToolbar';
|
||||
import * as pasteAsText from './pasteAsText';
|
||||
import * as showLocalSearch from './showLocalSearch';
|
||||
import * as showRevisions from './showRevisions';
|
||||
@@ -8,6 +9,7 @@ import * as showRevisions from './showRevisions';
|
||||
const index: any[] = [
|
||||
focusElementNoteBody,
|
||||
focusElementNoteTitle,
|
||||
focusElementToolbar,
|
||||
pasteAsText,
|
||||
showLocalSearch,
|
||||
showRevisions,
|
||||
|
@@ -5,3 +5,8 @@
|
||||
@use "./styles/note-title-wrapper.scss";
|
||||
@use "./styles/note-editor-wrapper.scss";
|
||||
@use "./styles/note-editor-viewer-row.scss";
|
||||
@use "./styles/revision-viewer-root.scss";
|
||||
@use "./styles/revision-viewer-title.scss";
|
||||
@use "./styles/tag-bar.scss";
|
||||
@use "./styles/editor-status-bar.scss";
|
||||
@use "./styles/editor-status-indicator.scss";
|
||||
|
@@ -0,0 +1,13 @@
|
||||
|
||||
.editor-status-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
> .spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
> .status {
|
||||
align-self: end;
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
|
||||
.editor-status-indicator {
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&:has(> :focus-visible), &.-show {
|
||||
width: unset;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
> .button {
|
||||
font-size: var(--joplin-font-size-small);
|
||||
background-color: var(--joplin-background-color-active3);
|
||||
color: var(--joplin-color);
|
||||
border: none;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
|
||||
.revision-viewer-root {
|
||||
background-color: var(--joplin-background-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
|
||||
.revision-viewer-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
border-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
border-color: var(--joplin-divider-color);
|
||||
padding-bottom: 10px;
|
||||
|
||||
> .revisions {
|
||||
margin-left: 10px;
|
||||
flex: 0.5;
|
||||
}
|
||||
|
||||
> .title {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
14
packages/app-desktop/gui/NoteEditor/styles/tag-bar.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
.tag-bar {
|
||||
padding-left: var(--joplin-editor-padding-left);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
|
||||
> .content {
|
||||
padding-left: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
@@ -51,6 +51,7 @@ export async function commandAttachFileToBody(body: string, filePaths: string[]
|
||||
createFileURL: options.createFileURL,
|
||||
resizeLargeImages: Setting.value('imageResizing'),
|
||||
markupLanguage: options.markupLanguage,
|
||||
resourceSuffix: i > 0 ? ' ' : '',
|
||||
});
|
||||
|
||||
if (!newBody) {
|
||||
|
@@ -9,6 +9,10 @@ import { DropHandler } from './useDropHandler';
|
||||
import { SearchMarkers } from './useSearchMarkers';
|
||||
import { ParseOptions } from '@joplin/lib/HtmlToMd';
|
||||
import { ScrollStrategy } from '@joplin/editor/CodeMirror/CodeMirrorControl';
|
||||
import { MarkupToHtmlOptions } from '../../hooks/useMarkupToHtml';
|
||||
import { ScrollbarSize } from '@joplin/lib/models/settings/builtInMetadata';
|
||||
import { RefObject, SetStateAction } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface AllAssetsOptions {
|
||||
contentMaxWidthTarget?: string;
|
||||
@@ -47,10 +51,12 @@ export interface NoteEditorProps {
|
||||
watchedResources: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
highlightedWords: any[];
|
||||
tabMovesFocus: boolean;
|
||||
plugins: PluginStates;
|
||||
toolbarButtonInfos: ToolbarItem[];
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
contentMaxWidth: number;
|
||||
scrollbarSize: ScrollbarSize;
|
||||
isSafeMode: boolean;
|
||||
useCustomPdfViewer: boolean;
|
||||
shareCacheSetting: string;
|
||||
@@ -72,22 +78,7 @@ export interface NoteBodyEditorRef {
|
||||
execCommand(command: CommandValue): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MarkupToHtmlOptions {
|
||||
replaceResourceInternalToExternalLinks?: boolean;
|
||||
resourceInfos?: ResourceInfos;
|
||||
contentMaxWidth?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
plugins?: Record<string, any>;
|
||||
bodyOnly?: boolean;
|
||||
mapsToLine?: boolean;
|
||||
useCustomPdfViewer?: boolean;
|
||||
noteId?: string;
|
||||
vendorDir?: string;
|
||||
platformName?: string;
|
||||
allowedFilePrefixes?: string[];
|
||||
whiteBackgroundNoteRendering?: boolean;
|
||||
}
|
||||
|
||||
export { MarkupToHtmlOptions };
|
||||
export type MarkupToHtmlHandler = (markupLanguage: MarkupLanguage, markup: string, options: MarkupToHtmlOptions)=> Promise<RenderResult>;
|
||||
export type HtmlToMarkdownHandler = (markupLanguage: number, html: string, originalCss: string, parseOptions?: ParseOptions)=> Promise<string>;
|
||||
|
||||
@@ -105,6 +96,8 @@ export interface NoteBodyEditorProps {
|
||||
// avoid cases where black text is rendered over a dark background.
|
||||
whiteBackgroundNoteRendering: boolean;
|
||||
|
||||
scrollbarSize: ScrollbarSize;
|
||||
|
||||
content: string;
|
||||
contentKey: string;
|
||||
contentMarkupLanguage: number;
|
||||
@@ -119,8 +112,7 @@ export interface NoteBodyEditorProps {
|
||||
htmlToMarkdown: HtmlToMarkdownHandler;
|
||||
allAssets: (markupLanguage: MarkupLanguage, options: AllAssetsOptions)=> Promise<RenderResultPluginAsset[]>;
|
||||
disabled: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
dispatch: Function;
|
||||
dispatch: Dispatch;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
noteToolbar: any;
|
||||
setLocalSearchResultCount(count: number): void;
|
||||
@@ -131,6 +123,7 @@ export interface NoteBodyEditorProps {
|
||||
searchMarkers: SearchMarkers;
|
||||
visiblePanes: string[];
|
||||
keyboardMode: string;
|
||||
tabMovesFocus: boolean;
|
||||
resourceInfos: ResourceInfos;
|
||||
resourceDirectory: string;
|
||||
locale: string;
|
||||
@@ -281,3 +274,11 @@ export interface ScrollToTextValue {
|
||||
element: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'strong' | 'ul';
|
||||
scrollStrategy?: ScrollStrategy;
|
||||
}
|
||||
|
||||
export interface WindowCommandDependencies {
|
||||
setShowLocalSearch: React.Dispatch<SetStateAction<boolean>>;
|
||||
noteSearchBarRef: RefObject<HTMLInputElement>;
|
||||
editorRef: RefObject<NoteBodyEditorRef>;
|
||||
titleInputRef: RefObject<HTMLInputElement>;
|
||||
containerRef: RefObject<HTMLDivElement|null>;
|
||||
}
|
||||
|
43
packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const react_1 = require('react');
|
||||
const markupLanguageUtils_1 = require('@joplin/lib/utils/markupLanguageUtils');
|
||||
const Setting_1 = require('@joplin/lib/models/Setting');
|
||||
const shim_1 = require('@joplin/lib/shim');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
const Note_1 = require('@joplin/lib/models/Note');
|
||||
const resourceUtils_1 = require('@joplin/lib/models/utils/resourceUtils');
|
||||
function useMarkupToHtml(deps) {
|
||||
const { themeId, customCss, plugins, whiteBackgroundNoteRendering } = deps;
|
||||
const resourceBaseUrl = (0, react_1.useMemo)(() => {
|
||||
return `joplin-content://note-viewer/${Setting_1.default.value('resourceDir')}/`;
|
||||
}, []);
|
||||
const markupToHtml = (0, react_1.useMemo)(() => {
|
||||
return markupLanguageUtils_1.default.newMarkupToHtml(plugins, {
|
||||
resourceBaseUrl,
|
||||
customCss: customCss || '',
|
||||
});
|
||||
}, [plugins, customCss, resourceBaseUrl]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
return (0, react_1.useCallback)(async (markupLanguage, md, options = null) => {
|
||||
options = { replaceResourceInternalToExternalLinks: false, resourceInfos: {}, platformName: shim_1.default.platformName(), ...options };
|
||||
md = md || '';
|
||||
const theme = themeStyle(themeId);
|
||||
let resources = {};
|
||||
if (options.replaceResourceInternalToExternalLinks) {
|
||||
md = await Note_1.default.replaceResourceInternalToExternalLinks(md, { useAbsolutePaths: true });
|
||||
} else {
|
||||
resources = options.resourceInfos;
|
||||
}
|
||||
delete options.replaceResourceInternalToExternalLinks;
|
||||
const result = await markupToHtml.render(markupLanguage, md, theme, { codeTheme: theme.codeThemeCss, resources: resources, postMessageSyntax: 'ipcProxySendToHost', splitted: true, externalAssetsOnly: true, codeHighlightCacheKey: 'useMarkupToHtml', settingValue: deps.settingValue, whiteBackgroundNoteRendering, itemIdToUrl: (id, urlParameters = '') => {
|
||||
if (!(id in resources) || !resources[id]) {
|
||||
return null;
|
||||
}
|
||||
return (0, resourceUtils_1.resourceFullPath)(resources[id].item, resourceBaseUrl) + urlParameters;
|
||||
}, ...options });
|
||||
return result;
|
||||
}, [themeId, markupToHtml, whiteBackgroundNoteRendering, resourceBaseUrl, deps.settingValue]);
|
||||
}
|
||||
exports.default = useMarkupToHtml;
|
||||
// # sourceMappingURL=useMarkupToHtml.js.map
|
@@ -1,15 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import getActivePluginEditorView from '@joplin/lib/services/plugins/utils/getActivePluginEditorView';
|
||||
import getShownPluginEditorView from '@joplin/lib/services/plugins/utils/getShownPluginEditorView';
|
||||
|
||||
// If a plugin editor should be shown for the current note, this function will return the plugin and
|
||||
// associated view.
|
||||
export default (plugins: PluginStates, shownEditorViewIds: string[]) => {
|
||||
return useMemo(() => {
|
||||
const { editorPlugin, editorView } = getActivePluginEditorView(plugins);
|
||||
if (editorView) {
|
||||
if (!shownEditorViewIds.includes(editorView.id)) return { editorPlugin: null, editorView: null };
|
||||
}
|
||||
return { editorPlugin, editorView };
|
||||
return getShownPluginEditorView(plugins, shownEditorViewIds);
|
||||
}, [plugins, shownEditorViewIds]);
|
||||
};
|
||||
|
@@ -0,0 +1,21 @@
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface Props {
|
||||
noteId: string;
|
||||
windowId: string;
|
||||
}
|
||||
|
||||
const useResourceUnwatcher = ({ noteId, windowId }: Props) => {
|
||||
useEffect(() => {
|
||||
// All resources associated with the current window should no longer be watched after:
|
||||
// 1. The editor unloads, or
|
||||
// 2. The note shown in the editor changes.
|
||||
// Unwatching in a cleanup callback handles both cases.
|
||||
return () => {
|
||||
void ResourceEditWatcher.instance().stopWatchingAll(windowId);
|
||||
};
|
||||
}, [noteId, windowId]);
|
||||
};
|
||||
|
||||
export default useResourceUnwatcher;
|
@@ -1,7 +1,6 @@
|
||||
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { NoteBodyEditorRef, ScrollOptions, ScrollOptionTypes } from './types';
|
||||
import usePrevious from '@joplin/lib/hooks/usePrevious';
|
||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
|
||||
import type { EditorScrollPercents } from '../../../app.reducer';
|
||||
|
||||
interface Props {
|
||||
@@ -30,8 +29,6 @@ const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollP
|
||||
type: selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
|
||||
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
|
||||
});
|
||||
|
||||
void ResourceEditWatcher.instance().stopWatchingAll();
|
||||
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
|
||||
|
||||
const clearScrollWhenReady = useCallback(() => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { RefObject, useEffect } from 'react';
|
||||
import { NoteBodyEditorRef, OnChangeEvent, ScrollOptionTypes } from './types';
|
||||
import { RefObject, Dispatch, SetStateAction, useEffect } from 'react';
|
||||
import { WindowCommandDependencies, NoteBodyEditorRef, OnChangeEvent, ScrollOptionTypes } from './types';
|
||||
import editorCommandDeclarations, { enabledCondition } from '../editorCommandDeclarations';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime, CommandContext, RegisteredRuntime } from '@joplin/lib/services/CommandService';
|
||||
import time from '@joplin/lib/time';
|
||||
@@ -10,14 +10,14 @@ const commandsWithDependencies = [
|
||||
require('../commands/showLocalSearch'),
|
||||
require('../commands/focusElementNoteTitle'),
|
||||
require('../commands/focusElementNoteBody'),
|
||||
require('../commands/focusElementToolbar'),
|
||||
require('../commands/pasteAsText'),
|
||||
];
|
||||
|
||||
type OnBodyChange = (event: OnChangeEvent)=> void;
|
||||
|
||||
interface HookDependencies {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
setShowLocalSearch: Function;
|
||||
setShowLocalSearch: Dispatch<SetStateAction<boolean>>;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||
dispatch: Function;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
@@ -93,11 +93,12 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
|
||||
));
|
||||
}
|
||||
|
||||
const dependencies = {
|
||||
const dependencies: WindowCommandDependencies = {
|
||||
editorRef,
|
||||
setShowLocalSearch,
|
||||
noteSearchBarRef,
|
||||
titleInputRef,
|
||||
containerRef,
|
||||
};
|
||||
|
||||
for (const command of commandsWithDependencies) {
|
||||
|
@@ -29,6 +29,7 @@ import getNoteElementIdFromJoplinId from '../NoteListItem/utils/getNoteElementId
|
||||
import useFocusVisible from './utils/useFocusVisible';
|
||||
import { stateUtils } from '@joplin/lib/reducer';
|
||||
import { connect } from 'react-redux';
|
||||
import useOnNoteDoubleClick from './utils/useOnNoteDoubleClick';
|
||||
|
||||
const commands = {
|
||||
focusElementNoteList,
|
||||
@@ -103,6 +104,8 @@ const NoteList = (props: Props) => {
|
||||
|
||||
const onNoteClick = useOnNoteClick(props.dispatch, focusNote);
|
||||
|
||||
const onNoteDoubleClick = useOnNoteDoubleClick();
|
||||
|
||||
const onKeyDown = useOnKeyDown(
|
||||
activeNoteId,
|
||||
props.selectedNoteIds,
|
||||
@@ -115,6 +118,8 @@ const NoteList = (props: Props) => {
|
||||
props.notes.length,
|
||||
listRenderer.flow,
|
||||
itemsPerLine,
|
||||
props.showCompletedTodos,
|
||||
props.uncompletedTodosOnTop,
|
||||
);
|
||||
|
||||
useItemCss(listRenderer.itemCss);
|
||||
@@ -198,7 +203,9 @@ const NoteList = (props: Props) => {
|
||||
|
||||
const renderEmptyList = () => {
|
||||
if (props.notes.length) return null;
|
||||
return <div className="emptylist">{getEmptyFolderMessage(props.folders, props.selectedFolderId)}</div>;
|
||||
// Role status is necessary for the screenreader to announce that the list is empty, since when there are
|
||||
// zero items there is not list to render
|
||||
return <div className="emptylist" role="status">{getEmptyFolderMessage(props.folders, props.selectedFolderId)}</div>;
|
||||
};
|
||||
|
||||
const renderFiller = (key: string, style: React.CSSProperties) => {
|
||||
@@ -226,6 +233,7 @@ const NoteList = (props: Props) => {
|
||||
itemSize={itemSize}
|
||||
onChange={listRenderer.onChange}
|
||||
onClick={onNoteClick}
|
||||
onDoubleClick={onNoteDoubleClick}
|
||||
onContextMenu={onItemContextMenu}
|
||||
onDragStart={onDragStart}
|
||||
onDragOver={onDragOver}
|
||||
@@ -302,6 +310,7 @@ const NoteList = (props: Props) => {
|
||||
onKeyUp={onKeyUp}
|
||||
onDrop={onDrop}
|
||||
onContextMenu={onContainerContextMenu}
|
||||
id='notes-list'
|
||||
>
|
||||
{renderEmptyList()}
|
||||
{renderFiller('top', topFillerStyle)}
|
||||
|
@@ -23,6 +23,8 @@ const useOnKeyDown = (
|
||||
noteCount: number,
|
||||
flow: ItemFlow,
|
||||
itemsPerLine: number,
|
||||
showCompletedTodos: boolean,
|
||||
uncompletedTodosOnTop: boolean,
|
||||
) => {
|
||||
const scrollNoteIndex = useCallback((visibleItemCount: number, key: KeyboardEventKey, ctrlKey: boolean, metaKey: boolean, noteIndex: number) => {
|
||||
if (flow === ItemFlow.TopToBottom) {
|
||||
@@ -142,13 +144,32 @@ const useOnKeyDown = (
|
||||
const todos = selectedNotes.filter(n => !!n.is_todo);
|
||||
if (!todos.length) return;
|
||||
|
||||
const firstNoteIndex = notes.findIndex(n => n.id === todos[0].id);
|
||||
let nextSelectedNoteIndex = firstNoteIndex + 1;
|
||||
if (nextSelectedNoteIndex > notes.length - 1) nextSelectedNoteIndex = notes.length - 1;
|
||||
const nextSelectedNote = nextSelectedNoteIndex >= 0 ? notes[nextSelectedNoteIndex] : todos[0];
|
||||
|
||||
for (let i = 0; i < todos.length; i++) {
|
||||
const toggledTodo = Note.toggleTodoCompleted(todos[i]);
|
||||
await Note.save(toggledTodo);
|
||||
}
|
||||
|
||||
// When the settings `uncompletedTodosOnTop` or `showCompletedTodos` are enabled, the
|
||||
// note that got set as completed or uncompleted is going to disappear from view,
|
||||
// possibly hidden or moved to the top or bottom of the note list. It is assumed that
|
||||
// the user does not want to keep that note selected since the to-do is indeed
|
||||
// "completed". And by keeping that selection, the cursor would jump, making you lose
|
||||
// context if you have multiple to-dos that need to be ticked. For that reason we set
|
||||
// the selection to the next note in the list, which also ensures that the scroll
|
||||
// position doesn't change. This is the same behaviour as when deleting a note.
|
||||
const maintainScrollPosition = !showCompletedTodos || uncompletedTodosOnTop;
|
||||
|
||||
if (maintainScrollPosition) {
|
||||
dispatch({ type: 'NOTE_SELECT', noteId: nextSelectedNote.id });
|
||||
}
|
||||
|
||||
dispatch({ type: 'NOTE_SORT' });
|
||||
focusNote(todos[0].id);
|
||||
if (!maintainScrollPosition) focusNote(todos[0].id);
|
||||
const wasCompleted = !!todos[0].todo_completed;
|
||||
announceForAccessibility(!wasCompleted ? _('Complete') : _('Incomplete'));
|
||||
}
|
||||
@@ -171,7 +192,7 @@ const useOnKeyDown = (
|
||||
type: 'NOTE_SELECT_ALL',
|
||||
});
|
||||
}
|
||||
}, [moveNote, focusNote, visibleItemCount, scrollNoteIndex, makeItemIndexVisible, notes, selectedNoteIds, activeNoteId, dispatch, flow, itemsPerLine]);
|
||||
}, [moveNote, focusNote, visibleItemCount, scrollNoteIndex, makeItemIndexVisible, notes, selectedNoteIds, activeNoteId, dispatch, flow, itemsPerLine, showCompletedTodos, uncompletedTodosOnTop]);
|
||||
|
||||
|
||||
return onKeyDown;
|
||||
|
@@ -0,0 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
|
||||
export default () => {
|
||||
return useCallback((event: React.MouseEvent<HTMLDivElement>) => {
|
||||
const noteId = event.currentTarget.getAttribute('data-id');
|
||||
void CommandService.instance().execute('openNoteInNewWindow', noteId);
|
||||
}, []);
|
||||
};
|